Skip to content

Commit

Permalink
backported some of the v0.23.0 form validators, fixes and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ganigeorgiev committed Oct 18, 2024
1 parent e92d3cc commit cbc88d7
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 20 deletions.
1 change: 1 addition & 0 deletions forms/realtime_subscribe.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ func NewRealtimeSubscribe() *RealtimeSubscribe {
func (form *RealtimeSubscribe) Validate() error {
return validation.ValidateStruct(form,
validation.Field(&form.ClientId, validation.Required, validation.Length(1, 255)),
validation.Field(&form.Subscriptions, validation.Length(0, 1000)),
)
}
79 changes: 66 additions & 13 deletions forms/realtime_subscribe_test.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,86 @@
package forms_test

import (
"encoding/json"
"fmt"
"strings"
"testing"

validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/pocketbase/pocketbase/forms"
)

func TestRealtimeSubscribeValidate(t *testing.T) {
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 {
clientId string
expectError bool
name string
data string
expectedErrors []string
}{
{"", true},
{strings.Repeat("a", 256), true},
{"test", false},
{
"empty data",
`{}`,
[]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 {
form := forms.NewRealtimeSubscribe()
form.ClientId = s.clientId
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
form := forms.NewRealtimeSubscribe()

err := json.Unmarshal([]byte(s.data), &form)
if err != nil {
t.Fatal(err)
}

result := form.Validate()

err := form.Validate()
// parse errors
errs, ok := result.(validation.Errors)
if !ok && result != nil {
t.Fatalf("Failed to parse errors %v", result)
return
}

hasErr := err != nil
if hasErr != s.expectError {
t.Errorf("(%d) Expected hasErr to be %v, got %v (%v)", i, s.expectError, hasErr, err)
}
// 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)
}
}
})
}
}
5 changes: 5 additions & 0 deletions forms/validators/record_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package validators

import (
"fmt"
"math"
"net/url"
"regexp"
"strings"
Expand Down Expand Up @@ -159,6 +160,10 @@ func (validator *RecordDataValidator) checkNumberValue(field *schema.SchemaField
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)

if options.NoDecimal && val != float64(int64(val)) {
Expand Down
10 changes: 10 additions & 0 deletions forms/validators/record_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,16 @@ func TestRecordDataValidatorValidateNumber(t *testing.T) {
nil,
[]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",
map[string]any{
Expand Down
23 changes: 16 additions & 7 deletions tools/rest/multi_binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import (
"io"
"net/http"
"reflect"
"regexp"
"strconv"
"strings"

"github.com/labstack/echo/v5"
"github.com/spf13/cast"
)

// MultipartJsonKey is the key for the special multipart/form-data
Expand Down Expand Up @@ -144,12 +145,16 @@ func bindFormData(c echo.Context, i any) error {
return echo.BindBody(c, i)
}

var inferNumberCharsRegex = regexp.MustCompile(`^[\-\.\d]+$`)

// In order to support more seamlessly both json and multipart/form-data requests,
// the following normalization rules are applied for plain multipart string values:
// - "true" is converted to the json `true`
// - "false" is converted to the json `false`
// - numeric (non-scientific) strings are converted to json number
// - any other string (empty string too) is left as it is
// - "true" is converted to the json "true"
// - "false" is converted to the json "false"
// - 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
func normalizeMultipartValue(raw string) any {
switch raw {
case "":
Expand All @@ -159,8 +164,12 @@ func normalizeMultipartValue(raw string) any {
case "false":
return false
default:
if raw[0] == '-' || (raw[0] >= '0' && raw[0] <= '9') {
if v, err := cast.ToFloat64E(raw); err == nil {
// try to convert to number
//
// 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
}
}
Expand Down

0 comments on commit cbc88d7

Please sign in to comment.