fix: make user signup work for the minimum

A lot of work is still to be done ...
This commit is contained in:
Muyao CHEN 2024-10-06 18:26:57 +02:00
parent 7b8abf8e5c
commit 344485d082
13 changed files with 103 additions and 24 deletions

1
.gitignore vendored
View File

@ -25,3 +25,4 @@ go.work.sum
# Custom
/_output
/deployment/db_data
/tmp/**

View File

@ -39,7 +39,7 @@ tags:
- name: user
paths:
/signup:
/user/signup:
post:
tags:
- user

View File

7
docs/error_code.md Normal file
View File

@ -0,0 +1,7 @@
# Platform level error code design
- InternalError
- InvalidParameter
- AuthFailure
- ResourceNotFound
- FailedOperation

View File

@ -25,33 +25,66 @@ package controller
import (
"net/http"
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model"
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/usecase"
"git.vinchent.xyz/vinchent/howmuch/internal/pkg/core"
"git.vinchent.xyz/vinchent/howmuch/internal/pkg/errno"
"github.com/gin-gonic/gin"
)
// User is the user controller interface, it describes all the handlers
// that need to be implemented for the /user endpoint
type User interface {
Signup(core.Context)
UpdateInfo(core.Context)
Login(core.Context)
Logout(core.Context)
ChangePassword(core.Context)
UpdateInfo(*gin.Context)
Login(*gin.Context)
Logout(*gin.Context)
ChangePassword(*gin.Context)
}
type UserController struct{}
type UserController struct {
userUsecase usecase.User
}
var UserParamsErr = &errno.Errno{
HTTP: http.StatusBadRequest,
Code: errno.ErrorCode(errno.InvalidParameterCode, "UserParamsErr"),
Message: "user info is not correct",
}
func NewUserController(us usecase.User) User {
return &UserController{
userUsecase: us,
}
}
func (uc *UserController) Signup(ctx core.Context) {
ctx.JSON(http.StatusOK, "hello")
var params model.User
if err := ctx.Bind(&params); err != nil {
core.WriteResponse(ctx, UserParamsErr, nil)
return
}
// TODO: check params validity (govalidator)
_, err := uc.userUsecase.Create(ctx, &params)
if err != nil {
core.WriteResponse(ctx, err, nil)
return
}
core.WriteResponse(ctx, errno.OK, nil)
}
func (uc *UserController) UpdateInfo(ctx core.Context) {
func (uc *UserController) UpdateInfo(ctx *gin.Context) {
}
func (uc *UserController) Login(ctx core.Context) {
func (uc *UserController) Login(ctx *gin.Context) {
}
func (uc *UserController) Logout(ctx core.Context) {
func (uc *UserController) Logout(ctx *gin.Context) {
}
func (uc *UserController) ChangePassword(ctx core.Context) {
func (uc *UserController) ChangePassword(ctx *gin.Context) {
}

View File

@ -34,9 +34,6 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
// Get business service model, convert to the DB model (generated by sqlc)
// To test repo's methods, I have to mock a pgx.Conn
type userRepository struct {
db *pgx.Conn
}
@ -67,9 +64,9 @@ func (ur *userRepository) Create(
UpdatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
}
tx, ok := transaction.(*pgx.Conn)
tx, ok := transaction.(pgx.Tx)
if !ok {
return nil, errors.New("transaction is not a *pgx.Conn")
return nil, errors.New("transaction is not a pgx.Tx")
}
queries := sqlc.New(tx)

View File

@ -48,7 +48,13 @@ func Routes(engine *gin.Engine, c controller.AppController) *gin.Engine {
core.WriteResponse(ctx, errno.PageNotFoundErr, nil)
})
engine.POST("/signup", func(ctx *gin.Context) { c.User.Signup(ctx) })
v1 := engine.Group("/v1")
{
userV1 := v1.Group("/user")
{
userV1.POST("/signup", func(ctx *gin.Context) { c.User.Signup(ctx) })
}
}
return engine
}

View File

@ -22,9 +22,14 @@
package registry
import "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/controller"
import (
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/controller"
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo"
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/usecase"
)
// NewUserController returns a user controller's implementation
func (r *registry) NewUserController() controller.User {
return &controller.UserController{}
u := usecase.NewUserUsecase(repo.NewUserRepository(r.db), repo.NewDBRepository(r.db))
return controller.NewUserController(u)
}

View File

@ -51,6 +51,10 @@ func (uuc *userUsecase) Create(ctx context.Context, u *model.User) (*model.User,
data, err := uuc.dbRepo.Transaction(
ctx,
func(txCtx context.Context, tx interface{}) (interface{}, error) {
// TODO: should check if the user exists
// DB will return an error since we have set email to UNIQUE.
// But we may not want to expose the exact db error.
u, err := uuc.userRepo.Create(txCtx, tx, u)
if err != nil {
return nil, err
@ -68,11 +72,12 @@ func (uuc *userUsecase) Create(ctx context.Context, u *model.User) (*model.User,
return u, err
},
)
user := data.(*model.User)
if err != nil {
// TODO: We should wrap the error at service level
return nil, err
}
user := data.(*model.User)
return user, nil
}

View File

@ -22,6 +22,18 @@
package core
import "time"
type Context interface {
// Context
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
// Request
Bind(obj any) error
// Response
JSON(code int, obj any)
}

View File

@ -24,18 +24,28 @@ package errno
import "net/http"
type PlatformLevelErrCode string
const (
InternalErrorCode = "InternalError"
InvalidParameterCode = "InvalidParameter"
AuthFailureCode = "AuthFailure"
ResourceNotFoundCode = "ResourceNotFound"
FailedOperationCode = "FailedOperation"
)
var (
OK = &Errno{HTTP: http.StatusOK, Code: "", Message: ""}
InternalServerErr = &Errno{
HTTP: http.StatusInternalServerError,
Code: "InternalError",
Code: InternalErrorCode,
Message: "Internal server error",
}
PageNotFoundErr = &Errno{
HTTP: http.StatusNotFound,
Code: "ResourceNotFound.PageNotFound",
Code: ErrorCode(ResourceNotFoundCode, "PageNotFound"),
Message: "Page not found",
}
)

View File

@ -28,6 +28,10 @@ type Errno struct {
Message string
}
func ErrorCode(platformErrCode string, resourceErrCode string) string {
return platformErrCode + "." + resourceErrCode
}
// Error implements Error() method in error interface
func (err *Errno) Error() string {
return err.Message

View File

@ -1 +0,0 @@
exit status 2exit status 2exit status 2exit status 2exit status 2