Refactor form validation into a new type

I was worried that i was repeating the AddInputErrors function for each
form, because they were basically the same.  I could create a Form type
and make all forms embed it, but i realized that with a separate
validator i would have cleaner validation functions and would not need
the Valid field in the form that i am using only for that method.
This commit is contained in:
jordi fita mas 2023-02-01 11:30:30 +01:00
parent ff5b76b4f5
commit b8b3d73e95
5 changed files with 86 additions and 83 deletions

View File

@ -2,6 +2,7 @@ package pkg
import ( import (
"context" "context"
"errors"
"net/http" "net/http"
"net/mail" "net/mail"
"strings" "strings"
@ -25,19 +26,6 @@ func (field *InputField) FillValue(r *http.Request) {
field.Value = strings.TrimSpace(r.FormValue(field.Name)) field.Value = strings.TrimSpace(r.FormValue(field.Name))
} }
func (field *InputField) Equals(other *InputField) bool {
return field.Value == other.Value
}
func (field *InputField) IsEmpty() bool {
return field.Value == ""
}
func (field *InputField) HasValidEmail() bool {
_, err := mail.ParseAddress(field.Value)
return err == nil
}
type SelectOption struct { type SelectOption struct {
Value string Value string
Label string Label string
@ -86,3 +74,48 @@ func MustGetOptions(ctx context.Context, conn *Conn, sql string, args ...interfa
return options return options
} }
type FormValidator struct {
Valid bool
}
func newFormValidator() *FormValidator {
return &FormValidator{true}
}
func (v *FormValidator) AllOK() bool {
return v.Valid
}
func (v *FormValidator) CheckRequiredInput(field *InputField, message string) bool {
return v.checkInput(field, field.Value != "", message)
}
func (v *FormValidator) CheckValidEmailInput(field *InputField, message string) bool {
_, err := mail.ParseAddress(field.Value)
return v.checkInput(field, err == nil, message)
}
func (v *FormValidator) CheckPasswordConfirmation(password *InputField, confirm *InputField, message string) bool {
return v.checkInput(confirm, password.Value == confirm.Value, message)
}
func (v *FormValidator) CheckValidSelectOption(field *SelectField, message string) bool {
return v.checkSelect(field, field.HasValidOption(), message)
}
func (v *FormValidator) checkInput(field *InputField, ok bool, message string) bool {
if !ok {
field.Errors = append(field.Errors, errors.New(message))
v.Valid = false
}
return ok
}
func (v *FormValidator) checkSelect(field *SelectField, ok bool, message string) bool {
if !ok {
field.Errors = append(field.Errors, errors.New(message))
v.Valid = false
}
return ok
}

View File

@ -23,7 +23,6 @@ type loginForm struct {
Errors []error Errors []error
Email *InputField Email *InputField
Password *InputField Password *InputField
Valid bool
} }
func newLoginForm(locale *Locale) *loginForm { func newLoginForm(locale *Locale) *loginForm {
@ -63,21 +62,12 @@ func (form *loginForm) Parse(r *http.Request) error {
} }
func (form *loginForm) Validate() bool { func (form *loginForm) Validate() bool {
form.Valid = true validator := newFormValidator()
if form.Email.IsEmpty() { if validator.CheckRequiredInput(form.Email, gettext("Email can not be empty.", form.locale)) {
form.AppendInputError(form.Email, errors.New(gettext("Email can not be empty.", form.locale))) validator.CheckValidEmailInput(form.Email, gettext("This value is not a valid email. It should be like name@domain.com.", form.locale))
} else if !form.Email.HasValidEmail() {
form.AppendInputError(form.Email, errors.New(gettext("This value is not a valid email. It should be like name@domain.com.", form.locale)))
} }
if form.Password.IsEmpty() { validator.CheckRequiredInput(form.Password, gettext("Password can not be empty.", form.locale))
form.AppendInputError(form.Password, errors.New(gettext("Password can not be empty.", form.locale))) return validator.AllOK()
}
return form.Valid
}
func (form *loginForm) AppendInputError(field *InputField, err error) {
field.Errors = append(field.Errors, err)
form.Valid = false
} }
func LoginHandler() http.Handler { func LoginHandler() http.Handler {

View File

@ -2,7 +2,6 @@ package pkg
import ( import (
"context" "context"
"errors"
"net/http" "net/http"
) )
@ -18,7 +17,6 @@ type profileForm struct {
Password *InputField Password *InputField
PasswordConfirm *InputField PasswordConfirm *InputField
Language *SelectField Language *SelectField
Valid bool
} }
func newProfileForm(ctx context.Context, conn *Conn, locale *Locale) *profileForm { func newProfileForm(ctx context.Context, conn *Conn, locale *Locale) *profileForm {
@ -69,32 +67,14 @@ func (form *profileForm) Parse(r *http.Request) error {
} }
func (form *profileForm) Validate() bool { func (form *profileForm) Validate() bool {
form.Valid = true validator := newFormValidator()
if form.Email.IsEmpty() { if validator.CheckRequiredInput(form.Email, gettext("Email can not be empty.", form.locale)) {
form.AppendInputError(form.Email, errors.New(gettext("Email can not be empty.", form.locale))) validator.CheckValidEmailInput(form.Email, gettext("This value is not a valid email. It should be like name@domain.com.", form.locale))
} else if !form.Email.HasValidEmail() {
form.AppendInputError(form.Email, errors.New(gettext("This value is not a valid email. It should be like name@domain.com.", form.locale)))
} }
if form.Name.IsEmpty() { validator.CheckRequiredInput(form.Name, gettext("Name can not be empty.", form.locale))
form.AppendInputError(form.Name, errors.New(gettext("Name can not be empty.", form.locale))) validator.CheckPasswordConfirmation(form.Password, form.PasswordConfirm, gettext("Confirmation does not match password.", form.locale))
} validator.CheckValidSelectOption(form.Language, gettext("Selected language is not valid.", form.locale))
if !form.PasswordConfirm.Equals(form.Password) { return validator.AllOK()
form.AppendInputError(form.PasswordConfirm, errors.New(gettext("Confirmation does not match password.", form.locale)))
}
if !form.Language.HasValidOption() {
form.AppendSelectError(form.Language, errors.New(gettext("Selected language is not valid.", form.locale)))
}
return form.Valid
}
func (form *profileForm) AppendInputError(field *InputField, err error) {
field.Errors = append(field.Errors, err)
form.Valid = false
}
func (form *profileForm) AppendSelectError(field *SelectField, err error) {
field.Errors = append(field.Errors, err)
form.Valid = false
} }
func ProfileHandler() http.Handler { func ProfileHandler() http.Handler {

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-02-01 10:52+0100\n" "POT-Creation-Date: 2023-02-01 11:26+0100\n"
"PO-Revision-Date: 2023-01-18 17:08+0100\n" "PO-Revision-Date: 2023-01-18 17:08+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n" "Language-Team: Catalan <ca@dodds.net>\n"
@ -142,7 +142,7 @@ msgid "Phone"
msgstr "Telèfon" msgstr "Telèfon"
#: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27 #: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27
#: pkg/login.go:34 pkg/profile.go:37 #: pkg/login.go:33 pkg/profile.go:35
msgctxt "input" msgctxt "input"
msgid "Email" msgid "Email"
msgstr "Correu-e" msgstr "Correu-e"
@ -221,56 +221,56 @@ msgctxt "title"
msgid "New Contact" msgid "New Contact"
msgstr "Nou contacte" msgstr "Nou contacte"
#: pkg/login.go:45 pkg/profile.go:43 #: pkg/login.go:44 pkg/profile.go:41
msgctxt "input" msgctxt "input"
msgid "Password" msgid "Password"
msgstr "Contrasenya" msgstr "Contrasenya"
#: pkg/login.go:68 pkg/profile.go:74 #: pkg/login.go:66 pkg/profile.go:71
msgid "Email can not be empty." msgid "Email can not be empty."
msgstr "No podeu deixar el correu-e en blanc." msgstr "No podeu deixar el correu-e en blanc."
#: pkg/login.go:70 pkg/profile.go:76 #: pkg/login.go:67 pkg/profile.go:72
msgid "This value is not a valid email. It should be like name@domain.com." msgid "This value is not a valid email. It should be like name@domain.com."
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat." msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
#: pkg/login.go:73 #: pkg/login.go:69
msgid "Password can not be empty." msgid "Password can not be empty."
msgstr "No podeu deixar la contrasenya en blanc." msgstr "No podeu deixar la contrasenya en blanc."
#: pkg/login.go:105 #: pkg/login.go:95
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nom dusuari o contrasenya incorrectes." msgstr "Nom dusuari o contrasenya incorrectes."
#: pkg/profile.go:25 #: pkg/profile.go:23
msgctxt "language option" msgctxt "language option"
msgid "Automatic" msgid "Automatic"
msgstr "Automàtic" msgstr "Automàtic"
#: pkg/profile.go:31 #: pkg/profile.go:29
msgctxt "input" msgctxt "input"
msgid "User name" msgid "User name"
msgstr "Nom dusuari" msgstr "Nom dusuari"
#: pkg/profile.go:48 #: pkg/profile.go:46
msgctxt "input" msgctxt "input"
msgid "Password Confirmation" msgid "Password Confirmation"
msgstr "Confirmació contrasenya" msgstr "Confirmació contrasenya"
#: pkg/profile.go:53 #: pkg/profile.go:51
msgctxt "input" msgctxt "input"
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: pkg/profile.go:79 #: pkg/profile.go:74
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podeu deixar el nom en blanc." msgstr "No podeu deixar el nom en blanc."
#: pkg/profile.go:82 #: pkg/profile.go:75
msgid "Confirmation does not match password." msgid "Confirmation does not match password."
msgstr "La confirmació no és igual a la contrasenya." msgstr "La confirmació no és igual a la contrasenya."
#: pkg/profile.go:85 #: pkg/profile.go:76
msgid "Selected language is not valid." msgid "Selected language is not valid."
msgstr "Heu seleccionat un idioma que no és vàlid." msgstr "Heu seleccionat un idioma que no és vàlid."

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-02-01 10:52+0100\n" "POT-Creation-Date: 2023-02-01 11:26+0100\n"
"PO-Revision-Date: 2023-01-18 17:45+0100\n" "PO-Revision-Date: 2023-01-18 17:45+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n" "Language-Team: Spanish <es@tp.org.es>\n"
@ -142,7 +142,7 @@ msgid "Phone"
msgstr "Teléfono" msgstr "Teléfono"
#: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27 #: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27
#: pkg/login.go:34 pkg/profile.go:37 #: pkg/login.go:33 pkg/profile.go:35
msgctxt "input" msgctxt "input"
msgid "Email" msgid "Email"
msgstr "Correo-e" msgstr "Correo-e"
@ -221,56 +221,56 @@ msgctxt "title"
msgid "New Contact" msgid "New Contact"
msgstr "Nuevo contacto" msgstr "Nuevo contacto"
#: pkg/login.go:45 pkg/profile.go:43 #: pkg/login.go:44 pkg/profile.go:41
msgctxt "input" msgctxt "input"
msgid "Password" msgid "Password"
msgstr "Contraseña" msgstr "Contraseña"
#: pkg/login.go:68 pkg/profile.go:74 #: pkg/login.go:66 pkg/profile.go:71
msgid "Email can not be empty." msgid "Email can not be empty."
msgstr "No podéis dejar el correo-e en blanco." msgstr "No podéis dejar el correo-e en blanco."
#: pkg/login.go:70 pkg/profile.go:76 #: pkg/login.go:67 pkg/profile.go:72
msgid "This value is not a valid email. It should be like name@domain.com." msgid "This value is not a valid email. It should be like name@domain.com."
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es." msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
#: pkg/login.go:73 #: pkg/login.go:69
msgid "Password can not be empty." msgid "Password can not be empty."
msgstr "No podéis dejar la contaseña en blanco." msgstr "No podéis dejar la contaseña en blanco."
#: pkg/login.go:105 #: pkg/login.go:95
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nombre de usuario o contraseña inválido." msgstr "Nombre de usuario o contraseña inválido."
#: pkg/profile.go:25 #: pkg/profile.go:23
msgctxt "language option" msgctxt "language option"
msgid "Automatic" msgid "Automatic"
msgstr "Automático" msgstr "Automático"
#: pkg/profile.go:31 #: pkg/profile.go:29
msgctxt "input" msgctxt "input"
msgid "User name" msgid "User name"
msgstr "Nombre de usuario" msgstr "Nombre de usuario"
#: pkg/profile.go:48 #: pkg/profile.go:46
msgctxt "input" msgctxt "input"
msgid "Password Confirmation" msgid "Password Confirmation"
msgstr "Confirmación contrasenya" msgstr "Confirmación contrasenya"
#: pkg/profile.go:53 #: pkg/profile.go:51
msgctxt "input" msgctxt "input"
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: pkg/profile.go:79 #: pkg/profile.go:74
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podéis dejar el nombre en blanco." msgstr "No podéis dejar el nombre en blanco."
#: pkg/profile.go:82 #: pkg/profile.go:75
msgid "Confirmation does not match password." msgid "Confirmation does not match password."
msgstr "La confirmación no corresponde con la contraseña." msgstr "La confirmación no corresponde con la contraseña."
#: pkg/profile.go:85 #: pkg/profile.go:76
msgid "Selected language is not valid." msgid "Selected language is not valid."
msgstr "Habéis escogido un idioma que no es válido." msgstr "Habéis escogido un idioma que no es válido."