feat: add usecase to check if a user exists
This commit is contained in:
parent
c312b4e2c8
commit
3e09afd4b0
@ -25,3 +25,8 @@ INSERT INTO "user" (
|
||||
email, first_name, last_name, password, created_at, updated_at
|
||||
) VALUES ( $1, $2, $3, $4, $5, $6 )
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetUserByEmail :one
|
||||
SELECT id, email, first_name, last_name, password, created_at, updated_at
|
||||
FROM "user"
|
||||
WHERE email = $1;
|
||||
|
@ -11,6 +11,27 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const getUserByEmail = `-- name: GetUserByEmail :one
|
||||
SELECT id, email, first_name, last_name, password, created_at, updated_at
|
||||
FROM "user"
|
||||
WHERE email = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByEmail, email)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.Password,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertUser = `-- name: InsertUser :one
|
||||
|
||||
INSERT INTO "user" (
|
||||
|
@ -86,3 +86,25 @@ func (ur *userRepository) Create(
|
||||
UpdatedAt: userDB.CreatedAt.Time,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetByEmail if not found, return nil for user but not error.
|
||||
func (ur *userRepository) GetByEmail(ctx context.Context, email string) (*model.User, error) {
|
||||
queries := sqlc.New(ur.db)
|
||||
userDB, err := queries.GetUserByEmail(ctx, email)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
// No query error, but user not found
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.User{
|
||||
ID: int(userDB.ID),
|
||||
Email: userDB.Email,
|
||||
FirstName: userDB.FirstName,
|
||||
LastName: userDB.LastName,
|
||||
Password: userDB.Password,
|
||||
CreatedAt: userDB.CreatedAt.Time,
|
||||
UpdatedAt: userDB.CreatedAt.Time,
|
||||
}, nil
|
||||
}
|
||||
|
@ -27,8 +27,11 @@ import (
|
||||
|
||||
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var UserTestDummyErr = errors.New("dummy error")
|
||||
|
||||
type TestUserRepository struct{}
|
||||
|
||||
func (tur *TestUserRepository) Create(
|
||||
@ -46,3 +49,21 @@ func (tur *TestUserRepository) Create(
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (ur *TestUserRepository) GetByEmail(ctx context.Context, email string) (*model.User, error) {
|
||||
hashedPwd, _ := bcrypt.GenerateFromPassword([]byte("strongHashed"), 12)
|
||||
switch email {
|
||||
case "a@b.c":
|
||||
return &model.User{
|
||||
ID: 123,
|
||||
Email: "a@b.c",
|
||||
Password: string(hashedPwd),
|
||||
}, nil
|
||||
case "query@error.com":
|
||||
return nil, UserTestDummyErr
|
||||
case "inexist@error.com":
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, UserTestDummyErr
|
||||
}
|
||||
|
@ -30,4 +30,5 @@ import (
|
||||
|
||||
type UserRepository interface {
|
||||
Create(ctx context.Context, transaction interface{}, u *model.User) (*model.User, error)
|
||||
GetByEmail(ctx context.Context, email string) (*model.User, error)
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@ -35,11 +36,23 @@ import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var UserExisted = &errno.Errno{
|
||||
var (
|
||||
UserExisted = &errno.Errno{
|
||||
HTTP: http.StatusBadRequest,
|
||||
Code: errno.ErrorCode(errno.FailedOperationCode, "UserExisted"),
|
||||
Message: "email already existed.",
|
||||
}
|
||||
UserNotExist = &errno.Errno{
|
||||
HTTP: http.StatusBadRequest,
|
||||
Code: errno.ErrorCode(errno.ResourceNotFoundCode, "UserNotExist"),
|
||||
Message: "user does not exists.",
|
||||
}
|
||||
UserWrongPassword = &errno.Errno{
|
||||
HTTP: http.StatusBadRequest,
|
||||
Code: errno.ErrorCode(errno.AuthFailureCode, "UserWrongPassword"),
|
||||
Message: "wrong password.",
|
||||
}
|
||||
)
|
||||
|
||||
type userUsecase struct {
|
||||
userRepo repo.UserRepository
|
||||
@ -48,6 +61,7 @@ type userUsecase struct {
|
||||
|
||||
type User interface {
|
||||
Create(ctx context.Context, u *model.User) (*model.User, error)
|
||||
Exist(ctx context.Context, u *model.User) (bool, error)
|
||||
}
|
||||
|
||||
func NewUserUsecase(r repo.UserRepository, d repo.DBRepository) User {
|
||||
@ -98,3 +112,24 @@ func (uuc *userUsecase) Create(ctx context.Context, u *model.User) (*model.User,
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (uuc *userUsecase) Exist(ctx context.Context, u *model.User) (bool, error) {
|
||||
got, err := uuc.userRepo.GetByEmail(ctx, u.Email)
|
||||
// Any query error?
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// User exists?
|
||||
if got == nil {
|
||||
return false, UserNotExist
|
||||
}
|
||||
|
||||
// Password correct?
|
||||
err = bcrypt.CompareHashAndPassword([]byte(got.Password), []byte(u.Password))
|
||||
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
|
||||
return false, UserWrongPassword
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
@ -63,3 +63,38 @@ func TestCreateUser(t *testing.T) {
|
||||
assert.EqualError(t, err, UserExisted.Error())
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserExist(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
User *model.User
|
||||
ExpErr error
|
||||
ExpRes bool
|
||||
}{
|
||||
{"user exists", &model.User{
|
||||
Email: "a@b.c",
|
||||
Password: "strongHashed",
|
||||
}, nil, true},
|
||||
{"query error", &model.User{
|
||||
Email: "query@error.com",
|
||||
Password: "strongHashed",
|
||||
}, repo.UserTestDummyErr, false},
|
||||
{"user doesn not exist", &model.User{
|
||||
Email: "inexist@error.com",
|
||||
Password: "strongHashed",
|
||||
}, UserNotExist, false},
|
||||
{"wrong password", &model.User{
|
||||
Email: "a@b.c",
|
||||
Password: "wrongHashed",
|
||||
}, UserWrongPassword, false},
|
||||
}
|
||||
|
||||
for _, tst := range testCases {
|
||||
ctx := context.Background()
|
||||
userUsecase := NewUserUsecase(&repo.TestUserRepository{}, &repo.TestDBRepository{})
|
||||
|
||||
got, err := userUsecase.Exist(ctx, tst.User)
|
||||
assert.ErrorIs(t, err, tst.ExpErr)
|
||||
assert.Equal(t, tst.ExpRes, got)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user