mirror of
https://github.com/pocketbase/pocketbase.git
synced 2024-11-28 10:03:42 +02:00
backported some of the v0.23.0 form validators, fixes and tests
This commit is contained in:
parent
e92d3cccd0
commit
cbc88d7a01
@ -19,5 +19,6 @@ func NewRealtimeSubscribe() *RealtimeSubscribe {
|
|||||||
func (form *RealtimeSubscribe) Validate() error {
|
func (form *RealtimeSubscribe) Validate() error {
|
||||||
return validation.ValidateStruct(form,
|
return validation.ValidateStruct(form,
|
||||||
validation.Field(&form.ClientId, validation.Required, validation.Length(1, 255)),
|
validation.Field(&form.ClientId, validation.Required, validation.Length(1, 255)),
|
||||||
|
validation.Field(&form.Subscriptions, validation.Length(0, 1000)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,86 @@
|
|||||||
package forms_test
|
package forms_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||||
"github.com/pocketbase/pocketbase/forms"
|
"github.com/pocketbase/pocketbase/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRealtimeSubscribeValidate(t *testing.T) {
|
func TestRealtimeSubscribeValidate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
validSubscriptionsLimit := make([]string, 1000)
|
||||||
|
for i := 0; i < len(validSubscriptionsLimit); i++ {
|
||||||
|
validSubscriptionsLimit[i] = fmt.Sprintf(`"%d"`, i)
|
||||||
|
}
|
||||||
|
invalidSubscriptionsLimit := make([]string, 1001)
|
||||||
|
for i := 0; i < len(invalidSubscriptionsLimit); i++ {
|
||||||
|
invalidSubscriptionsLimit[i] = fmt.Sprintf(`"%d"`, i)
|
||||||
|
}
|
||||||
|
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
clientId string
|
name string
|
||||||
expectError bool
|
data string
|
||||||
|
expectedErrors []string
|
||||||
}{
|
}{
|
||||||
{"", true},
|
{
|
||||||
{strings.Repeat("a", 256), true},
|
"empty data",
|
||||||
{"test", false},
|
`{}`,
|
||||||
|
[]string{"clientId"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clientId > max chars limit",
|
||||||
|
`{"clientId":"` + strings.Repeat("a", 256) + `"}`,
|
||||||
|
[]string{"clientId"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clientId <= max chars limit",
|
||||||
|
`{"clientId":"` + strings.Repeat("a", 255) + `"}`,
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subscriptions > max limit",
|
||||||
|
`{"clientId":"test", "subscriptions":[` + strings.Join(invalidSubscriptionsLimit, ",") + `]}`,
|
||||||
|
[]string{"subscriptions"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subscriptions <= max limit",
|
||||||
|
`{"clientId":"test", "subscriptions":[` + strings.Join(validSubscriptionsLimit, ",") + `]}`,
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.name, func(t *testing.T) {
|
||||||
form := forms.NewRealtimeSubscribe()
|
form := forms.NewRealtimeSubscribe()
|
||||||
form.ClientId = s.clientId
|
|
||||||
|
|
||||||
err := form.Validate()
|
err := json.Unmarshal([]byte(s.data), &form)
|
||||||
|
if err != nil {
|
||||||
hasErr := err != nil
|
t.Fatal(err)
|
||||||
if hasErr != s.expectError {
|
|
||||||
t.Errorf("(%d) Expected hasErr to be %v, got %v (%v)", i, s.expectError, hasErr, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result := form.Validate()
|
||||||
|
|
||||||
|
// parse errors
|
||||||
|
errs, ok := result.(validation.Errors)
|
||||||
|
if !ok && result != nil {
|
||||||
|
t.Fatalf("Failed to parse errors %v", result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check errors
|
||||||
|
if len(errs) > len(s.expectedErrors) {
|
||||||
|
t.Fatalf("Expected error keys %v, got %v", s.expectedErrors, errs)
|
||||||
|
}
|
||||||
|
for _, k := range s.expectedErrors {
|
||||||
|
if _, ok := errs[k]; !ok {
|
||||||
|
t.Fatalf("Missing expected error key %q in %v", k, errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package validators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@ -159,6 +160,10 @@ func (validator *RecordDataValidator) checkNumberValue(field *schema.SchemaField
|
|||||||
return nil // nothing to check (skip zero-defaults)
|
return nil // nothing to check (skip zero-defaults)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if math.IsInf(val, 0) || math.IsNaN(val) {
|
||||||
|
return validation.NewError("validation_nan", "The submitted number is not properly formatted")
|
||||||
|
}
|
||||||
|
|
||||||
options, _ := field.Options.(*schema.NumberOptions)
|
options, _ := field.Options.(*schema.NumberOptions)
|
||||||
|
|
||||||
if options.NoDecimal && val != float64(int64(val)) {
|
if options.NoDecimal && val != float64(int64(val)) {
|
||||||
|
@ -248,6 +248,16 @@ func TestRecordDataValidatorValidateNumber(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
[]string{"field2"},
|
[]string{"field2"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"(number) check infinities and NaN",
|
||||||
|
map[string]any{
|
||||||
|
"field1": "Inf",
|
||||||
|
"field2": "-Inf",
|
||||||
|
"field4": "NaN",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
[]string{"field1", "field2", "field4"},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"(number) check min constraint",
|
"(number) check min constraint",
|
||||||
map[string]any{
|
map[string]any{
|
||||||
|
@ -6,10 +6,11 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/labstack/echo/v5"
|
"github.com/labstack/echo/v5"
|
||||||
"github.com/spf13/cast"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MultipartJsonKey is the key for the special multipart/form-data
|
// MultipartJsonKey is the key for the special multipart/form-data
|
||||||
@ -144,11 +145,15 @@ func bindFormData(c echo.Context, i any) error {
|
|||||||
return echo.BindBody(c, i)
|
return echo.BindBody(c, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var inferNumberCharsRegex = regexp.MustCompile(`^[\-\.\d]+$`)
|
||||||
|
|
||||||
// In order to support more seamlessly both json and multipart/form-data requests,
|
// In order to support more seamlessly both json and multipart/form-data requests,
|
||||||
// the following normalization rules are applied for plain multipart string values:
|
// the following normalization rules are applied for plain multipart string values:
|
||||||
// - "true" is converted to the json `true`
|
// - "true" is converted to the json "true"
|
||||||
// - "false" is converted to the json `false`
|
// - "false" is converted to the json "false"
|
||||||
// - numeric (non-scientific) strings are converted to json number
|
// - numeric strings are converted to json number ONLY if the resulted
|
||||||
|
// minimal number string representation is the same as the provided raw string
|
||||||
|
// (aka. scientific notations, "Infinity", "0.0", "0001", etc. are kept as string)
|
||||||
// - any other string (empty string too) is left as it is
|
// - any other string (empty string too) is left as it is
|
||||||
func normalizeMultipartValue(raw string) any {
|
func normalizeMultipartValue(raw string) any {
|
||||||
switch raw {
|
switch raw {
|
||||||
@ -159,8 +164,12 @@ func normalizeMultipartValue(raw string) any {
|
|||||||
case "false":
|
case "false":
|
||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
if raw[0] == '-' || (raw[0] >= '0' && raw[0] <= '9') {
|
// try to convert to number
|
||||||
if v, err := cast.ToFloat64E(raw); err == nil {
|
//
|
||||||
|
// note: expects the provided raw string to match exactly with the minimal string representation of the parsed float
|
||||||
|
if raw[0] == '-' || (raw[0] >= '0' && raw[0] <= '9') && inferNumberCharsRegex.Match([]byte(raw)) {
|
||||||
|
v, err := strconv.ParseFloat(raw, 64)
|
||||||
|
if err == nil && strconv.FormatFloat(v, 'f', -1, 64) == raw {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user