mirror of
https://github.com/eduardolat/pgbackweb.git
synced 2026-01-14 00:00:12 -06:00
Add email and struct validation
This commit is contained in:
5
internal/validate/README.md
Normal file
5
internal/validate/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Validate functions
|
||||
|
||||
All the functions in this directory are used to validate data, all of them _MUST BE PURE FUNCTIONS_.
|
||||
|
||||
https://en.wikipedia.org/wiki/Pure_function
|
||||
17
internal/validate/email.go
Normal file
17
internal/validate/email.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package validate
|
||||
|
||||
import "regexp"
|
||||
|
||||
// Email validates an email address.
|
||||
// It returns a boolean indicating whether
|
||||
// the email is valid or not.
|
||||
func Email(email string) bool {
|
||||
// Regular expression to match email format
|
||||
regex := `^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`
|
||||
|
||||
// Compile the regular expression
|
||||
re := regexp.MustCompile(regex)
|
||||
|
||||
// Match the email against the regular expression
|
||||
return re.MatchString(email)
|
||||
}
|
||||
25
internal/validate/email_test.go
Normal file
25
internal/validate/email_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package validate
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestEmail(t *testing.T) {
|
||||
tests := []struct {
|
||||
email string
|
||||
valid bool
|
||||
}{
|
||||
{"", false},
|
||||
{"test", false},
|
||||
{"test@", false},
|
||||
{"@example.com", false},
|
||||
{"test@example", false},
|
||||
{"test@example.com", true},
|
||||
{"test@example.com.gt", true},
|
||||
}
|
||||
|
||||
for _, testItem := range tests {
|
||||
isValid := Email(testItem.email)
|
||||
if isValid != testItem.valid {
|
||||
t.Errorf("Email(%s) expected %v, got %v", testItem.email, testItem.valid, isValid)
|
||||
}
|
||||
}
|
||||
}
|
||||
98
internal/validate/struct.go
Normal file
98
internal/validate/struct.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
// StructError is the error returned by Struct.
|
||||
type StructError struct {
|
||||
errs []error
|
||||
}
|
||||
|
||||
// SetErrs sets the errors.
|
||||
func (e *StructError) SetErrs(errs []error) {
|
||||
e.errs = errs
|
||||
}
|
||||
|
||||
// AddErr adds an error.
|
||||
func (e *StructError) AddErr(err error) {
|
||||
e.errs = append(e.errs, err)
|
||||
}
|
||||
|
||||
// HasErrs returns true if there are errors.
|
||||
func (e *StructError) HasErrs() bool {
|
||||
return len(e.errs) > 0
|
||||
}
|
||||
|
||||
// Error returns all the errors as a string separated by commas.
|
||||
func (e *StructError) Error() string {
|
||||
errStr := ""
|
||||
for idx, err := range e.errs {
|
||||
if idx > 0 {
|
||||
errStr += ", "
|
||||
}
|
||||
errStr += err.Error()
|
||||
}
|
||||
|
||||
return errStr
|
||||
}
|
||||
|
||||
// Errors returns all the errors as a slice of strings.
|
||||
func (e *StructError) Errors() []string {
|
||||
errStrs := make([]string, len(e.errs))
|
||||
for idx, err := range e.errs {
|
||||
errStrs[idx] = err.Error()
|
||||
}
|
||||
|
||||
return errStrs
|
||||
}
|
||||
|
||||
// ErrorsRaw returns all the errors as a slice of errors.
|
||||
func (e *StructError) ErrorsRaw() []error {
|
||||
return e.errs
|
||||
}
|
||||
|
||||
// Struct validates the given struct using go-playground/validator.
|
||||
func Struct[T any](sPointer *T) *StructError {
|
||||
err := validator.New().Struct(sPointer)
|
||||
|
||||
if err != nil {
|
||||
errs := StructError{}
|
||||
|
||||
if _, ok := err.(*validator.InvalidValidationError); ok {
|
||||
errs.AddErr(fmt.Errorf("validation error (check if it's a struct): %s", err))
|
||||
}
|
||||
|
||||
if validationErrors, ok := err.(validator.ValidationErrors); ok {
|
||||
for _, validationError := range validationErrors {
|
||||
errs.AddErr(fmt.Errorf(
|
||||
"error in field %s: %s",
|
||||
validationError.StructField(),
|
||||
validationError.Tag(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return &errs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StructSlice validates the given slice of structs using go-playground/validator.
|
||||
func StructSlice[T any](sPointerSlice *[]T) *StructError {
|
||||
for i, sPointer := range *sPointerSlice {
|
||||
num := i + 1
|
||||
if err := Struct(&sPointer); err != nil {
|
||||
se := &StructError{}
|
||||
errs := []error{fmt.Errorf("error in row %d", num)}
|
||||
errs = append(errs, err.ErrorsRaw()...)
|
||||
se.SetErrs(errs)
|
||||
return se
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
130
internal/validate/struct_test.go
Normal file
130
internal/validate/struct_test.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestStruct struct {
|
||||
Field string `validate:"required"`
|
||||
}
|
||||
|
||||
func TestValidateStruct(t *testing.T) {
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
s := TestStruct{
|
||||
Field: "value",
|
||||
}
|
||||
err := Struct(&s)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "value", s.Field)
|
||||
})
|
||||
|
||||
t.Run("Fail", func(t *testing.T) {
|
||||
s := TestStruct{
|
||||
Field: "",
|
||||
}
|
||||
err := Struct(&s)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.IsType(t, &StructError{}, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStructSlice(t *testing.T) {
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
s := []TestStruct{
|
||||
{Field: "value1"},
|
||||
{Field: "value2"},
|
||||
}
|
||||
err := StructSlice(&s)
|
||||
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Fail on row 1", func(t *testing.T) {
|
||||
s := []TestStruct{
|
||||
{Field: ""},
|
||||
{Field: "value2"},
|
||||
}
|
||||
err := StructSlice(&s)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.IsType(t, &StructError{}, err)
|
||||
assert.Contains(t, err.Error(), "error in row 1")
|
||||
})
|
||||
|
||||
t.Run("Fail on row 2", func(t *testing.T) {
|
||||
s := []TestStruct{
|
||||
{Field: "value1"},
|
||||
{Field: ""},
|
||||
}
|
||||
err := StructSlice(&s)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.IsType(t, &StructError{}, err)
|
||||
assert.Contains(t, err.Error(), "error in row 2")
|
||||
})
|
||||
}
|
||||
|
||||
func TestStructError(t *testing.T) {
|
||||
t.Run("Error method", func(t *testing.T) {
|
||||
err := &StructError{
|
||||
errs: []error{errors.New("error1"), errors.New("error2")},
|
||||
}
|
||||
assert.Equal(t, "error1, error2", err.Error())
|
||||
})
|
||||
|
||||
t.Run("Errors method", func(t *testing.T) {
|
||||
err := &StructError{
|
||||
errs: []error{errors.New("error1"), errors.New("error2")},
|
||||
}
|
||||
assert.Equal(t, []string{"error1", "error2"}, err.Errors())
|
||||
})
|
||||
|
||||
t.Run("ErrorsRaw method", func(t *testing.T) {
|
||||
err := &StructError{
|
||||
errs: []error{errors.New("error1"), errors.New("error2")},
|
||||
}
|
||||
assert.Equal(
|
||||
t,
|
||||
[]error{errors.New("error1"), errors.New("error2")},
|
||||
err.ErrorsRaw(),
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("AddErr method", func(t *testing.T) {
|
||||
err := &StructError{}
|
||||
err.AddErr(errors.New("error1"))
|
||||
err.AddErr(errors.New("error2"))
|
||||
assert.Equal(t, []string{"error1", "error2"}, err.Errors())
|
||||
})
|
||||
|
||||
t.Run("SetErrs method", func(t *testing.T) {
|
||||
err := &StructError{}
|
||||
err.SetErrs([]error{errors.New("error1"), errors.New("error2")})
|
||||
assert.Equal(t, []string{"error1", "error2"}, err.Errors())
|
||||
})
|
||||
|
||||
t.Run("SetErrs method (overwrite)", func(t *testing.T) {
|
||||
err := &StructError{
|
||||
errs: []error{errors.New("error0")},
|
||||
}
|
||||
err.SetErrs([]error{errors.New("error1"), errors.New("error2")})
|
||||
assert.Equal(t, []string{"error1", "error2"}, err.Errors())
|
||||
})
|
||||
|
||||
t.Run("HasErrs method (with errors)", func(t *testing.T) {
|
||||
err := &StructError{
|
||||
errs: []error{errors.New("error1"), errors.New("error2")},
|
||||
}
|
||||
assert.True(t, err.HasErrs())
|
||||
})
|
||||
|
||||
t.Run("HasErrs method (without errors)", func(t *testing.T) {
|
||||
err := &StructError{}
|
||||
assert.False(t, err.HasErrs())
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user