You've already forked golang-saas-starter-kit
mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-15 00:15:15 +02:00
improved unique validation function to support multiple fields.
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
// This file was generated by geeks-accelerator/swag at
|
||||
// 2019-08-07 19:21:01.42416 -0800 AKDT m=+430.010377170
|
||||
// 2019-08-07 20:20:04.308889 -0800 AKDT m=+439.591671736
|
||||
|
||||
package docs
|
||||
|
||||
|
@ -114,7 +114,7 @@ func (h *Signup) Step1(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
||||
|
||||
data["form"] = req
|
||||
|
||||
if verr, ok := weberror.NewValidationError(ctx, signup.Validator().Struct(signup.SignupRequest{})); ok {
|
||||
if verr, ok := weberror.NewValidationError(ctx, webcontext.Validator().Struct(signup.SignupRequest{})); ok {
|
||||
data["validationDefaults"] = verr.(*weberror.Error)
|
||||
}
|
||||
|
||||
|
4
go.mod
4
go.mod
@ -9,9 +9,9 @@ require (
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/fatih/camelcase v1.0.0
|
||||
github.com/fatih/structtag v1.0.0
|
||||
github.com/geeks-accelerator/files v0.0.0-20190704085106-630677cd5c14 // indirect
|
||||
github.com/geeks-accelerator/files v0.0.0-20190704085106-630677cd5c14
|
||||
github.com/geeks-accelerator/sqlxmigrate v0.0.0-20190527223850-4a863a2d30db
|
||||
github.com/geeks-accelerator/swag v1.6.3 // indirect
|
||||
github.com/geeks-accelerator/swag v1.6.3
|
||||
github.com/go-openapi/spec v0.19.2 // indirect
|
||||
github.com/go-openapi/swag v0.19.4 // indirect
|
||||
github.com/go-playground/locales v0.12.1
|
||||
|
@ -8,12 +8,12 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
||||
"github.com/pkg/errors"
|
||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||
"github.com/geeks-accelerator/files"
|
||||
"github.com/geeks-accelerator/swag"
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -91,23 +91,6 @@ func init() {
|
||||
zh_translations.RegisterDefaultTranslations(validate, transZh)
|
||||
|
||||
/*
|
||||
validate.RegisterTranslation("required", transEn, func(ut ut.Translator) error {
|
||||
return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("required", fe.Field())
|
||||
|
||||
return t
|
||||
})
|
||||
|
||||
validate.RegisterTranslation("required", transFr, func(ut ut.Translator) error {
|
||||
return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("required", fe.Field())
|
||||
|
||||
return t
|
||||
})
|
||||
|
||||
|
||||
validate.RegisterTranslation("unique", transEn, func(ut ut.Translator) error {
|
||||
return ut.Add("unique", "{0} must be unique", true) // see universal-translator for details
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
@ -122,14 +105,20 @@ func init() {
|
||||
t, _ := ut.T("unique", fe.Field())
|
||||
|
||||
return t
|
||||
})*/
|
||||
|
||||
})
|
||||
*/
|
||||
}
|
||||
|
||||
// ctxKeyTagUnique represents the type of unique value for the context key used by the validation function.
|
||||
type ctxKeyTagUnique int
|
||||
|
||||
// KeyTagUnique defines the value used in the context key for storing the uniqueness of a field.
|
||||
const KeyTagUnique ctxKeyTagUnique = 1
|
||||
|
||||
// KeyTagFieldValue defined the struct+field name used as the context key for storing whether the field is unique
|
||||
// or not that is used by the custom validation function registered in newValidator.
|
||||
type KeyTagFieldValue string
|
||||
|
||||
// newValidator inits a new validator with custom settings.
|
||||
func newValidator() *validator.Validate {
|
||||
var v = validator.New()
|
||||
@ -145,21 +134,25 @@ func newValidator() *validator.Validate {
|
||||
return "{{" + name + "}}"
|
||||
})
|
||||
|
||||
// Empty method that can be overwritten in business logic packages to prevent web.Decode from failing.
|
||||
f := func(fl validator.FieldLevel) bool {
|
||||
return false
|
||||
}
|
||||
v.RegisterValidation("unique", f)
|
||||
|
||||
// Custom Validation function for the unique tag that checks the context to determine if a field is
|
||||
// unique or not. First it will check a field specific context key and if that doesn't work, it will
|
||||
// check a shared context key.
|
||||
fctx := func(ctx context.Context, fl validator.FieldLevel) bool {
|
||||
if fl.Field().String() == "invalid" {
|
||||
return false
|
||||
}
|
||||
|
||||
// First check to see if a value is set for the specific field.
|
||||
fk := KeyTagFieldValue(fl.Parent().Type().String() + "." + fl.StructFieldName())
|
||||
cv := ctx.Value(fk)
|
||||
|
||||
// Second check if the default unique key is set in context.
|
||||
if cv == nil {
|
||||
cv := ctx.Value(KeyTagUnique)
|
||||
if cv == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := cv.(bool); ok {
|
||||
return v
|
||||
@ -172,6 +165,14 @@ func newValidator() *validator.Validate {
|
||||
return v
|
||||
}
|
||||
|
||||
// ContextAddUniqueValue allows multiple fields to be validated using the same unique validation function by added
|
||||
// the unique value to the context using the struct name and field name.
|
||||
func ContextAddUniqueValue(ctx context.Context, req interface{}, fieldName string, unique bool) context.Context {
|
||||
fk := KeyTagFieldValue(reflect.TypeOf(req).String() + "." + fieldName)
|
||||
|
||||
return context.WithValue(ctx, fk, unique)
|
||||
}
|
||||
|
||||
// Validator returns the current init validator.
|
||||
func Validator() *validator.Validate {
|
||||
return validate
|
||||
|
@ -14,7 +14,7 @@ type SignupRequest struct {
|
||||
|
||||
// SignupAccount defined the details needed for account.
|
||||
type SignupAccount struct {
|
||||
Name string `json:"name" validate:"required,unique-name" example:"Company {RANDOM_UUID}"`
|
||||
Name string `json:"name" validate:"required,unique" example:"Company {RANDOM_UUID}"`
|
||||
Address1 string `json:"address1" validate:"required" example:"221 Tatitlek Ave"`
|
||||
Address2 string `json:"address2" validate:"omitempty" example:"Box #1832"`
|
||||
City string `json:"city" validate:"required" example:"Valdez"`
|
||||
@ -28,7 +28,7 @@ type SignupAccount struct {
|
||||
type SignupUser struct {
|
||||
FirstName string `json:"first_name" validate:"required" example:"Gabi"`
|
||||
LastName string `json:"last_name" validate:"required" example:"May"`
|
||||
Email string `json:"email" validate:"required,email,unique-email" example:"{RANDOM_EMAIL}"`
|
||||
Email string `json:"email" validate:"required,email,unique" example:"{RANDOM_EMAIL}"`
|
||||
Password string `json:"password" validate:"required" example:"SecretString"`
|
||||
PasswordConfirm string `json:"password_confirm" validate:"required,eqfield=Password" example:"SecretString"`
|
||||
}
|
||||
|
@ -11,62 +11,8 @@ import (
|
||||
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
)
|
||||
|
||||
type ctxKeyTagUniqueName int
|
||||
|
||||
const KeyTagUniqueName ctxKeyTagUniqueName = 1
|
||||
|
||||
type ctxKeyTagUniqueEmail int
|
||||
|
||||
const KeyTagUniqueEmail ctxKeyTagUniqueEmail = 1
|
||||
|
||||
// validate holds the settings and caches for validating request struct values.
|
||||
var validate *validator.Validate
|
||||
|
||||
// Validator returns the current init validator.
|
||||
func Validator() *validator.Validate {
|
||||
if validate == nil {
|
||||
validate = webcontext.Validator()
|
||||
|
||||
validate.RegisterValidationCtx("unique-name", func(ctx context.Context, fl validator.FieldLevel) bool {
|
||||
if fl.Field().String() == "invalid" {
|
||||
return false
|
||||
}
|
||||
|
||||
cv := ctx.Value(KeyTagUniqueName)
|
||||
if cv == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if v, ok := cv.(bool); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
validate.RegisterValidationCtx("unique-email", func(ctx context.Context, fl validator.FieldLevel) bool {
|
||||
if fl.Field().String() == "invalid" {
|
||||
return false
|
||||
}
|
||||
|
||||
cv := ctx.Value(KeyTagUniqueEmail)
|
||||
if cv == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if v, ok := cv.(bool); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
return validate
|
||||
}
|
||||
|
||||
// Signup performs the steps needed to create a new account, new user and then associate
|
||||
// both records with a new user_account entry.
|
||||
func Signup(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req SignupRequest, now time.Time) (*SignupResult, error) {
|
||||
@ -78,17 +24,17 @@ func Signup(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req Signup
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = context.WithValue(ctx, KeyTagUniqueEmail, uniqEmail)
|
||||
ctx = webcontext.ContextAddUniqueValue(ctx, req.User, "Email", uniqEmail)
|
||||
|
||||
// Validate the account name is unique in the database.
|
||||
uniqName, err := account.UniqueName(ctx, dbConn, req.Account.Name, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = context.WithValue(ctx, KeyTagUniqueName, uniqName)
|
||||
ctx = webcontext.ContextAddUniqueValue(ctx, req.Account, "Name", uniqName)
|
||||
|
||||
// Validate the request.
|
||||
err = Validator().StructCtx(ctx, req)
|
||||
err = webcontext.Validator().StructCtx(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user