big refacto

This commit is contained in:
2023-10-03 00:40:01 +02:00
parent aa722718f7
commit 932f423faf
35 changed files with 354 additions and 934 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -2,9 +2,14 @@ package cmd
import ( import (
"fmt" "fmt"
"nos-comptes/ginserver"
"nos-comptes/handler" "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" "nos-comptes/internal/utils"
validatorInternal "nos-comptes/internal/utils/validator"
"os" "os"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@@ -20,7 +25,6 @@ var (
const ( const (
parameterConfigurationFile = "config" parameterConfigurationFile = "config"
parameterLogLevel = "loglevel" parameterLogLevel = "loglevel"
parameterMock = "mock"
parameterLogFormat = "logformat" parameterLogFormat = "logformat"
parameterDBConnectionURI = "dbconnectionuri" parameterDBConnectionURI = "dbconnectionuri"
parameterPort = "port" parameterPort = "port"
@@ -40,15 +44,19 @@ var rootCmd = &cobra.Command{
utils.InitLogger(config.LogLevel, config.LogFormat) utils.InitLogger(config.LogLevel, config.LogFormat)
logrus. logrus.
WithField(parameterConfigurationFile, cfgFile). WithField(parameterConfigurationFile, cfgFile).
WithField(parameterMock, config.Mock).
WithField(parameterLogLevel, config.LogLevel). WithField(parameterLogLevel, config.LogLevel).
WithField(parameterLogFormat, config.LogFormat). WithField(parameterLogFormat, config.LogFormat).
WithField(parameterPort, config.Port). WithField(parameterPort, config.Port).
WithField(parameterDBConnectionURI, config.DBConnectionURI). WithField(parameterDBConnectionURI, config.DBConnectionURI).
Warn("Configuration") Warn("Configuration")
injector := &utils.Injector{}
router := ginserver.NewRouter(config) ginserver.Setup(injector, config)
router.Run(fmt.Sprintf(":%d", config.Port)) 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") rootCmd.Flags().Int(parameterPort, defaultPort, "Use this flag to set the listening port of the api")
viper.BindPFlag(parameterPort, rootCmd.Flags().Lookup(parameterPort)) 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. // initConfig reads in config file and ENV variables if set.
@@ -96,7 +102,6 @@ func initConfig() {
config.LogLevel = viper.GetString(parameterLogLevel) config.LogLevel = viper.GetString(parameterLogLevel)
config.LogFormat = viper.GetString(parameterLogFormat) config.LogFormat = viper.GetString(parameterLogFormat)
config.Mock = viper.GetBool(parameterMock)
config.DBConnectionURI = viper.GetString(parameterDBConnectionURI) config.DBConnectionURI = viper.GetString(parameterDBConnectionURI)
config.Port = viper.GetInt(parameterPort) config.Port = viper.GetInt(parameterPort)
} }

View File

@@ -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
}

2
go.mod
View File

@@ -1,6 +1,6 @@
module nos-comptes module nos-comptes
go 1.17 go 1.18
require ( require (
github.com/gin-contrib/cors v1.3.1 github.com/gin-contrib/cors v1.3.1

View File

@@ -1,51 +1,18 @@
package handler package handler
import ( import (
"net/http"
"nos-comptes/internal/storage/validators"
"nos-comptes/internal/utils"
"reflect"
"strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gopkg.in/go-playground/validator.v9" "net/http"
"nos-comptes/internal/utils"
) )
type Config struct { type Config struct {
Mock bool
DBConnectionURI string DBConnectionURI string
Port int Port int
LogLevel string LogLevel string
LogFormat string LogFormat string
} }
type Context struct { func GetHealth(c *gin.Context) {
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) {
utils.JSON(c.Writer, http.StatusNoContent, nil) utils.JSON(c.Writer, http.StatusNoContent, nil)
} }

BIN
internal/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -2,8 +2,6 @@ package account
import ( import (
"net/http" "net/http"
"nos-comptes/handler"
"nos-comptes/internal/storage/dao/postgresql"
"nos-comptes/internal/storage/model" "nos-comptes/internal/storage/model"
"nos-comptes/internal/storage/validators" "nos-comptes/internal/storage/validators"
"nos-comptes/internal/user" "nos-comptes/internal/user"
@@ -16,7 +14,7 @@ type Context struct {
service *Service service *Service
db *Database db *Database
userService *user.Service userService *user.Service
*handler.Context validator *Validator
} }
func (c *Context) GetAllAccountOfUser(gc *gin.Context) { 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) utils.JSON(gc.Writer, http.StatusOK, account)
} }
func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { func NewHandler(validator *Validator, database *Database, service *Service, userService *user.Service) *Context {
database := NewDatabase(db) return &Context{service: service, db: database, userService: userService, validator: validator}
service := NewService(database)
userService := user.NewService(user.NewDatabase(db))
return &Context{service: service, db: database, userService: userService, Context: ctx}
} }

34
internal/account/setup.go Normal file
View File

@@ -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)
}

View File

@@ -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
}
}

View File

@@ -2,10 +2,9 @@ package expense
import ( import (
"encoding/csv" "encoding/csv"
"gopkg.in/go-playground/validator.v9"
"net/http" "net/http"
"nos-comptes/handler"
"nos-comptes/internal/account" "nos-comptes/internal/account"
"nos-comptes/internal/storage/dao/postgresql"
"nos-comptes/internal/storage/model" "nos-comptes/internal/storage/model"
"nos-comptes/internal/storage/validators" "nos-comptes/internal/storage/validators"
"nos-comptes/internal/utils" "nos-comptes/internal/utils"
@@ -18,7 +17,7 @@ type Context struct {
service *Service service *Service
db *Database db *Database
accountService *account.Service accountService *account.Service
*handler.Context validator *validator.Validate
} }
func (c *Context) ImportExpenseFromCSV(gc *gin.Context) { 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 { func NewHandler(validator *validator.Validate, database *Database, service *Service, accountService *account.Service) *Context {
database := NewDatabase(db) return &Context{service: service, db: database, accountService: accountService, validator: validator}
service := NewService(database)
accountService := account.NewService(account.NewDatabase(db))
return &Context{service: service, db: database, accountService: accountService, Context: ctx}
} }

36
internal/expense/setup.go Normal file
View File

@@ -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)
}

View File

@@ -1,4 +1,4 @@
package middleware package ginserver
import ( import (
"fmt" "fmt"
@@ -16,6 +16,7 @@ func ValidateOAuthToken(c *gin.Context) {
authorizationHeaderSplitted := strings.Split(authorizationHeader, " ") authorizationHeaderSplitted := strings.Split(authorizationHeader, " ")
if len(authorizationHeaderSplitted) != 2 { if len(authorizationHeaderSplitted) != 2 {
utils.JSONError(c.Writer, model.ErrBadRequestFormat) utils.JSONError(c.Writer, model.ErrBadRequestFormat)
c.Abort()
return return
} }
@@ -23,6 +24,7 @@ func ValidateOAuthToken(c *gin.Context) {
if oauth2Service == nil { if oauth2Service == nil {
fmt.Println(err) fmt.Println(err)
utils.JSONError(c.Writer, model.ErrInternalServer) utils.JSONError(c.Writer, model.ErrInternalServer)
c.Abort()
return return
} }
tokenInfoCall := oauth2Service.Tokeninfo() tokenInfoCall := oauth2Service.Tokeninfo()
@@ -31,6 +33,7 @@ func ValidateOAuthToken(c *gin.Context) {
if err != nil { if err != nil {
utils.GetLogger().WithError(err).Error(err) utils.GetLogger().WithError(err).Error(err)
utils.JSONError(c.Writer, model.ErrBadRequestFormat) utils.JSONError(c.Writer, model.ErrBadRequestFormat)
c.Abort()
return return
} }
c.Set("googleUserId", token.UserId) c.Set("googleUserId", token.UserId)

View File

@@ -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)
}

View File

@@ -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 ...")
}

View File

@@ -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}
}

View File

@@ -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}
}

