From 78071a6a917ef36561c93ec07f312caf26af9bba Mon Sep 17 00:00:00 2001 From: Jeffrey Duroyon Date: Mon, 8 Nov 2021 00:26:01 +0100 Subject: [PATCH] fix(account): add account creante and fix incorrect field for getting all --- ginserver/router.go | 4 ++- internal/account/database.go | 42 +++++++++++++++++++++++++-- internal/account/handler.go | 55 +++++++++++++++++++++++++++++++++++- internal/account/model.go | 12 ++++---- internal/account/service.go | 22 ++++++++++++++- middleware/oauth_token.go | 36 +++++++++++++++++++++++ 6 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 middleware/oauth_token.go diff --git a/ginserver/router.go b/ginserver/router.go index 0c81f33..e72f5b4 100644 --- a/ginserver/router.go +++ b/ginserver/router.go @@ -8,6 +8,7 @@ import ( sharedaccount "nos-comptes/internal/shared-account" "nos-comptes/internal/storage/dao/postgresql" "nos-comptes/internal/user" + "nos-comptes/middleware" "time" "github.com/gin-gonic/gin" @@ -48,12 +49,13 @@ func NewRouter(config *handler.Config) *gin.Engine { userRoute.Handle(http.MethodPost, "", uh.CreateUser) securedUserRoute := userRoute.Group("") + securedUserRoute.Use(middleware.ValidateOAuthToken) //TODO add secure auth securedUserRoute.Handle(http.MethodGet, "/:userId", uh.GetUser) //account route securedUserRoute.Handle(http.MethodGet, "/:userId/accounts", ah.GetAllAccountOfUser) - securedUserRoute.Handle(http.MethodPost, "/:userId/accounts/:accountId", ah.CreateAccountOfUser) + securedUserRoute.Handle(http.MethodPost, "/:userId/accounts", ah.CreateAccountOfUser) securedUserRoute.Handle(http.MethodDelete, "/:userId/accounts/:accountId", ah.DeleteAccountOfUser) securedUserRoute.Handle(http.MethodGet, "/:userId/accounts/:accountId", ah.GetSpecificAccountOfUser) diff --git a/internal/account/database.go b/internal/account/database.go index 60211fe..70f76fd 100644 --- a/internal/account/database.go +++ b/internal/account/database.go @@ -2,6 +2,8 @@ package account import ( "nos-comptes/internal/storage/dao/postgresql" + + "github.com/lib/pq" ) type Database struct { @@ -12,7 +14,7 @@ func (db *Database) GetAllAccountOfUser(id string) ([]*Account, error) { q := ` SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at FROM public.account a - WHERE a.id = $1 + WHERE a.user_id = $1 ` rows, err := db.Session.Query(q, id) if err != nil { @@ -23,7 +25,7 @@ func (db *Database) GetAllAccountOfUser(id string) ([]*Account, error) { as := make([]*Account, 0) for rows.Next() { a := Account{} - err := rows.Scan(&a.ID, &a.userId, &a.name, &a.provider, &a.CreatedAt, &a.UpdatedAt) + err := rows.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt) if err != nil { return nil, err } @@ -32,6 +34,42 @@ func (db *Database) GetAllAccountOfUser(id string) ([]*Account, error) { return as, nil } +func (db *Database) GetAccountWithNameForUser(name string, id string) (*Account, error) { + q := ` + SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at + FROM public.account a + WHERE a.user_id = $1 + AND a.name = $2 + ` + row := db.Session.QueryRow(q, id, name) + + a := Account{} + err := row.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt) + if errPq, ok := err.(*pq.Error); ok { + return nil, postgresql.HandlePgError(errPq) + } + return &a, nil +} + +func (db *Database) CreateAccount(account *Account) error { + q := ` + INSERT INTO public.account + (Name, Provider, user_id) + VALUES + ($1, $2, $3) + RETURNING id, created_at + ` + + err := db.Session. + QueryRow(q, account.Name, account.Provider, account.UserId). + Scan(&account.ID, &account.CreatedAt) + if errPq, ok := err.(*pq.Error); ok { + return postgresql.HandlePgError(errPq) + } + return err + +} + func NewDatabase(db *postgresql.DatabasePostgreSQL) *Database { return &Database{db} } diff --git a/internal/account/handler.go b/internal/account/handler.go index 546d43e..da94a6f 100644 --- a/internal/account/handler.go +++ b/internal/account/handler.go @@ -1,6 +1,7 @@ package account import ( + "fmt" "net/http" "nos-comptes/handler" "nos-comptes/internal/storage/dao/postgresql" @@ -56,8 +57,60 @@ func (c *Context) GetAllAccountOfUser(gc *gin.Context) { } -func (c *Context) CreateAccountOfUser(context *gin.Context) { +func (c *Context) CreateAccountOfUser(gc *gin.Context) { + userId := gc.Param("userId") + err := c.Validator.VarCtx(gc, userId, "uuid4") + if err != nil { + utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) + return + } + _, err = c.userService.GetUserById(userId) + if e, ok := err.(*model.APIError); ok { + utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error") + utils.JSONErrorWithMessage(gc.Writer, *e, e.Description) + } else if err != nil { + utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user") + utils.JSONError(gc.Writer, model.ErrInternalServer) + return + } + var account Account + var accountEditable AccountEditable + if err := gc.BindJSON(&accountEditable); err != nil { + utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) + return + } + account = Account{AccountEditable: accountEditable, UserId: userId} + utils.GetLogger().Warn(account) + utils.GetLogger().Warn(accountEditable.Name) + utils.GetLogger().Warn(accountEditable.Provider) + accountFound, err := c.service.GetAccountWithNameForUser(account.Name, userId) + utils.GetLogger().Warn(err) + utils.GetLogger().Warn(accountFound) + if e, ok := err.(*model.APIError); ok { + if e.Type != model.ErrNotFound.Type { + utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error") + utils.JSONErrorWithMessage(gc.Writer, *e, e.Description) + } + } else if err != nil { + utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user") + utils.JSONError(gc.Writer, model.ErrInternalServer) + return + } + + if accountFound != nil { + utils.GetLoggerFromCtx(gc).WithError(&model.ErrAlreadyExists).WithField("type", model.ErrAlreadyExists.Type).Error("error CreateAccount: account already exists") + utils.JSONErrorWithMessage(gc.Writer, model.ErrAlreadyExists, "account already exists with the same Name") + } + account.UserId = userId + accountSaved, err := c.service.CreateAccount(account) + if err != nil { + fmt.Println(err) + utils.JSONError(gc.Writer, model.ErrInternalServer) + return + } + utils.JSON(gc.Writer, http.StatusCreated, accountSaved) + return } func (c *Context) DeleteAccountOfUser(context *gin.Context) { diff --git a/internal/account/model.go b/internal/account/model.go index 61c49ee..a74ec61 100644 --- a/internal/account/model.go +++ b/internal/account/model.go @@ -4,13 +4,13 @@ import "time" type Account struct { AccountEditable - userId string `json:"userId"` + UserId string `json:"userId,omitempty"` } type AccountEditable struct { - ID string `json:"id"` - name string `json:"name"` - provider string `json:"provider"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt *time.Time `json:"updatedAt"` + ID string `json:"id,omitempty"` + Name string `json:"name"` + Provider string `json:"provider"` + CreatedAt time.Time `json:"createdAt,omitempty"` + UpdatedAt *time.Time `json:"updatedAt,omitempty"` } diff --git a/internal/account/service.go b/internal/account/service.go index f3e204f..1cd9584 100644 --- a/internal/account/service.go +++ b/internal/account/service.go @@ -12,7 +12,6 @@ type Service struct { func (s *Service) GetAllAccountOfUser(userId string) ([]*Account, error) { accounts, err := s.db.GetAllAccountOfUser(userId) - utils.GetLogger().Warn(err) if e, ok := err.(*dao.Error); ok { switch { case e.Type == dao.ErrTypeNotFound: @@ -30,6 +29,27 @@ func (s *Service) GetAllAccountOfUser(userId string) ([]*Account, error) { return accounts, nil } +func (s *Service) GetAccountWithNameForUser(name string, id string) (*Account, error) { + account, err := s.db.GetAccountWithNameForUser(name, id) + utils.GetLogger().Warn(err) + if e, ok := err.(*dao.Error); ok { + switch { + case e.Type == dao.ErrTypeNotFound: + return nil, &model.ErrNotFound + default: + return nil, &model.ErrInternalServer + } + } else if err != nil { + return nil, &model.ErrInternalServer + } + return account, nil +} + +func (s *Service) CreateAccount(account Account) (*Account, error) { + err := s.db.CreateAccount(&account) + return &account, err +} + func NewService(database *Database) *Service { return &Service{db: database} } diff --git a/middleware/oauth_token.go b/middleware/oauth_token.go new file mode 100644 index 0000000..6a2bc61 --- /dev/null +++ b/middleware/oauth_token.go @@ -0,0 +1,36 @@ +package middleware + +import ( + "fmt" + "net/http" + "nos-comptes/internal/storage/model" + "nos-comptes/internal/utils" + "strings" + + "github.com/gin-gonic/gin" + "google.golang.org/api/oauth2/v1" +) + +func ValidateOAuthToken(c *gin.Context) { + authorizationHeader := c.GetHeader("Authorization") + authorizationHeaderSplitted := strings.Split(authorizationHeader, " ") + if len(authorizationHeaderSplitted) != 2 { + utils.JSONError(c.Writer, model.ErrBadRequestFormat) + return + } + + oauth2Service, err := oauth2.New(&http.Client{}) + if oauth2Service == nil { + fmt.Println(err) + utils.JSONError(c.Writer, model.ErrInternalServer) + return + } + tokenInfoCall := oauth2Service.Tokeninfo() + tokenInfoCall.IdToken(authorizationHeaderSplitted[1]) + _, err = tokenInfoCall.Do() + if err != nil { + utils.GetLogger().WithError(err).Error(err) + utils.JSONError(c.Writer, model.ErrBadRequestFormat) + return + } +}