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
|
email, first_name, last_name, password, created_at, updated_at
|
||||||
) VALUES ( $1, $2, $3, $4, $5, $6 )
|
) VALUES ( $1, $2, $3, $4, $5, $6 )
|
||||||
RETURNING *;
|
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"
|
"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
|
const insertUser = `-- name: InsertUser :one
|
||||||
|
|
||||||
INSERT INTO "user" (
|
INSERT INTO "user" (
|
||||||
|
@ -86,3 +86,25 @@ func (ur *userRepository) Create(
|
|||||||
UpdatedAt: userDB.CreatedAt.Time,
|
UpdatedAt: userDB.CreatedAt.Time,
|
||||||
}, nil
|
}, 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"
|
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var UserTestDummyErr = errors.New("dummy error")
|
||||||
|
|
||||||
type TestUserRepository struct{}
|
type TestUserRepository struct{}
|
||||||
|
|
||||||
func (tur *TestUserRepository) Create(
|
func (tur *TestUserRepository) Create(
|
||||||
@ -46,3 +49,21 @@ func (tur *TestUserRepository) Create(
|
|||||||
|
|
||||||
return &user, nil
|
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 {
|
type UserRepository interface {
|
||||||
Create(ctx context.Context, transaction interface{}, u *model.User) (*model.User, error)
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -35,11 +36,23 @@ import (
|
|||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var UserExisted = &errno.Errno{
|
var (
|
||||||
|
UserExisted = &errno.Errno{
|
||||||
HTTP: http.StatusBadRequest,
|
HTTP: http.StatusBadRequest,
|
||||||
Code: errno.ErrorCode(errno.FailedOperationCode, "UserExisted"),
|
Code: errno.ErrorCode(errno.FailedOperationCode, "UserExisted"),
|
||||||
Message: "email already existed.",
|
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 {
|
type userUsecase struct {
|
||||||
userRepo repo.UserRepository
|
userRepo repo.UserRepository
|
||||||
@ -48,6 +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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserUsecase(r repo.UserRepository, d repo.DBRepository) User {
|
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
|
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())
|
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