View File

@@ -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"`
}

View File

@@ -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}
}

View File

@@ -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}
}

View File

@@ -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}
}

View File

@@ -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"`
}

View File

@@ -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}
}

View File

@@ -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}
}

View File

@@ -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}
}

View File

@@ -1,4 +0,0 @@
package sharedaccount
type SharedAccount struct {
}

View File

@@ -1,9 +0,0 @@
package sharedaccount
type Service struct {
Db *Database
}
func NewService(database *Database) *Service {
return &Service{Db: database}
}

BIN
internal/storage/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -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)
}

View File

@@ -2,8 +2,6 @@ package user
import ( import (
"net/http" "net/http"
"nos-comptes/handler"
"nos-comptes/internal/storage/dao/postgresql"
"nos-comptes/internal/storage/model" "nos-comptes/internal/storage/model"
"nos-comptes/internal/utils" "nos-comptes/internal/utils"
"strings" "strings"
@@ -15,13 +13,7 @@ import (
type Context struct { type Context struct {
service *Service service *Service
db *Database db *Database
*handler.Context validator *Validator
}
func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context {
database := NewDatabase(db)
service := NewService(database)
return &Context{service: service, db: database, Context: ctx}
} }
func (uc *Context) GetAllUsers(c *gin.Context) { func (uc *Context) GetAllUsers(c *gin.Context) {
@@ -130,3 +122,7 @@ func (hc *Context) GetUser(c *gin.Context) {
user, _ := hc.service.GetUserById(userID) user, _ := hc.service.GetUserById(userID)
utils.JSON(c.Writer, http.StatusOK, user) utils.JSON(c.Writer, http.StatusOK, user)
} }
func NewHandler(validator *Validator, database *Database, service *Service) *Context {
return &Context{service: service, db: database, validator: validator}
}

30
internal/user/setup.go Normal file
View File

@@ -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) {
}

View File

@@ -1,27 +1,22 @@
package middleware package user
import ( import (
"nos-comptes/handler" "gopkg.in/go-playground/validator.v9"
"nos-comptes/internal/account"
"nos-comptes/internal/jointaccount"
"nos-comptes/internal/storage/dao/postgresql"
"nos-comptes/internal/storage/model" "nos-comptes/internal/storage/model"
"nos-comptes/internal/storage/validators" "nos-comptes/internal/storage/validators"
"nos-comptes/internal/user"
"nos-comptes/internal/utils" "nos-comptes/internal/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type Validator struct { type Validator struct {
jointaccountService *jointaccount.Service userService *Service
accountService *account.Service Validator validator.Validate
userService *user.Service
*handler.Context
} }
func NewValidator(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Validator { func NewValidator(service *Service) *Validator {
return &Validator{accountService: account.NewService(account.NewDatabase(db)), userService: user.NewService(user.NewDatabase(db)), jointaccountService: jointaccount.NewService(jointaccount.NewDatabase(db)), Context: ctx} return &Validator{userService: service}
} }
func (v Validator) HasValidUserId(gc *gin.Context) { func (v Validator) HasValidUserId(gc *gin.Context) {
userId := gc.Param("userId") userId := gc.Param("userId")
@@ -85,53 +80,3 @@ func (v Validator) UserdIdMatchOAuthToken(gc *gin.Context) {
return 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
}
}

View File

@@ -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
}

View File

@@ -0,0 +1,12 @@
package validator
import (
"nos-comptes/internal/utils"
)
const ValidatorInjectorKey = "VALIDATOR"
func Setup(injector *utils.Injector) {
injector.Set(ValidatorInjectorKey, newValidator())
}

View File

@@ -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
}