diff --git a/.DS_Store b/.DS_Store index 2eed2be..7e681a4 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/cmd/root.go b/cmd/root.go index d4e9fda..6411e1f 100755 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,9 +2,14 @@ package cmd import ( "fmt" - "nos-comptes/ginserver" "nos-comptes/handler" + "nos-comptes/internal/account" + "nos-comptes/internal/expense" + ginserver "nos-comptes/internal/ginserver" + "nos-comptes/internal/storage/dao/postgresql" + "nos-comptes/internal/user" "nos-comptes/internal/utils" + validatorInternal "nos-comptes/internal/utils/validator" "os" "github.com/sirupsen/logrus" @@ -20,7 +25,6 @@ var ( const ( parameterConfigurationFile = "config" parameterLogLevel = "loglevel" - parameterMock = "mock" parameterLogFormat = "logformat" parameterDBConnectionURI = "dbconnectionuri" parameterPort = "port" @@ -40,15 +44,19 @@ var rootCmd = &cobra.Command{ utils.InitLogger(config.LogLevel, config.LogFormat) logrus. WithField(parameterConfigurationFile, cfgFile). - WithField(parameterMock, config.Mock). WithField(parameterLogLevel, config.LogLevel). WithField(parameterLogFormat, config.LogFormat). WithField(parameterPort, config.Port). WithField(parameterDBConnectionURI, config.DBConnectionURI). Warn("Configuration") - - router := ginserver.NewRouter(config) - router.Run(fmt.Sprintf(":%d", config.Port)) + injector := &utils.Injector{} + ginserver.Setup(injector, config) + postgresql.Setup(injector, config.DBConnectionURI) + validatorInternal.Setup(injector) + user.Setup(injector) + account.Setup(injector) + expense.Setup(injector) + ginserver.Start(injector) }, } @@ -76,8 +84,6 @@ func init() { rootCmd.Flags().Int(parameterPort, defaultPort, "Use this flag to set the listening port of the api") viper.BindPFlag(parameterPort, rootCmd.Flags().Lookup(parameterPort)) - rootCmd.Flags().Bool(parameterMock, false, "Use this flag to enable the mock mode") - viper.BindPFlag(parameterMock, rootCmd.Flags().Lookup(parameterMock)) } // initConfig reads in config file and ENV variables if set. @@ -96,7 +102,6 @@ func initConfig() { config.LogLevel = viper.GetString(parameterLogLevel) config.LogFormat = viper.GetString(parameterLogFormat) - config.Mock = viper.GetBool(parameterMock) config.DBConnectionURI = viper.GetString(parameterDBConnectionURI) config.Port = viper.GetInt(parameterPort) } diff --git a/ginserver/router.go b/ginserver/router.go deleted file mode 100644 index b268493..0000000 --- a/ginserver/router.go +++ /dev/null @@ -1,87 +0,0 @@ -package ginserver - -import ( - "net/http" - "nos-comptes/handler" - "nos-comptes/internal/account" - "nos-comptes/internal/expense" - "nos-comptes/internal/jointaccount" - "nos-comptes/internal/storage/dao/postgresql" - "nos-comptes/internal/user" - "nos-comptes/middleware" - "time" - - "github.com/gin-gonic/gin" - - "github.com/gin-contrib/cors" -) - -func NewRouter(config *handler.Config) *gin.Engine { - gin.SetMode(gin.ReleaseMode) - - router := gin.New() - router.HandleMethodNotAllowed = true - - router.Use(cors.New(cors.Config{ - AllowOrigins: []string{"http://localhost:8080/", "http://localhost:8080"}, - AllowMethods: []string{"*"}, - AllowHeaders: []string{"*"}, - ExposeHeaders: []string{"*"}, - AllowCredentials: true, - MaxAge: 12 * time.Hour, - })) - router.Use(gin.Recovery()) - router.Use(GetLoggerMiddleware()) - router.Use(GetHTTPLoggerMiddleware()) - - db := postgresql.NewDatabasePostgreSQL(config.DBConnectionURI) - hc := handler.NewContext() - uh := user.NewHandler(hc, db) - ah := account.NewHandler(hc, db) - jah := jointaccount.NewHandler(hc, db) - eh := expense.NewHandler(hc, db) - mv := middleware.NewValidator(hc, db) - public := router.Group("/") - public.Handle(http.MethodGet, "/_health", hc.GetHealth) - - userRoute := public.Group("/users") - userRoute.Handle("GET", "", uh.ConnectUser) - userRoute.Handle(http.MethodPost, "", uh.CreateUser) - - securedUserRoute := userRoute.Group("/") - securedUserRoute.Use(middleware.ValidateOAuthToken) - //TODO add secure auth - securedUserRoute.Handle(http.MethodGet, "/:userId", uh.GetUser) - - securedMatchingToken := securedUserRoute.Group("/:userId") - securedMatchingToken.Use(mv.HasValidUserId) - securedMatchingToken.Use(mv.UserdIdMatchOAuthToken) - //account route - securedMatchingToken.Handle(http.MethodGet, "/accounts", ah.GetAllAccountOfUser) - securedMatchingToken.Handle(http.MethodPost, "/accounts", ah.CreateAccountOfUser) - - securedValidAccount := securedMatchingToken.Group("/accounts/:accountId") - securedValidAccount.Use(mv.HasValidAccountId) - securedValidAccount.Use(mv.AccountExists) - securedValidAccount.Handle(http.MethodDelete, "", ah.DeleteAccountOfUser) - securedValidAccount.Handle(http.MethodGet, "", ah.GetSpecificAccountOfUser) - securedValidAccount.Handle(http.MethodPost, "/expenses", eh.CreateAnExpense) - securedValidAccount.Handle(http.MethodGet, "/expenses", eh.GetAllExpenses) - - securedExistingExpenses := securedValidAccount.Group("/expenses/:expenseId") - securedExistingExpenses.Handle(http.MethodGet, "", eh.GetAnExpenses) - securedExistingExpenses.Handle(http.MethodDelete, "", eh.DeleteExpense) - - //account route - securedMatchingToken.Handle(http.MethodGet, "/jointaccounts", jah.GetAllJointaccountOfUser) - securedMatchingToken.Handle(http.MethodPost, "/jointaccounts", jah.CreateJointaccountOfUser) - - securedValidJointAccount := securedMatchingToken.Group("/jointaccounts/:jointaccountId") - securedValidJointAccount.Use(mv.HasValidJointAccountId) - securedValidJointAccount.Use(mv.JointAccountExists) - securedValidJointAccount.Handle(http.MethodDelete, "", jah.DeleteJointaccountOfUser) - securedValidJointAccount.Handle(http.MethodGet, "", jah.GetSpecificJointaccountOfUser) - securedValidJointAccount.Handle(http.MethodPost, "/expenses", eh.CreateAnExpense) - securedValidJointAccount.Handle(http.MethodGet, "/expenses", eh.GetAllExpenses) - return router -} diff --git a/go.mod b/go.mod index ad16bfa..90a063d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module nos-comptes - go 1.17 + go 1.18 require ( github.com/gin-contrib/cors v1.3.1 diff --git a/handler/handler.go b/handler/handler.go index 28e525a..39a00ef 100755 --- a/handler/handler.go +++ b/handler/handler.go @@ -1,51 +1,18 @@ package handler import ( - "net/http" - "nos-comptes/internal/storage/validators" - "nos-comptes/internal/utils" - "reflect" - "strings" - "github.com/gin-gonic/gin" - "gopkg.in/go-playground/validator.v9" + "net/http" + "nos-comptes/internal/utils" ) type Config struct { - Mock bool DBConnectionURI string Port int LogLevel string LogFormat string } -type Context struct { - Validator *validator.Validate -} - -func NewContext() *Context { - return &Context{Validator: newValidator()} -} -func newValidator() *validator.Validate { - va := validator.New() - - va.RegisterTagNameFunc(func(fld reflect.StructField) string { - name := strings.SplitN(fld.Tag.Get("json"), ",", 2) - if len(name) < 1 { - return "" - } - return name[0] - }) - - for k, v := range validators.CustomValidators { - if v.Validator != nil { - va.RegisterValidationCtx(k, v.Validator) - } - } - - return va -} - -func (hc *Context) GetHealth(c *gin.Context) { +func GetHealth(c *gin.Context) { utils.JSON(c.Writer, http.StatusNoContent, nil) } diff --git a/internal/.DS_Store b/internal/.DS_Store new file mode 100644 index 0000000..2858e5c Binary files /dev/null and b/internal/.DS_Store differ diff --git a/internal/account/handler.go b/internal/account/handler.go index f9981b8..eaa813e 100644 --- a/internal/account/handler.go +++ b/internal/account/handler.go @@ -2,8 +2,6 @@ package account import ( "net/http" - "nos-comptes/handler" - "nos-comptes/internal/storage/dao/postgresql" "nos-comptes/internal/storage/model" "nos-comptes/internal/storage/validators" "nos-comptes/internal/user" @@ -16,7 +14,7 @@ type Context struct { service *Service db *Database userService *user.Service - *handler.Context + validator *Validator } func (c *Context) GetAllAccountOfUser(gc *gin.Context) { @@ -92,9 +90,6 @@ func (c *Context) GetSpecificAccountOfUser(gc *gin.Context) { utils.JSON(gc.Writer, http.StatusOK, account) } -func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { - database := NewDatabase(db) - service := NewService(database) - userService := user.NewService(user.NewDatabase(db)) - return &Context{service: service, db: database, userService: userService, Context: ctx} +func NewHandler(validator *Validator, database *Database, service *Service, userService *user.Service) *Context { + return &Context{service: service, db: database, userService: userService, validator: validator} } diff --git a/internal/account/setup.go b/internal/account/setup.go new file mode 100644 index 0000000..add0d88 --- /dev/null +++ b/internal/account/setup.go @@ -0,0 +1,34 @@ +package account + +import ( + "github.com/gin-gonic/gin" + "net/http" + "nos-comptes/internal/ginserver" + "nos-comptes/internal/storage/dao/postgresql" + "nos-comptes/internal/user" + "nos-comptes/internal/utils" +) + +const ServiceInjectorKey = "ACCOUNT_SERVICE" + +func Setup(injector *utils.Injector) { + pg := utils.Get[*postgresql.DatabasePostgreSQL](injector, postgresql.DatabaseKey) + userService := utils.Get[*user.Service](injector, user.ServiceInjectorKey) + database := NewDatabase(pg) + service := NewService(database) + validator := NewValidator(service) + handler := NewHandler(validator, database, service, userService) + securedRoute := utils.Get[*gin.RouterGroup](injector, ginserver.SecuredRouterInjectorKey) + securedUserRoute := securedRoute.Group("/:userId") + //account route + securedUserRoute.Handle(http.MethodGet, "/accounts", handler.GetAllAccountOfUser) + securedUserRoute.Handle(http.MethodPost, "/accounts", handler.CreateAccountOfUser) + + securedValidAccount := securedUserRoute.Group("/accounts/:accountId") + securedValidAccount.Use(validator.HasValidAccountId) + securedValidAccount.Use(validator.AccountExists) + securedValidAccount.Handle(http.MethodDelete, "", handler.DeleteAccountOfUser) + securedValidAccount.Handle(http.MethodGet, "", handler.GetSpecificAccountOfUser) + + injector.Set(ServiceInjectorKey, service) +} diff --git a/internal/account/validator.go b/internal/account/validator.go new file mode 100644 index 0000000..7477c66 --- /dev/null +++ b/internal/account/validator.go @@ -0,0 +1,44 @@ +package account + +import ( + "gopkg.in/go-playground/validator.v9" + "nos-comptes/internal/storage/model" + "nos-comptes/internal/storage/validators" + "nos-comptes/internal/utils" + + "github.com/gin-gonic/gin" +) + +type Validator struct { + accountService *Service + Validator validator.Validate +} + +func NewValidator(service *Service) *Validator { + return &Validator{accountService: service} +} + +func (v Validator) HasValidAccountId(gc *gin.Context) { + accountId := gc.Param("accountId") + err := v.Validator.VarCtx(gc, accountId, "uuid4") + if err != nil { + utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) + return + } +} + +func (v Validator) AccountExists(gc *gin.Context) { + userId := gc.Param("userId") + accountId := gc.Param("accountId") + _, err := v.accountService.GetASpecificAccountForUser(userId, accountId) + if e, ok := err.(*model.APIError); ok { + utils.GetLogger().Info(err) + utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id") + utils.JSONErrorWithMessage(gc.Writer, *e, e.Description) + return + } else if err != nil { + utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id") + utils.JSONError(gc.Writer, model.ErrInternalServer) + return + } +} diff --git a/internal/expense/handler.go b/internal/expense/handler.go index 281a9a9..adfa581 100644 --- a/internal/expense/handler.go +++ b/internal/expense/handler.go @@ -2,10 +2,9 @@ package expense import ( "encoding/csv" + "gopkg.in/go-playground/validator.v9" "net/http" - "nos-comptes/handler" "nos-comptes/internal/account" - "nos-comptes/internal/storage/dao/postgresql" "nos-comptes/internal/storage/model" "nos-comptes/internal/storage/validators" "nos-comptes/internal/utils" @@ -18,7 +17,7 @@ type Context struct { service *Service db *Database accountService *account.Service - *handler.Context + validator *validator.Validate } func (c *Context) ImportExpenseFromCSV(gc *gin.Context) { @@ -124,9 +123,6 @@ func (c *Context) GetAnExpenses(context *gin.Context) { } -func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { - database := NewDatabase(db) - service := NewService(database) - accountService := account.NewService(account.NewDatabase(db)) - return &Context{service: service, db: database, accountService: accountService, Context: ctx} +func NewHandler(validator *validator.Validate, database *Database, service *Service, accountService *account.Service) *Context { + return &Context{service: service, db: database, accountService: accountService, validator: validator} } diff --git a/internal/expense/setup.go b/internal/expense/setup.go new file mode 100644 index 0000000..44a6460 --- /dev/null +++ b/internal/expense/setup.go @@ -0,0 +1,36 @@ +package expense + +import ( + "github.com/gin-gonic/gin" + "gopkg.in/go-playground/validator.v9" + "net/http" + "nos-comptes/internal/account" + "nos-comptes/internal/ginserver" + "nos-comptes/internal/storage/dao/postgresql" + "nos-comptes/internal/utils" + validatorInternal "nos-comptes/internal/utils/validator" +) + +const ServiceInjectorKey = "EXPENSE_SERVICE" + +func Setup(injector *utils.Injector) { + pg := utils.Get[*postgresql.DatabasePostgreSQL](injector, postgresql.DatabaseKey) + validate := utils.Get[*validator.Validate](injector, validatorInternal.ValidatorInjectorKey) + accountService := utils.Get[*account.Service](injector, account.ServiceInjectorKey) + database := NewDatabase(pg) + service := NewService(database) + handler := NewHandler(validate, database, service, accountService) + securedRoute := utils.Get[*gin.RouterGroup](injector, ginserver.SecuredRouterInjectorKey) + securedUserRoute := securedRoute.Group("/:userId") + securedValidAccount := securedUserRoute.Group("/accounts/:accountId") + + injector.Set(ServiceInjectorKey, service) + + securedValidAccount.Handle(http.MethodPost, "/expenses", handler.CreateAnExpense) + securedValidAccount.Handle(http.MethodGet, "/expenses", handler.GetAllExpenses) + + securedExistingExpenses := securedValidAccount.Group("/expenses/:expenseId") + securedExistingExpenses.Handle(http.MethodGet, "", handler.GetAnExpenses) + securedExistingExpenses.Handle(http.MethodDelete, "", handler.DeleteExpense) + +} diff --git a/ginserver/logger.go b/internal/ginserver/logger.go similarity index 100% rename from ginserver/logger.go rename to internal/ginserver/logger.go diff --git a/middleware/oauth_token.go b/internal/ginserver/oauth_token.go similarity index 94% rename from middleware/oauth_token.go rename to internal/ginserver/oauth_token.go index d22a1dc..92df5ba 100644 --- a/middleware/oauth_token.go +++ b/internal/ginserver/oauth_token.go @@ -1,4 +1,4 @@ -package middleware +package ginserver import ( "fmt" @@ -16,6 +16,7 @@ func ValidateOAuthToken(c *gin.Context) { authorizationHeaderSplitted := strings.Split(authorizationHeader, " ") if len(authorizationHeaderSplitted) != 2 { utils.JSONError(c.Writer, model.ErrBadRequestFormat) + c.Abort() return } @@ -23,6 +24,7 @@ func ValidateOAuthToken(c *gin.Context) { if oauth2Service == nil { fmt.Println(err) utils.JSONError(c.Writer, model.ErrInternalServer) + c.Abort() return } tokenInfoCall := oauth2Service.Tokeninfo() @@ -31,6 +33,7 @@ func ValidateOAuthToken(c *gin.Context) { if err != nil { utils.GetLogger().WithError(err).Error(err) utils.JSONError(c.Writer, model.ErrBadRequestFormat) + c.Abort() return } c.Set("googleUserId", token.UserId) diff --git a/internal/ginserver/setup.go b/internal/ginserver/setup.go new file mode 100644 index 0000000..53420d3 --- /dev/null +++ b/internal/ginserver/setup.go @@ -0,0 +1,47 @@ +package ginserver + +import ( + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + "net/http" + "nos-comptes/handler" + "nos-comptes/internal/utils" + "time" +) + +var ( + routerInjectorKey = "ROUTER" + + SecuredRouterInjectorKey = "SECURED_ROUTER" + UnsecuredRouterInjectorKey = "UNSECURED_ROUTER" +) + +func Setup(injector *utils.Injector, config *handler.Config) { + + gin.SetMode(gin.ReleaseMode) + + router := gin.New() + router.HandleMethodNotAllowed = true + + router.Use(cors.New(cors.Config{ + AllowOrigins: []string{"http://localhost:8080/", "http://localhost:8080"}, + AllowMethods: []string{"*"}, + AllowHeaders: []string{"*"}, + ExposeHeaders: []string{"*"}, + AllowCredentials: true, + MaxAge: 12 * time.Hour, + })) + router.Use(gin.Recovery()) + router.Use(GetLoggerMiddleware()) + router.Use(GetHTTPLoggerMiddleware()) + public := router.Group("/") + public.Handle(http.MethodGet, "/_health", handler.GetHealth) + + injector.Set(UnsecuredRouterInjectorKey, public) + securedUserRoute := public.Group("/users") + securedUserRoute.Use(ValidateOAuthToken) + + injector.Set(SecuredRouterInjectorKey, securedUserRoute) + injector.Set(routerInjectorKey, router) + +} diff --git a/internal/ginserver/start.go b/internal/ginserver/start.go new file mode 100644 index 0000000..42d09e9 --- /dev/null +++ b/internal/ginserver/start.go @@ -0,0 +1,38 @@ +package ginserver + +import ( + "github.com/gin-gonic/gin" + "log" + "net/http" + "nos-comptes/internal/utils" + "os" + "os/signal" + "syscall" +) + +func Start(injector *utils.Injector) { + router := utils.Get[*gin.Engine](injector, routerInjectorKey) + + srv := &http.Server{ + Addr: ":8080", + Handler: router, + } + + go func() { + // service connections + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("listen: %s\n", err) + } + }() + + // Wait for interrupt signal to gracefully shutdown the server with + // a timeout of 5 seconds. + quit := make(chan os.Signal) + // kill (no param) default send syscanll.SIGTERM + // kill -2 is syscall.SIGINT + // kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + log.Println("Shutdown Server ...") + +} diff --git a/internal/jointaccount/database.go b/internal/jointaccount/database.go deleted file mode 100644 index 40edc74..0000000 --- a/internal/jointaccount/database.go +++ /dev/null @@ -1,116 +0,0 @@ -package jointaccount - -import ( - "fmt" - "nos-comptes/internal/storage/dao" - "nos-comptes/internal/storage/dao/postgresql" - "nos-comptes/internal/utils" - - "github.com/lib/pq" -) - -type Database struct { - *postgresql.DatabasePostgreSQL -} - -func (db *Database) GetAllJointaccountOfUser(id string) ([]*Jointaccount, error) { - q := ` - SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at - FROM public.jointaccount a - WHERE a.user_id = $1 - ` - rows, err := db.Session.Query(q, id) - if err != nil { - return nil, err - } - defer rows.Close() - - as := make([]*Jointaccount, 0) - for rows.Next() { - a := Jointaccount{} - err := rows.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt) - if err != nil { - return nil, err - } - as = append(as, &a) - } - return as, nil -} - -func (db *Database) GetJointaccountWithNameForUser(name string, id string) (*Jointaccount, error) { - q := ` - SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at - FROM public.jointaccount a - WHERE a.user_id = $1 - AND a.name = $2 - ` - row, err := db.Session.Query(q, id, name) - if !row.Next() { - return nil, dao.NewDAOError(dao.ErrTypeNotFound, fmt.Errorf("No row found")) - } - a := Jointaccount{} - row.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt) - if row.Next() { - return nil, fmt.Errorf("Impossibru") - } - if errPq, ok := err.(*pq.Error); ok { - return nil, postgresql.HandlePgError(errPq) - } - if err != nil { - utils.GetLogger().Info(err) - return nil, err - } - return &a, nil -} - -func (db *Database) CreateJointaccount(jointaccount *Jointaccount) error { - q := ` - INSERT INTO public.jointaccount - (Name, Provider, user_id) - VALUES - ($1, $2, $3) - RETURNING id, created_at - ` - - err := db.Session. - QueryRow(q, jointaccount.Name, jointaccount.Provider, jointaccount.UserId). - Scan(&jointaccount.ID, &jointaccount.CreatedAt) - if errPq, ok := err.(*pq.Error); ok { - return postgresql.HandlePgError(errPq) - } - return err - -} - -func (db *Database) DeleteJointaccountOfAnUser(userId, jointaccountId string) error { - query := ` - DELETE FROM jointaccount - WHERE user_id = $1 - AND id = $2;` - _, err := db.Session.Exec(query, userId, jointaccountId) - return err -} - -func (db *Database) GetASpecificJointaccountForUser(userId, jointaccountId string) (*Jointaccount, error) { - q := ` - SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at - FROM public.jointaccount a - WHERE a.user_id = $1 - AND a.id = $2 - ` - row := db.Session.QueryRow(q, userId, jointaccountId) - a := Jointaccount{} - 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) - } - if err != nil { - utils.GetLogger().Info(err) - return nil, err - } - return &a, nil -} - -func NewDatabase(db *postgresql.DatabasePostgreSQL) *Database { - return &Database{db} -} diff --git a/internal/jointaccount/handler.go b/internal/jointaccount/handler.go deleted file mode 100644 index 779ab50..0000000 --- a/internal/jointaccount/handler.go +++ /dev/null @@ -1,100 +0,0 @@ -package jointaccount - -import ( - "net/http" - "nos-comptes/handler" - "nos-comptes/internal/storage/dao/postgresql" - "nos-comptes/internal/storage/model" - "nos-comptes/internal/storage/validators" - "nos-comptes/internal/user" - "nos-comptes/internal/utils" - - "github.com/gin-gonic/gin" -) - -type Context struct { - service *Service - db *Database - userService *user.Service - *handler.Context -} - -func (c *Context) GetAllJointaccountOfUser(gc *gin.Context) { - userId := gc.Param("userId") - jointaccounts, err := c.service.GetAllJointaccountOfUser(userId) - if e, ok := err.(*model.APIError); ok { - utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetAllJointaccounts: get jointaccounts") - utils.JSONErrorWithMessage(gc.Writer, *e, e.Description) - } else if err != nil { - utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get jointaccounts") - utils.JSONError(gc.Writer, model.ErrInternalServer) - return - } - - if len(jointaccounts) == 0 { - utils.JSON(gc.Writer, http.StatusNoContent, nil) - } else { - utils.JSON(gc.Writer, http.StatusOK, jointaccounts) - } - -} - -func (c *Context) CreateJointaccountOfUser(gc *gin.Context) { - userId := gc.Param("userId") - - var jointaccount Jointaccount - var jointaccountEditable JointaccountEditable - if err := gc.BindJSON(&jointaccountEditable); err != nil { - utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) - return - } - jointaccount = Jointaccount{JointaccountEditable: jointaccountEditable, UserId: userId} - jointaccountFound, err := c.service.GetJointaccountWithNameForUser(jointaccount.Name, userId) - if e, ok := err.(*model.APIError); ok { - if e.Type != model.ErrNotFound.Type { - utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetJointaccount: get jointaccount error") - utils.JSONErrorWithMessage(gc.Writer, *e, e.Description) - return - } - } else if err != nil { - utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get jointaccount") - utils.JSONError(gc.Writer, model.ErrInternalServer) - return - } - - if jointaccountFound != nil { - utils.GetLoggerFromCtx(gc).WithError(&model.ErrAlreadyExists).WithField("type", model.ErrAlreadyExists.Type).Error("error CreateJointaccount: jointaccount already exists") - utils.JSONErrorWithMessage(gc.Writer, model.ErrAlreadyExists, "jointaccount already exists with the same Name") - return - } - jointaccount.UserId = userId - jointaccountSaved, err := c.service.CreateJointaccount(jointaccount) - if err != nil { - utils.GetLogger().Info(err) - utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) - return - } - utils.JSON(gc.Writer, http.StatusCreated, jointaccountSaved) - return -} - -func (c *Context) DeleteJointaccountOfUser(gc *gin.Context) { - userId := gc.Param("userId") - jointaccountId := gc.Param("jointaccountId") - c.service.DeleteJointaccountOfUser(userId, jointaccountId) - -} - -func (c *Context) GetSpecificJointaccountOfUser(gc *gin.Context) { - userId := gc.Param("userId") - jointaccountId := gc.Param("jointaccountId") - jointaccount, _ := c.service.GetASpecificJointaccountForUser(userId, jointaccountId) - utils.JSON(gc.Writer, http.StatusOK, jointaccount) -} - -func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { - database := NewDatabase(db) - service := NewService(database) - userService := user.NewService(user.NewDatabase(db)) - return &Context{service: service, db: database, userService: userService, Context: ctx} -} diff --git a/internal/jointaccount/model.go b/internal/jointaccount/model.go deleted file mode 100644 index abb2635..0000000 --- a/internal/jointaccount/model.go +++ /dev/null @@ -1,16 +0,0 @@ -package jointaccount - -import "time" - -type Jointaccount struct { - JointaccountEditable - UserId string `json:"userId,omitempty"` -} - -type JointaccountEditable struct { - 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/jointaccount/service.go b/internal/jointaccount/service.go deleted file mode 100644 index 5f95c41..0000000 --- a/internal/jointaccount/service.go +++ /dev/null @@ -1,62 +0,0 @@ -package jointaccount - -import ( - "nos-comptes/internal/storage/dao" - "nos-comptes/internal/storage/model" -) - -type Service struct { - db *Database -} - -func (s *Service) GetAllJointaccountOfUser(userId string) ([]*Jointaccount, error) { - jointaccounts, err := s.db.GetAllJointaccountOfUser(userId) - 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 - } - - if jointaccounts == nil { - return nil, &model.ErrNotFound - } - return jointaccounts, nil -} - -func (s *Service) GetJointaccountWithNameForUser(name string, id string) (*Jointaccount, error) { - jointaccount, err := s.db.GetJointaccountWithNameForUser(name, id) - 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 jointaccount, nil -} - -func (s *Service) CreateJointaccount(jointaccount Jointaccount) (*Jointaccount, error) { - err := s.db.CreateJointaccount(&jointaccount) - return &jointaccount, err -} - -func (s *Service) DeleteJointaccountOfUser(userId, jointaccountId string) error { - return s.db.DeleteJointaccountOfAnUser(userId, jointaccountId) -} - -func (s *Service) GetASpecificJointaccountForUser(userId, jointaccountId string) (*Jointaccount, error) { - return s.db.GetASpecificJointaccountForUser(userId, jointaccountId) - -} - -func NewService(database *Database) *Service { - return &Service{db: database} -} diff --git a/internal/jointexpense/database.go b/internal/jointexpense/database.go deleted file mode 100644 index ba6b9f5..0000000 --- a/internal/jointexpense/database.go +++ /dev/null @@ -1,86 +0,0 @@ -package jointexpense - -import ( - "nos-comptes/internal/storage/dao/postgresql" - "nos-comptes/internal/utils" - - "github.com/lib/pq" -) - -type Database struct { - *postgresql.DatabasePostgreSQL -} - -func (db *Database) CreateJointexpense(jointexpense *Jointexpense) error { - q := ` - INSERT INTO public.jointexpense - (jointaccount_id, value, type_jointexpense, jointexpense_date, libelle) - VALUES - ($1, $2, $3, $4, $5) - RETURNING id, created_at - ` - - err := db.Session. - QueryRow(q, jointexpense.JointaccountId, jointexpense.Value, jointexpense.TypeJointexpense, jointexpense.JointexpenseDate, jointexpense.Libelle). - Scan(&jointexpense.ID, &jointexpense.CreatedAt) - if err != nil { - utils.GetLogger().Info(err) - } - if errPq, ok := err.(*pq.Error); ok { - return postgresql.HandlePgError(errPq) - } - return err -} - -func (db Database) GetJointexpensesOfAnJointaccountBetween(id, from, to string) ([]*Jointexpense, error) { - q := ` - SELECT a.id, a.jointaccount_id, a.value, a.type_jointexpense, a.jointexpense_date, a.created_at, a.updated_at, a.libelle - FROM public.jointexpense a - WHERE a.jointaccount_id = $1 - AND a.jointexpense_date BETWEEN $2 and $3 - ` - rows, err := db.Session.Query(q, id, from, to) - if err != nil { - return nil, err - } - defer rows.Close() - - es := make([]*Jointexpense, 0) - for rows.Next() { - e := Jointexpense{} - err := rows.Scan(&e.ID, &e.JointjointaccountId, &e.Value, &e.TypeJointexpense, &e.JointexpenseDate, &e.CreatedAt, &e.UpdatedAt, &e.Libelle) - if err != nil { - return nil, err - } - es = append(es, &e) - } - return es, nil -} - -func (db Database) GetAllJointexpensesOfAnJointaccount(id string) ([]*Jointexpense, error) { - q := ` - SELECT a.id, a.jointaccount_id, a.value, a.type_jointexpense, a.jointexpense_date, a.created_at, a.updated_at, a.libelle - FROM public.jointexpense a - WHERE a.jointaccount_id = $1 - ` - rows, err := db.Session.Query(q, id) - if err != nil { - return nil, err - } - defer rows.Close() - - es := make([]*Jointexpense, 0) - for rows.Next() { - e := Jointexpense{} - err := rows.Scan(&e.ID, &e.JointaccountId, &e.Value, &e.TypeJointexpense, &e.JointexpenseDate, &e.CreatedAt, &e.UpdatedAt, &e.Libelle) - if err != nil { - return nil, err - } - es = append(es, &e) - } - return es, nil -} - -func NewDatabase(db *postgresql.DatabasePostgreSQL) *Database { - return &Database{db} -} diff --git a/internal/jointexpense/handler.go b/internal/jointexpense/handler.go deleted file mode 100644 index 65f5848..0000000 --- a/internal/jointexpense/handler.go +++ /dev/null @@ -1,132 +0,0 @@ -package jointexpense - -import ( - "encoding/csv" - "net/http" - "nos-comptes/handler" - "nos-comptes/internal/jointaccount" - "nos-comptes/internal/storage/dao/postgresql" - "nos-comptes/internal/storage/model" - "nos-comptes/internal/storage/validators" - "nos-comptes/internal/utils" - "time" - - "github.com/gin-gonic/gin" -) - -type Context struct { - service *Service - db *Database - jointaccountService *jointaccount.Service - *handler.Context -} - -func (c *Context) ImportJointexpenseFromCSV(gc *gin.Context) { -} -func (c *Context) CreateAnJointexpense(gc *gin.Context) { - jointaccountID := gc.Param("jointaccountId") - userId := gc.Param("userId") - csvHeaderFile, err := gc.FormFile("attachment") - if err != nil { - utils.GetLogger().Info(err) - utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) - return - } - if err == nil { - csvFile, err := csvHeaderFile.Open() - if err != nil { - utils.GetLogger().Info(err) - utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) - return - } - csvr := csv.NewReader(csvFile) - csvr.FieldsPerRecord = -1 - csvr.Comma = ';' - filedata, err := csvr.ReadAll() - - jointaccount, err := c.jointaccountService.GetASpecificJointaccountForUser(userId, jointaccountID) - if err != nil { - utils.GetLogger().Info(err) - utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) - return - } - err = c.service.ProcessCSVFile(filedata, jointaccount) - if err != nil { - utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) - return - } - return - } - var jointexpense Jointexpense - var jointexpenseEditable JointexpenseEditable - if err := gc.BindJSON(&jointexpenseEditable); err != nil { - utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) - return - } - jointexpense = Jointexpense{JointexpenseEditable: jointexpenseEditable, JointaccountId: jointaccountID} - err = c.service.CreateJointexpense(&jointexpense) - if err != nil { - utils.GetLogger().Info(err) - utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) - return - } - utils.JSON(gc.Writer, http.StatusCreated, jointexpense) -} - -func (c *Context) DeleteJointexpense(gc *gin.Context) { - -} - -func (c *Context) GetAllJointexpenses(gc *gin.Context) { - jointaccountId := gc.Param("jointaccountId") - from := gc.Query("from") - to := gc.Query("to") - var jointexpenses []*Jointexpense - var err error - if from != "" || to != "" { - if to == "" { - fromParsed, err := time.Parse("2006-01-02", from) - if err == nil { - to = time.Now().Format("2006-01-02") - } else { - to = fromParsed.AddDate(0, 1, 0).Format("2006-01-02") - } - } - if from == "" { - toParsed, err := time.Parse("2006-01-02", to) - if err == nil { - from = "1900-01-01" - } else { - from = toParsed.AddDate(0, -1, 0).Format("2006-01-02") - } - } - jointexpenses, err = c.service.GetJointexpensesOfAnJointaccountBetween(jointaccountId, from, to) - } else { - jointexpenses, err = c.service.GetAllJointexpensesOfAnJointaccount(jointaccountId) - } - if e, ok := err.(*model.APIError); ok { - utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetAllJointexpenses: get jointexpenses") - utils.JSONErrorWithMessage(gc.Writer, *e, e.Description) - } else if err != nil { - utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get jointexpenses") - utils.JSONError(gc.Writer, model.ErrInternalServer) - return - } - - if len(jointexpenses) == 0 { - utils.JSON(gc.Writer, http.StatusNoContent, nil) - } else { - utils.JSON(gc.Writer, http.StatusOK, jointexpenses) - } -} - -func (c *Context) GetAnJointexpenses(context *gin.Context) { - -} - -func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { - database := NewDatabase(db) - service := NewService(database) - jointaccountService := jointaccount.NewService(jointaccount.NewDatabase(db)) - return &Context{service: service, db: database, jointaccountService: jointaccountService, Context: ctx} -} diff --git a/internal/jointexpense/model.go b/internal/jointexpense/model.go deleted file mode 100644 index beb1845..0000000 --- a/internal/jointexpense/model.go +++ /dev/null @@ -1,18 +0,0 @@ -package jointexpense - -import "time" - -type Jointexpense struct { - JointexpenseEditable - JointaccountId string `json:"jointaccountId,omitempty"` -} - -type JointexpenseEditable struct { - ID string `json:"id,omitempty"` - Value float32 `json:"value"` - Libelle string `json:"libelle"` - TypeJointexpense string `json:"typeJointexpense"` - JointexpenseDate time.Time `json:"jointexpenseDate,omitempty"` - CreatedAt *time.Time `json:"createdAt,omitempty"` - UpdatedAt *time.Time `json:"updatedAt,omitempty"` -} diff --git a/internal/jointexpense/service.go b/internal/jointexpense/service.go deleted file mode 100644 index 4559e14..0000000 --- a/internal/jointexpense/service.go +++ /dev/null @@ -1,120 +0,0 @@ -package jointexpense - -import ( - "nos-comptes/internal/jointaccount" - "nos-comptes/internal/storage/dao" - "nos-comptes/internal/storage/model" - "nos-comptes/internal/utils" - "strconv" - "strings" - "time" -) - -type Service struct { - db *Database -} - -func (s Service) GetJointexpensesOfAnJointaccountBetween(jointaccountId, from, to string) ([]*Jointexpense, error) { - jointexpenses, err := s.db.GetJointexpensesOfAnJointaccountBetween(jointaccountId, from, to) - utils.GetLogger().Info(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 - } - - if jointexpenses == nil { - return nil, &model.ErrNotFound - } - return jointexpenses, nil -} - -func (s Service) GetAllJointexpensesOfAnJointaccount(jointaccountId string) ([]*Jointexpense, error) { - jointexpenses, err := s.db.GetAllJointexpensesOfAnJointaccount(jointaccountId) - utils.GetLogger().Info(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 - } - - if jointexpenses == nil { - return nil, &model.ErrNotFound - } - return jointexpenses, nil -} - -func (s Service) CreateJointexpense(jointexpense *Jointexpense) error { - - return s.db.CreateJointexpense(jointexpense) -} - -func (s Service) ProcessCSVFile(filedata [][]string, jointaccount *jointaccount.Jointaccount) error { - switch jointaccount.Provider { - case "caisse-epargne": - return s.processCaisseEpargne(filedata, jointaccount) - case "boursorama": - return s.processBoursorama(filedata, jointaccount) - case "bnp": - return s.processBnp(filedata, jointaccount) - default: - return nil - } - -} - -func (s Service) processCaisseEpargne(filedata [][]string, jointaccount *jointaccount.Jointaccount) error { - for _, val := range filedata[4:] { - jointexpenseDate, err := time.Parse("02/01/06", val[0]) - if err != nil { - utils.GetLogger().Info(err) - continue - } - amount := val[3] - typeJointexpense := "D" - if amount == "" { - amount = val[4] - typeJointexpense = "C" - } - amountParsed, err := strconv.ParseFloat(strings.Trim(strings.ReplaceAll(amount, ",", "."), "+"), 32) - if err != nil { - utils.GetLogger().Info(err) - continue - } - jointexpense := &Jointexpense{ - JointexpenseEditable: JointexpenseEditable{ - Value: float32(amountParsed), - Libelle: val[2], - TypeJointexpense: typeJointexpense, - JointexpenseDate: jointexpenseDate, - }, - JointaccountId: jointaccount.ID, - } - s.CreateJointexpense(jointexpense) - utils.GetLogger().Info(val) - } - - return nil -} - -func (s Service) processBoursorama(filedata [][]string, jointaccount *jointaccount.Jointaccount) error { - return nil -} - -func (s Service) processBnp(filedata [][]string, jointaccount *jointaccount.Jointaccount) error { - return nil -} - -func NewService(database *Database) *Service { - return &Service{db: database} -} diff --git a/internal/shared-account/database.go b/internal/shared-account/database.go deleted file mode 100644 index 63f6c80..0000000 --- a/internal/shared-account/database.go +++ /dev/null @@ -1,11 +0,0 @@ -package sharedaccount - -import "nos-comptes/internal/storage/dao/postgresql" - -type Database struct { - *postgresql.DatabasePostgreSQL -} - -func NewDatabase(db *postgresql.DatabasePostgreSQL) *Database { - return &Database{db} -} diff --git a/internal/shared-account/handler.go b/internal/shared-account/handler.go deleted file mode 100644 index 367671b..0000000 --- a/internal/shared-account/handler.go +++ /dev/null @@ -1,37 +0,0 @@ -package sharedaccount - -import ( - "nos-comptes/handler" - "nos-comptes/internal/storage/dao/postgresql" - - "github.com/gin-gonic/gin" -) - -type Context struct { - service *Service - db *Database - *handler.Context -} - -func (c *Context) ShareAnAccount(context *gin.Context) { - -} - -func (c *Context) DeleteSharedAccount(context *gin.Context) { - -} - -func (c *Context) GetAllSharedAccountOfUser(context *gin.Context) { - -} - -func (c *Context) GetSpecificSharedAccountOfUser(context *gin.Context) { - -} - -func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { - database := NewDatabase(db) - service := NewService(database) - return &Context{service: service, db: database, Context: ctx} - -} diff --git a/internal/shared-account/model.go b/internal/shared-account/model.go deleted file mode 100644 index 4e7ce03..0000000 --- a/internal/shared-account/model.go +++ /dev/null @@ -1,4 +0,0 @@ -package sharedaccount - -type SharedAccount struct { -} diff --git a/internal/shared-account/service.go b/internal/shared-account/service.go deleted file mode 100644 index 3b85d54..0000000 --- a/internal/shared-account/service.go +++ /dev/null @@ -1,9 +0,0 @@ -package sharedaccount - -type Service struct { - Db *Database -} - -func NewService(database *Database) *Service { - return &Service{Db: database} -} diff --git a/internal/storage/.DS_Store b/internal/storage/.DS_Store new file mode 100644 index 0000000..7592a5a Binary files /dev/null and b/internal/storage/.DS_Store differ diff --git a/internal/storage/dao/postgresql/setup.go b/internal/storage/dao/postgresql/setup.go new file mode 100644 index 0000000..b4ce9ac --- /dev/null +++ b/internal/storage/dao/postgresql/setup.go @@ -0,0 +1,13 @@ +package postgresql + +import ( + "nos-comptes/internal/utils" +) + +var DatabaseKey = "POSTGRES" + +func Setup(injector *utils.Injector, connectionUri string) { + database := NewDatabasePostgreSQL(connectionUri) + + injector.Set(DatabaseKey, database) +} diff --git a/internal/user/handler.go b/internal/user/handler.go index b0e3105..c4fa896 100755 --- a/internal/user/handler.go +++ b/internal/user/handler.go @@ -2,8 +2,6 @@ package user import ( "net/http" - "nos-comptes/handler" - "nos-comptes/internal/storage/dao/postgresql" "nos-comptes/internal/storage/model" "nos-comptes/internal/utils" "strings" @@ -13,15 +11,9 @@ import ( ) type Context struct { - service *Service - db *Database - *handler.Context -} - -func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { - database := NewDatabase(db) - service := NewService(database) - return &Context{service: service, db: database, Context: ctx} + service *Service + db *Database + validator *Validator } func (uc *Context) GetAllUsers(c *gin.Context) { @@ -130,3 +122,7 @@ func (hc *Context) GetUser(c *gin.Context) { user, _ := hc.service.GetUserById(userID) utils.JSON(c.Writer, http.StatusOK, user) } + +func NewHandler(validator *Validator, database *Database, service *Service) *Context { + return &Context{service: service, db: database, validator: validator} +} diff --git a/internal/user/setup.go b/internal/user/setup.go new file mode 100644 index 0000000..680c47b --- /dev/null +++ b/internal/user/setup.go @@ -0,0 +1,30 @@ +package user + +import ( + "github.com/gin-gonic/gin" + "net/http" + "nos-comptes/internal/ginserver" + "nos-comptes/internal/storage/dao/postgresql" + "nos-comptes/internal/utils" +) + +const ServiceInjectorKey = "USER_SERVICE" + +func Setup(injector *utils.Injector) { + pg := utils.Get[*postgresql.DatabasePostgreSQL](injector, postgresql.DatabaseKey) + database := NewDatabase(pg) + service := NewService(database) + validator := NewValidator(service) + handler := NewHandler(validator, database, service) + securedRoute := utils.Get[*gin.RouterGroup](injector, ginserver.SecuredRouterInjectorKey) + //TODO add secure auth + securedRoute.Handle(http.MethodGet, "/:userId", handler.GetUser) + + securedUserRoute := securedRoute.Group("/:userId") + securedUserRoute.Use(validator.HasValidUserId) + securedUserRoute.Use(validator.UserdIdMatchOAuthToken) + injector.Set(ServiceInjectorKey, service) +} + +func SetupRoute(injector *utils.Injector) { +} diff --git a/middleware/validator.go b/internal/user/validator.go similarity index 50% rename from middleware/validator.go rename to internal/user/validator.go index e8486ba..cbe6afb 100644 --- a/middleware/validator.go +++ b/internal/user/validator.go @@ -1,27 +1,22 @@ -package middleware +package user import ( - "nos-comptes/handler" - "nos-comptes/internal/account" - "nos-comptes/internal/jointaccount" - "nos-comptes/internal/storage/dao/postgresql" + "gopkg.in/go-playground/validator.v9" "nos-comptes/internal/storage/model" "nos-comptes/internal/storage/validators" - "nos-comptes/internal/user" + "nos-comptes/internal/utils" "github.com/gin-gonic/gin" ) type Validator struct { - jointaccountService *jointaccount.Service - accountService *account.Service - userService *user.Service - *handler.Context + userService *Service + Validator validator.Validate } -func NewValidator(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Validator { - return &Validator{accountService: account.NewService(account.NewDatabase(db)), userService: user.NewService(user.NewDatabase(db)), jointaccountService: jointaccount.NewService(jointaccount.NewDatabase(db)), Context: ctx} +func NewValidator(service *Service) *Validator { + return &Validator{userService: service} } func (v Validator) HasValidUserId(gc *gin.Context) { userId := gc.Param("userId") @@ -85,53 +80,3 @@ func (v Validator) UserdIdMatchOAuthToken(gc *gin.Context) { return } } - -func (v Validator) HasValidAccountId(gc *gin.Context) { - accountId := gc.Param("accountId") - err := v.Validator.VarCtx(gc, accountId, "uuid4") - if err != nil { - utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) - return - } -} - -func (v Validator) AccountExists(gc *gin.Context) { - userId := gc.Param("userId") - accountId := gc.Param("accountId") - _, err := v.accountService.GetASpecificAccountForUser(userId, accountId) - if e, ok := err.(*model.APIError); ok { - utils.GetLogger().Info(err) - utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id") - utils.JSONErrorWithMessage(gc.Writer, *e, e.Description) - return - } else if err != nil { - utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id") - utils.JSONError(gc.Writer, model.ErrInternalServer) - return - } -} - -func (v Validator) HasValidJointAccountId(gc *gin.Context) { - accountId := gc.Param("jointaccountId") - err := v.Validator.VarCtx(gc, accountId, "uuid4") - if err != nil { - utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) - return - } -} - -func (v Validator) JointAccountExists(gc *gin.Context) { - userId := gc.Param("userId") - accountId := gc.Param("jointaccountId") - _, err := v.jointaccountService.GetASpecificJointaccountForUser(userId, accountId) - if e, ok := err.(*model.APIError); ok { - utils.GetLogger().Info(err) - utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id") - utils.JSONErrorWithMessage(gc.Writer, *e, e.Description) - return - } else if err != nil { - utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id") - utils.JSONError(gc.Writer, model.ErrInternalServer) - return - } -} diff --git a/internal/utils/injector.go b/internal/utils/injector.go new file mode 100644 index 0000000..c337622 --- /dev/null +++ b/internal/utils/injector.go @@ -0,0 +1,29 @@ +package utils + +import "fmt" + +type Injector struct { + content map[string]any +} + +func (i *Injector) Get(key string) any { + val, ok := i.content[key] + if !ok { + panic(fmt.Sprintf("Can't get key %s from injector", key)) + } + return val +} +func Get[T any](i *Injector, key string) T { + return i.Get(key).(T) +} + +func (i *Injector) Set(key string, content any) { + if i.content == nil { + i.content = map[string]any{} + } + _, ok := i.content[key] + if ok { + panic(fmt.Sprintf("Key %s already have content", key)) + } + i.content[key] = content +} diff --git a/internal/utils/validator/setup.go b/internal/utils/validator/setup.go new file mode 100644 index 0000000..87ca6cc --- /dev/null +++ b/internal/utils/validator/setup.go @@ -0,0 +1,12 @@ +package validator + +import ( + "nos-comptes/internal/utils" +) + +const ValidatorInjectorKey = "VALIDATOR" + +func Setup(injector *utils.Injector) { + + injector.Set(ValidatorInjectorKey, newValidator()) +} diff --git a/internal/utils/validator/validator.go b/internal/utils/validator/validator.go new file mode 100644 index 0000000..893c445 --- /dev/null +++ b/internal/utils/validator/validator.go @@ -0,0 +1,28 @@ +package validator + +import ( + "gopkg.in/go-playground/validator.v9" + "nos-comptes/internal/storage/validators" + "reflect" + "strings" +) + +func newValidator() *validator.Validate { + va := validator.New() + + va.RegisterTagNameFunc(func(fld reflect.StructField) string { + name := strings.SplitN(fld.Tag.Get("json"), ",", 2) + if len(name) < 1 { + return "" + } + return name[0] + }) + + for k, v := range validators.CustomValidators { + if v.Validator != nil { + va.RegisterValidationCtx(k, v.Validator) + } + } + + return va +}