diff --git a/cmd/api/main.go b/cmd/api/main.go index 0b069c8..7cf2ae4 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -41,7 +41,7 @@ func main() { // User userService := usr.NewUserService(db, env) - user.NewUserHandler(s, "/user", userService) + user.NewUserHandler(s, "/user", userService, validate) // Restaurant restaurantService := restro.NewRestaurantService(db, env) diff --git a/pkg/database/models/user/user.go b/pkg/database/models/user/user.go index 4f634f4..7308e1b 100644 --- a/pkg/database/models/user/user.go +++ b/pkg/database/models/user/user.go @@ -1,16 +1,19 @@ package user import ( + "errors" + "github.com/go-playground/validator/v10" "github.com/uptrace/bun" "golang.org/x/crypto/bcrypt" "log" + "regexp" ) type User struct { bun.BaseModel `bun:"table:users"` ID int64 `bun:",pk,autoincrement" json:"id"` - Name string `bun:",notnull" json:"name"` - Email string `bun:",unique,notnull" json:"email"` + Name string `bun:",notnull" json:"name" validate:"name"` + Email string `bun:",unique,notnull" json:"email" validate:"email"` Password string `bun:",notnull" json:"password"` } @@ -30,3 +33,39 @@ func (u *User) HashPassword() { func (l *LoginUser) CheckPassword(hashPassword string) error { return bcrypt.CompareHashAndPassword([]byte(hashPassword), []byte(l.Password)) } + +func NameValidator(fl validator.FieldLevel) bool { + str, ok := fl.Field().Interface().(string) + return ok && str != "" +} + +func EmailValidator(fl validator.FieldLevel) bool { + email, ok := fl.Field().Interface().(string) + if !ok { + return false + } + // Basic email regex pattern + re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) + return re.MatchString(email) +} + +func UserValidationError(err error) map[string]string { + var validationErrors validator.ValidationErrors + if !errors.As(err, &validationErrors) { + return map[string]string{"error": "Unknown error"} + } + + errorsMap := make(map[string]string) + for _, e := range validationErrors { + field := e.Field() + switch e.Tag() { + case "name": + errorsMap[field] = "Provide your full name" + case "email": + errorsMap[field] = "Provide valid email address" + default: + errorsMap[field] = "Invalid" + } + } + return errorsMap +} diff --git a/pkg/handler/user/service.go b/pkg/handler/user/service.go index be1ba32..b41e25f 100644 --- a/pkg/handler/user/service.go +++ b/pkg/handler/user/service.go @@ -1,26 +1,36 @@ package user import ( + userValidate "Go_Food_Delivery/pkg/database/models/user" "Go_Food_Delivery/pkg/handler" "Go_Food_Delivery/pkg/service/user" "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" ) type UserHandler struct { - Serve *handler.Server - group string - router *gin.RouterGroup - service *user.UsrService + Serve *handler.Server + group string + router *gin.RouterGroup + service *user.UsrService + validate *validator.Validate } -func NewUserHandler(s *handler.Server, groupName string, service *user.UsrService) { +func NewUserHandler(s *handler.Server, groupName string, service *user.UsrService, validate *validator.Validate) { usrHandler := &UserHandler{ s, groupName, &gin.RouterGroup{}, service, + validate, } usrHandler.router = usrHandler.registerGroup() usrHandler.routes() + usrHandler.registerValidator() +} + +func (s *UserHandler) registerValidator() { + _ = s.validate.RegisterValidation("name", userValidate.NameValidator) + _ = s.validate.RegisterValidation("email", userValidate.EmailValidator) } diff --git a/pkg/handler/user/user.go b/pkg/handler/user/user.go index 56bb311..9efbe87 100644 --- a/pkg/handler/user/user.go +++ b/pkg/handler/user/user.go @@ -20,6 +20,12 @@ func (s *UserHandler) addUser(c *gin.Context) { return } + if err := s.validate.Struct(user); err != nil { + validationError := userModel.UserValidationError(err) + c.JSON(http.StatusBadRequest, gin.H{"error": validationError}) + return + } + _, err := s.service.Add(ctx, &user) if err != nil { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": err.Error()})