feat: Use gin default validator
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Build and test / Build (push) Successful in 2m9s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Build and test / Build (push) Successful in 2m9s
				
			This commit is contained in:
		
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							@ -3,7 +3,6 @@ module git.vinchent.xyz/vinchent/howmuch
 | 
				
			|||||||
go 1.23.1
 | 
					go 1.23.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
 | 
					 | 
				
			||||||
	github.com/fsnotify/fsnotify v1.7.0
 | 
						github.com/fsnotify/fsnotify v1.7.0
 | 
				
			||||||
	github.com/gin-contrib/cors v1.7.2
 | 
						github.com/gin-contrib/cors v1.7.2
 | 
				
			||||||
	github.com/gin-gonic/gin v1.10.0
 | 
						github.com/gin-gonic/gin v1.10.0
 | 
				
			||||||
@ -31,7 +30,7 @@ require (
 | 
				
			|||||||
	github.com/gin-contrib/sse v0.1.0 // indirect
 | 
						github.com/gin-contrib/sse v0.1.0 // indirect
 | 
				
			||||||
	github.com/go-playground/locales v0.14.1 // indirect
 | 
						github.com/go-playground/locales v0.14.1 // indirect
 | 
				
			||||||
	github.com/go-playground/universal-translator v0.18.1 // indirect
 | 
						github.com/go-playground/universal-translator v0.18.1 // indirect
 | 
				
			||||||
	github.com/go-playground/validator/v10 v10.20.0 // indirect
 | 
						github.com/go-playground/validator/v10 v10.22.1 // indirect
 | 
				
			||||||
	github.com/goccy/go-json v0.10.2 // indirect
 | 
						github.com/goccy/go-json v0.10.2 // indirect
 | 
				
			||||||
	github.com/google/go-cmp v0.6.0 // indirect
 | 
						github.com/google/go-cmp v0.6.0 // indirect
 | 
				
			||||||
	github.com/hashicorp/hcl v1.0.0 // indirect
 | 
						github.com/hashicorp/hcl v1.0.0 // indirect
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								go.sum
									
									
									
									
									
								
							@ -1,5 +1,3 @@
 | 
				
			|||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
 | 
					 | 
				
			||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 | 
					 | 
				
			||||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
 | 
					github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
 | 
				
			||||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
 | 
					github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
 | 
				
			||||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
 | 
					github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
 | 
				
			||||||
@ -33,8 +31,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 | 
				
			|||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 | 
					github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 | 
				
			||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 | 
					github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 | 
				
			||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 | 
					github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 | 
				
			||||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
 | 
					github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
 | 
				
			||||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
 | 
					github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
 | 
				
			||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
 | 
					github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
 | 
				
			||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 | 
					github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 | 
				
			||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
					github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
				
			||||||
 | 
				
			|||||||
@ -29,7 +29,6 @@ import (
 | 
				
			|||||||
	"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/usecase"
 | 
						"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/usecase"
 | 
				
			||||||
	"git.vinchent.xyz/vinchent/howmuch/internal/pkg/core"
 | 
						"git.vinchent.xyz/vinchent/howmuch/internal/pkg/core"
 | 
				
			||||||
	"git.vinchent.xyz/vinchent/howmuch/internal/pkg/errno"
 | 
						"git.vinchent.xyz/vinchent/howmuch/internal/pkg/errno"
 | 
				
			||||||
	"github.com/asaskevich/govalidator"
 | 
					 | 
				
			||||||
	"github.com/gin-gonic/gin"
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -58,22 +57,26 @@ func NewUserController(us usecase.User) User {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (uc *UserController) Create(ctx core.Context) {
 | 
					func (uc *UserController) Create(ctx core.Context) {
 | 
				
			||||||
	var params model.User
 | 
						var params struct {
 | 
				
			||||||
 | 
							Email     string `json:"email" binding:"required,email"`
 | 
				
			||||||
 | 
							FirstName string `json:"first_name" binding:"required"`
 | 
				
			||||||
 | 
							LastName  string `json:"last_name" binding:"required"`
 | 
				
			||||||
 | 
							Password  string `json:"password" binding:"required"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := ctx.Bind(¶ms); err != nil {
 | 
						if err := ctx.Bind(¶ms); err != nil {
 | 
				
			||||||
		core.WriteResponse(ctx, UserParamsErr, nil)
 | 
							core.WriteResponse(ctx, UserParamsErr, nil)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := govalidator.ValidateStruct(params)
 | 
						user := model.User{
 | 
				
			||||||
	if err != nil {
 | 
							Email:     params.Email,
 | 
				
			||||||
		errno := UserParamsErr
 | 
							FirstName: params.FirstName,
 | 
				
			||||||
		errno.Message = err.Error()
 | 
							LastName:  params.LastName,
 | 
				
			||||||
		core.WriteResponse(ctx, errno, nil)
 | 
							Password:  params.Password,
 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err = uc.userUsecase.Create(ctx, ¶ms)
 | 
						_, err := uc.userUsecase.Create(ctx, &user)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		core.WriteResponse(ctx, err, nil)
 | 
							core.WriteResponse(ctx, err, nil)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 | 
				
			|||||||
@ -26,11 +26,11 @@ import "time"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// User model
 | 
					// User model
 | 
				
			||||||
type User struct {
 | 
					type User struct {
 | 
				
			||||||
	ID        int       `json:"id"`
 | 
						ID        int
 | 
				
			||||||
	Email     string    `json:"email"      valid:"email"`
 | 
						Email     string
 | 
				
			||||||
	FirstName string    `json:"first_name" valid:"required"`
 | 
						FirstName string
 | 
				
			||||||
	LastName  string    `json:"last_name"  valid:"required"`
 | 
						LastName  string
 | 
				
			||||||
	Password  string    `json:"password"   valid:"required"`
 | 
						Password  string
 | 
				
			||||||
	CreatedAt time.Time `json:"created_at"`
 | 
						CreatedAt time.Time
 | 
				
			||||||
	UpdatedAt time.Time `json:"updated_at"`
 | 
						UpdatedAt time.Time
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -61,7 +61,7 @@ type userUsecase struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type User interface {
 | 
					type User interface {
 | 
				
			||||||
	Create(ctx context.Context, u *model.User) (*model.User, error)
 | 
						Create(ctx context.Context, u *model.User) (*model.User, error)
 | 
				
			||||||
	Exist(ctx context.Context, u *model.User) (bool, error)
 | 
						Exist(ctx context.Context, u *model.User) error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewUserUsecase(r repo.UserRepository, d repo.DBRepository) User {
 | 
					func NewUserUsecase(r repo.UserRepository, d repo.DBRepository) User {
 | 
				
			||||||
@ -113,23 +113,23 @@ func (uuc *userUsecase) Create(ctx context.Context, u *model.User) (*model.User,
 | 
				
			|||||||
	return user, nil
 | 
						return user, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (uuc *userUsecase) Exist(ctx context.Context, u *model.User) (bool, error) {
 | 
					func (uuc *userUsecase) Exist(ctx context.Context, u *model.User) error {
 | 
				
			||||||
	got, err := uuc.userRepo.GetByEmail(ctx, u.Email)
 | 
						got, err := uuc.userRepo.GetByEmail(ctx, u.Email)
 | 
				
			||||||
	// Any query error?
 | 
						// Any query error?
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return false, err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// User exists?
 | 
						// User exists?
 | 
				
			||||||
	if got == nil {
 | 
						if got == nil {
 | 
				
			||||||
		return false, UserNotExist
 | 
							return UserNotExist
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Password correct?
 | 
						// Password correct?
 | 
				
			||||||
	err = bcrypt.CompareHashAndPassword([]byte(got.Password), []byte(u.Password))
 | 
						err = bcrypt.CompareHashAndPassword([]byte(got.Password), []byte(u.Password))
 | 
				
			||||||
	if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
 | 
						if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
 | 
				
			||||||
		return false, UserWrongPassword
 | 
							return UserWrongPassword
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true, nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -69,32 +69,30 @@ func TestUserExist(t *testing.T) {
 | 
				
			|||||||
		Name   string
 | 
							Name   string
 | 
				
			||||||
		User   *model.User
 | 
							User   *model.User
 | 
				
			||||||
		ExpErr error
 | 
							ExpErr error
 | 
				
			||||||
		ExpRes bool
 | 
					 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{"user exists", &model.User{
 | 
							{"user exists", &model.User{
 | 
				
			||||||
			Email:    "a@b.c",
 | 
								Email:    "a@b.c",
 | 
				
			||||||
			Password: "strongHashed",
 | 
								Password: "strongHashed",
 | 
				
			||||||
		}, nil, true},
 | 
							}, nil},
 | 
				
			||||||
		{"query error", &model.User{
 | 
							{"query error", &model.User{
 | 
				
			||||||
			Email:    "query@error.com",
 | 
								Email:    "query@error.com",
 | 
				
			||||||
			Password: "strongHashed",
 | 
								Password: "strongHashed",
 | 
				
			||||||
		}, repomock.UserTestDummyErr, false},
 | 
							}, repomock.UserTestDummyErr},
 | 
				
			||||||
		{"user doesn not exist", &model.User{
 | 
							{"user doesn not exist", &model.User{
 | 
				
			||||||
			Email:    "inexist@error.com",
 | 
								Email:    "inexist@error.com",
 | 
				
			||||||
			Password: "strongHashed",
 | 
								Password: "strongHashed",
 | 
				
			||||||
		}, UserNotExist, false},
 | 
							}, UserNotExist},
 | 
				
			||||||
		{"wrong password", &model.User{
 | 
							{"wrong password", &model.User{
 | 
				
			||||||
			Email:    "a@b.c",
 | 
								Email:    "a@b.c",
 | 
				
			||||||
			Password: "wrongHashed",
 | 
								Password: "wrongHashed",
 | 
				
			||||||
		}, UserWrongPassword, false},
 | 
							}, UserWrongPassword},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, tst := range testCases {
 | 
						for _, tst := range testCases {
 | 
				
			||||||
		ctx := context.Background()
 | 
							ctx := context.Background()
 | 
				
			||||||
		userUsecase := NewUserUsecase(&repomock.TestUserRepository{}, &repomock.TestDBRepository{})
 | 
							userUsecase := NewUserUsecase(&repomock.TestUserRepository{}, &repomock.TestDBRepository{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		got, err := userUsecase.Exist(ctx, tst.User)
 | 
							err := userUsecase.Exist(ctx, tst.User)
 | 
				
			||||||
		assert.ErrorIs(t, err, tst.ExpErr)
 | 
							assert.ErrorIs(t, err, tst.ExpErr)
 | 
				
			||||||
		assert.Equal(t, tst.ExpRes, got)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,25 @@
 | 
				
			|||||||
 | 
					// MIT License
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Copyright (c) 2024 vinchent <vinchent@vinchent.xyz>
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					// of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					// in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					// copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					// furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The above copyright notice and this permission notice shall be included in all
 | 
				
			||||||
 | 
					// copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
				
			||||||
 | 
					// SOFTWARE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package test
 | 
					package test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user