diff --git a/.gitignore b/.gitignore index 33ab022..f7ddef1 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ _testmain.go *.prof .Dockerfile + +logs.txt +querys.txt \ No newline at end of file diff --git a/go.mod b/go.mod index c8e6d8f..ce5a720 100644 --- a/go.mod +++ b/go.mod @@ -1,21 +1,40 @@ module wallet-api -// +heroku goVersion go1.15 -go 1.15 +// +heroku goVersion go1.19 +go 1.19 require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gin-gonic/gin v1.7.7 github.com/go-pg/pg/v10 v10.10.6 - github.com/go-playground/validator/v10 v10.10.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect github.com/google/uuid v1.3.0 github.com/joho/godotenv v1.4.0 - github.com/json-iterator/go v1.1.12 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/ugorji/go v1.2.6 // indirect + go.uber.org/dig v1.15.0 golang.org/x/crypto v0.0.0-20220214200702-86341886e292 +) + +require ( + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-pg/zerochecker v0.2.0 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.10.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect + github.com/ugorji/go/codec v1.2.6 // indirect + github.com/vmihailenco/bufpool v0.1.11 // indirect + github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect + github.com/vmihailenco/tagparser v0.1.2 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect golang.org/x/text v0.3.7 // indirect + google.golang.org/protobuf v1.26.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + mellium.im/sasl v0.2.1 // indirect ) diff --git a/go.sum b/go.sum index 6831591..d59347c 100644 --- a/go.sum +++ b/go.sum @@ -108,12 +108,11 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= @@ -126,6 +125,8 @@ github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vb github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +go.uber.org/dig v1.15.0 h1:vq3YWr8zRj1eFGC7Gvf907hE0eRjPTZ1d3xHadD6liE= +go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM= golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -147,7 +148,6 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -164,7 +164,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -220,8 +219,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w= diff --git a/pkg/api/routes.go b/pkg/api/routes.go index 91b3452..8307cd2 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -1,60 +1,50 @@ package api import ( - "wallet-api/pkg/controllers" + "wallet-api/pkg/controller" "wallet-api/pkg/middleware" - "wallet-api/pkg/services" + "wallet-api/pkg/utl/common" "wallet-api/pkg/utl/configs" "github.com/gin-gonic/gin" "github.com/go-pg/pg/v10" + "go.uber.org/dig" ) /* Routes Initializes web api controllers and its corresponding routes. + Args: *gin.Engine: Gin Engine *pg.DB: Postgres database client */ func Routes(s *gin.Engine, db *pg.DB) { + c := dig.New() ver := s.Group(configs.Prefix) - api := ver.Group("api") - auth := ver.Group("auth") - wallet := ver.Group("wallet", middleware.Auth) - walletHeader := ver.Group("wallet/wallet-header", middleware.Auth) - transaction := ver.Group("transaction", middleware.Auth) - transactionType := ver.Group("transaction-type", middleware.Auth) - subscription := ver.Group("subscription", middleware.Auth) - subscriptionType := ver.Group("subscription-type", middleware.Auth) - transactionStatus := ver.Group("transaction-status", middleware.Auth) - + routeGroups := &common.RouteGroups{ + Api: ver.Group("api"), + Auth: ver.Group("auth"), + Wallet: ver.Group("wallet", middleware.Auth), + WalletHeader: ver.Group("wallet/wallet-header", middleware.Auth), + Transaction: ver.Group("transaction", middleware.Auth), + TransactionType: ver.Group("transaction-type", middleware.Auth), + Subscription: ver.Group("subscription", middleware.Auth), + SubscriptionType: ver.Group("subscription-type", middleware.Auth), + TransactionStatus: ver.Group("transaction-status", middleware.Auth), + } + s.NoRoute(func(c *gin.Context) { c.JSON(404, gin.H{"code": "PAGE_NOT_FOUND", "message": "Page not found"}) }) - - apiService := services.ApiService{Db: db} - usersService := services.UsersService{Db: db} - walletService := services.WalletService{Db: db} - transactionService := services.TransactionService{Db: db} - transactionTypeService := services.TransactionTypeService{Db: db} - subscriptionService := services.SubscriptionService{Db: db} - subscriptionTypeService := services.SubscriptionTypeService{Db: db} - transactionStatusService := services.TransactionStatusService{Db: db} - - walletService.Ss = &subscriptionService - transactionService.Ss = &subscriptionService - - controllers.NewApiController(&apiService, api) - controllers.NewAuthController(&usersService, auth) - controllers.NewWalletsController(&walletService, wallet) - controllers.NewWalletsHeaderController(&walletService, walletHeader) - controllers.NewTransactionController(&transactionService, transaction) - controllers.NewTransactionTypeController(&transactionTypeService, transactionType) - controllers.NewSubscriptionController(&subscriptionService, subscription) - controllers.NewSubscriptionTypeController(&subscriptionTypeService, subscriptionType) - controllers.NewTransactionStatusController(&transactionStatusService, transactionStatus) + c.Provide(func() *common.RouteGroups { + return routeGroups + }) + c.Provide(func() *pg.DB { + return db + }) + controller.InitializeControllers(c) } diff --git a/pkg/controllers/api.go b/pkg/controller/api.go similarity index 62% rename from pkg/controllers/api.go rename to pkg/controller/api.go index d0bf007..e8c7a4e 100644 --- a/pkg/controllers/api.go +++ b/pkg/controller/api.go @@ -1,32 +1,35 @@ -package controllers +package controller import ( "wallet-api/pkg/middleware" - "wallet-api/pkg/services" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" "github.com/gin-gonic/gin" ) type ApiController struct { - ApiService *services.ApiService + service *service.ApiService } /* NewApiController Initializes ApiController. + Args: *services.ApiService: API service *gin.RouterGroup: Gin Router Group Returns: *ApiController: Controller for "api" interactions */ -func NewApiController(as *services.ApiService, s *gin.RouterGroup) *ApiController { - ac := new(ApiController) - ac.ApiService = as +func NewApiController(as *service.ApiService, routeGroups *common.RouteGroups) *ApiController { + ac := &ApiController{ + service: as, + } - s.GET("", ac.getFirst) - s.POST("migrate", middleware.SecretCode, ac.postMigrate) + routeGroups.Api.GET("", ac.getFirst) + routeGroups.Api.POST("migrate", middleware.SecretCode, ac.postMigrate) return ac } @@ -38,7 +41,7 @@ getFirst */ // ROUTE (GET /api). func (ac *ApiController) getFirst(c *gin.Context) { - apiModel := ac.ApiService.GetFirst(c) + apiModel := ac.service.GetFirst(c) c.JSON(200, apiModel) } @@ -53,11 +56,11 @@ Requires "SECRET_CODE", "VERSION" (optional) from body. func (ac *ApiController) postMigrate(c *gin.Context) { migrateModel := c.MustGet("migrate") version := migrateModel.(middleware.SecretCodeModel).Version - mr, er := ac.ApiService.PostMigrate(c, version) + er := ac.service.PostMigrate(c, version) - if er.Message != "" { - c.JSON(er.StatusCode, er) + if len(er) > 0 { + c.JSON(500, er) } else { - c.JSON(200, mr) + c.JSON(200, nil) } } diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go new file mode 100644 index 0000000..25fbb9e --- /dev/null +++ b/pkg/controller/controller.go @@ -0,0 +1,70 @@ +package controller + +import ( + "fmt" + "go.uber.org/dig" + "strconv" + "strings" + "wallet-api/pkg/model" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" + + "github.com/gin-gonic/gin" +) + +/* +InitializeControllers + +Initializes Dependency Injection modules and registers controllers + + Args: + *dig.Container: Dig Container +*/ +func InitializeControllers(c *dig.Container) { + service.InitializeServices(c) + + c.Invoke(NewApiController) + c.Invoke(NewUserController) + c.Invoke(NewWalletController) + c.Invoke(NewWalletHeaderController) + c.Invoke(NewTransactionController) + c.Invoke(NewTransactionStatusController) + c.Invoke(NewTransactionTypeController) + c.Invoke(NewSubscriptionController) + c.Invoke(NewSubscriptionTypeController) + +} + +/* +FilteredResponse + +Gets query parameters and populates FilteredResponse model. + + Args: + *gin.Context: Gin Application Context + Returns: + *model.FilteredResponse: Filtered response +*/ +func FilteredResponse(c *gin.Context) *model.FilteredResponse { + filtered := new(model.FilteredResponse) + page, _ := c.GetQuery("page") + rpp, _ := c.GetQuery("rpp") + sortBy, _ := c.GetQuery("sortBy") + + dividers := [5]string{"|", " ", ".", "/", ","} + + for _, div := range dividers { + sortArr := strings.Split(sortBy, div) + + if len(sortArr) >= 2 { + sortBy = fmt.Sprintf("%s %s", common.ToSnakeCase(sortArr[0]), strings.ToUpper(sortArr[1])) + } + } + + filtered.Embed, _ = c.GetQuery("embed") + filtered.Page, _ = strconv.Atoi(page) + filtered.Rpp, _ = strconv.Atoi(rpp) + filtered.SortBy = sortBy + + return filtered +} diff --git a/pkg/controllers/subscriptions.go b/pkg/controller/subscription.go similarity index 62% rename from pkg/controllers/subscriptions.go rename to pkg/controller/subscription.go index 0ddaaba..f413c3a 100644 --- a/pkg/controllers/subscriptions.go +++ b/pkg/controller/subscription.go @@ -1,37 +1,41 @@ -package controllers +package controller import ( "net/http" - "wallet-api/pkg/models" - "wallet-api/pkg/services" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" "github.com/gin-gonic/gin" ) type SubscriptionController struct { - SubscriptionService *services.SubscriptionService + service *service.SubscriptionService } /* NewSubscriptionController Initializes SubscriptionController. + Args: *services.SubscriptionService: Subscription service *gin.RouterGroup: Gin Router Group Returns: *SubscriptionController: Controller for "subscription" route interactions */ -func NewSubscriptionController(as *services.SubscriptionService, s *gin.RouterGroup) *SubscriptionController { - wc := new(SubscriptionController) - wc.SubscriptionService = as +func NewSubscriptionController(as *service.SubscriptionService, routeGroups *common.RouteGroups) *SubscriptionController { + wc := &SubscriptionController{ + service: as, + } - s.POST("", wc.New) - s.PUT("/:id", wc.Edit) - s.GET("/:id", wc.Get) - s.GET("", wc.GetAll) + routeGroups.Subscription.POST("", wc.New) + routeGroups.Subscription.PUT("/:id", wc.Edit) + routeGroups.Subscription.GET("/:id", wc.Get) + routeGroups.Subscription.GET("", wc.GetAll) - se := s.Group("/end") + se := routeGroups.Subscription.Group("/end") { se.PUT("/:id", wc.End) } @@ -46,13 +50,15 @@ New */ // ROUTE (POST /subscription) func (wc *SubscriptionController) New(c *gin.Context) { - body := new(models.NewSubscriptionBody) + body := new(model.NewSubscriptionBody) if err := c.ShouldBind(body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - wm, exception := wc.SubscriptionService.New(c, body) + mdl := body.ToSubscription() + + wm, exception := wc.service.New(c, mdl) if exception != nil { c.JSON(exception.StatusCode, exception) @@ -69,7 +75,7 @@ Edit */ // ROUTE (PUT /subscription/:id) func (wc *SubscriptionController) Edit(c *gin.Context) { - body := new(models.SubscriptionEdit) + body := new(model.SubscriptionEdit) if err := c.ShouldBind(body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return @@ -77,7 +83,10 @@ func (wc *SubscriptionController) Edit(c *gin.Context) { id := c.Param("id") - wm, exception := wc.SubscriptionService.Edit(c, body, id) + mdl := body.ToSubscription() + mdl.Id = id + + wm, exception := wc.service.Edit(c, mdl) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -92,18 +101,21 @@ Get */ // ROUTE (GET /subscription/:id) func (wc *SubscriptionController) Get(c *gin.Context) { - body := new(models.Auth) - params := new(models.Params) + params := new(model.Params) auth := c.MustGet("auth") - body.Id = auth.(*models.Auth).Id + userId := auth.(*model.Auth).Id id := c.Param("id") embed, _ := c.GetQuery("embed") params.Embed = embed - fr, exception := wc.SubscriptionService.Get(c, body, id, params) + flt := filter.NewSubscriptionFilter(*params) + flt.Id = id + flt.UserId = userId + + fr, exception := wc.service.Get(c, *flt) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -114,20 +126,9 @@ func (wc *SubscriptionController) Get(c *gin.Context) { // ROUTE (PUT /subscription/end/:id) func (wc *SubscriptionController) End(c *gin.Context) { - body := new(models.Auth) - - auth := c.MustGet("auth") - body.Id = auth.(*models.Auth).Id - - end := new(models.SubscriptionEnd) - if err := c.ShouldBind(end); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - id := c.Param("id") - fr, exception := wc.SubscriptionService.End(c, id) + fr, exception := wc.service.End(c, id) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -143,14 +144,16 @@ GetAll */ // ROUTE (GET /subscription) func (wc *SubscriptionController) GetAll(c *gin.Context) { - body := new(models.Auth) auth := c.MustGet("auth") - body.Id = auth.(*models.Auth).Id - fr := FilteredResponse(c) wallet, _ := c.GetQuery("walletId") + embed, _ := c.GetQuery("embed") - exception := wc.SubscriptionService.GetAll(c, body, wallet, fr) + flt := filter.NewSubscriptionFilter(model.Params{Embed: embed}) + flt.WalletId = wallet + flt.UserId = auth.(*model.Auth).Id + + fr, exception := wc.service.GetAll(c, flt) if exception != nil { c.JSON(exception.StatusCode, exception) return diff --git a/pkg/controllers/subscriptionTypes.go b/pkg/controller/subscriptionType.go similarity index 60% rename from pkg/controllers/subscriptionTypes.go rename to pkg/controller/subscriptionType.go index 7e8b7d4..f0d5169 100644 --- a/pkg/controllers/subscriptionTypes.go +++ b/pkg/controller/subscriptionType.go @@ -1,33 +1,37 @@ -package controllers +package controller import ( "net/http" - "wallet-api/pkg/models" - "wallet-api/pkg/services" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" "github.com/gin-gonic/gin" ) type SubscriptionTypeController struct { - SubscriptionTypeService *services.SubscriptionTypeService + service *service.SubscriptionTypeService } /* NewSubscriptionTypeController Initializes SubscriptionTypeController. + Args: *services.SubscriptionTypeService: Subscription type service *gin.RouterGroup: Gin Router Group Returns: *SubscriptionTypeController: Controller for "subscription-types" route interactions */ -func NewSubscriptionTypeController(as *services.SubscriptionTypeService, s *gin.RouterGroup) *SubscriptionTypeController { - wc := new(SubscriptionTypeController) - wc.SubscriptionTypeService = as +func NewSubscriptionTypeController(as *service.SubscriptionTypeService, routeGroups *common.RouteGroups) *SubscriptionTypeController { + wc := &SubscriptionTypeController{ + service: as, + } - s.POST("", wc.New) - s.GET("", wc.GetAll) + routeGroups.SubscriptionType.POST("", wc.New) + routeGroups.SubscriptionType.GET("", wc.GetAll) return wc } @@ -39,13 +43,15 @@ New */ // ROUTE (POST /subscription-types) func (wc *SubscriptionTypeController) New(c *gin.Context) { - body := new(models.NewSubscriptionTypeBody) + body := new(model.NewSubscriptionTypeBody) if err := c.ShouldBind(body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - wm, exception := wc.SubscriptionTypeService.New(c, body) + mdl := body.ToSubscriptionType() + + wm, exception := wc.service.New(c, mdl) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -62,7 +68,9 @@ GetAll func (wc *SubscriptionTypeController) GetAll(c *gin.Context) { embed, _ := c.GetQuery("embed") - wm, exception := wc.SubscriptionTypeService.GetAll(c, embed) + flt := filter.NewSubscriptionTypeFilter(model.Params{Embed: embed}) + + wm, exception := wc.service.GetAll(c, flt) if exception != nil { c.JSON(exception.StatusCode, exception) return diff --git a/pkg/controllers/transactions.go b/pkg/controller/transaction.go similarity index 61% rename from pkg/controllers/transactions.go rename to pkg/controller/transaction.go index 77e285a..760ab3f 100644 --- a/pkg/controllers/transactions.go +++ b/pkg/controller/transaction.go @@ -1,42 +1,46 @@ -package controllers +package controller import ( "net/http" - "wallet-api/pkg/models" - "wallet-api/pkg/services" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" "github.com/gin-gonic/gin" ) type TransactionController struct { - TransactionService *services.TransactionService + service *service.TransactionService } /* NewTransactionController Initializes TransactionController. + Args: *services.TransactionService: Transaction service *gin.RouterGroup: Gin Router Group Returns: *TransactionController: Controller for "transaction" route interactions */ -func NewTransactionController(as *services.TransactionService, s *gin.RouterGroup) *TransactionController { - wc := new(TransactionController) - wc.TransactionService = as +func NewTransactionController(as *service.TransactionService, routeGroups *common.RouteGroups) *TransactionController { + wc := &TransactionController{ + service: as, + } - s.POST("", wc.New) - s.GET("", wc.GetAll) - s.PUT("/:id", wc.Edit) - s.GET("/:id", wc.Get) + routeGroups.Transaction.POST("", wc.New) + routeGroups.Transaction.GET("", wc.GetAll) + routeGroups.Transaction.PUT("/:id", wc.Edit) + routeGroups.Transaction.GET("/:id", wc.Get) - bulkGroup := s.Group("bulk") + bulkGroup := routeGroups.Transaction.Group("bulk") { bulkGroup.PUT("", wc.BulkEdit) } - checkGroup := s.Group("check") + checkGroup := routeGroups.Transaction.Group("check") { checkGroup.GET("", wc.Check) } @@ -51,13 +55,15 @@ New */ // ROUTE (POST /transactions) func (wc *TransactionController) New(c *gin.Context) { - body := new(models.NewTransactionBody) + body := new(model.NewTransactionBody) if err := c.ShouldBind(body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - wm, exception := wc.TransactionService.New(c, body) + mdl := body.ToTransaction() + + wm, exception := wc.service.New(c, mdl) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -72,17 +78,22 @@ GetAll */ // ROUTE (GET /transactions) func (wc *TransactionController) GetAll(c *gin.Context) { - body := new(models.Auth) auth := c.MustGet("auth") - body.Id = auth.(*models.Auth).Id + userId := auth.(*model.Auth).Id fr := FilteredResponse(c) wallet, _ := c.GetQuery("walletId") + embed, _ := c.GetQuery("embed") noPendingQry, _ := c.GetQuery("noPending") noPending := noPendingQry != "" - exception := wc.TransactionService.GetAll(c, body, wallet, fr, noPending) + flt := filter.NewTransactionFilter(model.Params{Embed: embed}) + flt.WalletId = wallet + flt.NoPending = noPending + flt.UserId = userId + + fr, exception := wc.service.GetAll(c, flt) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -98,14 +109,17 @@ Check */ // ROUTE (GET /transactions) func (wc *TransactionController) Check(c *gin.Context) { - body := new(models.Auth) auth := c.MustGet("auth") - body.Id = auth.(*models.Auth).Id + userId := auth.(*model.Auth).Id fr := FilteredResponse(c) wallet, _ := c.GetQuery("walletId") - exception := wc.TransactionService.Check(c, body, wallet, fr) + flt := filter.NewTransactionFilter(model.Params{}) + flt.WalletId = wallet + flt.UserId = userId + + fr, exception := wc.service.Check(c, flt) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -121,15 +135,17 @@ Edit */ // ROUTE (PUT /transactions/:id) func (wc *TransactionController) Edit(c *gin.Context) { - body := new(models.TransactionEdit) + body := new(model.TransactionEdit) if err := c.ShouldBind(body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } id := c.Param("id") + mdl := body.ToTransaction() + mdl.Id = id - wm, exception := wc.TransactionService.Edit(c, body, id) + wm, exception := wc.service.Edit(c, mdl) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -144,13 +160,19 @@ BulkEdit */ // ROUTE (PUT /transactions/:id) func (wc *TransactionController) BulkEdit(c *gin.Context) { - body := new([]models.TransactionEdit) + body := new([]model.TransactionEdit) if err := c.ShouldBind(body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - wm, exception := wc.TransactionService.BulkEdit(c, body) + mdl := new([]model.Transaction) + for _, transaction := range *body { + tm := transaction.ToTransaction() + *mdl = append(*mdl, *tm) + } + + wm, exception := wc.service.BulkEdit(c, mdl) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -165,18 +187,21 @@ Get */ // ROUTE (GET /transactions/:id) func (wc *TransactionController) Get(c *gin.Context) { - body := new(models.Auth) - params := new(models.Params) + params := new(model.Params) auth := c.MustGet("auth") - body.Id = auth.(*models.Auth).Id + userId := auth.(*model.Auth).Id id := c.Param("id") embed, _ := c.GetQuery("embed") params.Embed = embed - fr, exception := wc.TransactionService.Get(c, body, id, params) + flt := filter.NewTransactionFilter(*params) + flt.Id = id + flt.UserId = userId + + fr, exception := wc.service.Get(c, flt) if exception != nil { c.JSON(exception.StatusCode, exception) return diff --git a/pkg/controllers/transactionStatus.go b/pkg/controller/transactionStatus.go similarity index 60% rename from pkg/controllers/transactionStatus.go rename to pkg/controller/transactionStatus.go index cadb96e..e3fbd6a 100644 --- a/pkg/controllers/transactionStatus.go +++ b/pkg/controller/transactionStatus.go @@ -1,33 +1,37 @@ -package controllers +package controller import ( "net/http" - "wallet-api/pkg/models" - "wallet-api/pkg/services" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" "github.com/gin-gonic/gin" ) type TransactionStatusController struct { - TransactionStatusService *services.TransactionStatusService + service *service.TransactionStatusService } /* NewTransactionStatusController Initializes TransactionStatusController. + Args: *services.TransactionStatusService: Transaction Staus service *gin.RouterGroup: Gin Router Group Returns: *TransactionStatusController: Controller for "transaction-status" route interactions */ -func NewTransactionStatusController(as *services.TransactionStatusService, s *gin.RouterGroup) *TransactionStatusController { - wc := new(TransactionStatusController) - wc.TransactionStatusService = as +func NewTransactionStatusController(as *service.TransactionStatusService, routeGroups *common.RouteGroups) *TransactionStatusController { + wc := &TransactionStatusController{ + service: as, + } - s.POST("", wc.New) - s.GET("", wc.GetAll) + routeGroups.TransactionStatus.POST("", wc.New) + routeGroups.TransactionStatus.GET("", wc.GetAll) return wc } @@ -39,13 +43,15 @@ New */ // ROUTE (POST /transaction-status) func (wc *TransactionStatusController) New(c *gin.Context) { - body := new(models.NewTransactionStatusBody) + body := new(model.NewTransactionStatusBody) if err := c.ShouldBind(body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - wm, exception := wc.TransactionStatusService.New(c, body) + mdl := body.ToTransactionStatus() + + wm, exception := wc.service.New(c, mdl) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -61,8 +67,9 @@ GetAll // ROUTE (GET /transaction-status) func (wc *TransactionStatusController) GetAll(c *gin.Context) { embed, _ := c.GetQuery("embed") + flt := filter.NewTransactionStatusFilter(model.Params{Embed: embed}) - wm, exception := wc.TransactionStatusService.GetAll(c, embed) + wm, exception := wc.service.GetAll(c, flt) if exception != nil { c.JSON(exception.StatusCode, exception) return diff --git a/pkg/controllers/transactionTypes.go b/pkg/controller/transactionType.go similarity index 60% rename from pkg/controllers/transactionTypes.go rename to pkg/controller/transactionType.go index 1874bb6..9b5e734 100644 --- a/pkg/controllers/transactionTypes.go +++ b/pkg/controller/transactionType.go @@ -1,33 +1,37 @@ -package controllers +package controller import ( "net/http" - "wallet-api/pkg/models" - "wallet-api/pkg/services" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" "github.com/gin-gonic/gin" ) type TransactionTypeController struct { - TransactionTypeService *services.TransactionTypeService + service *service.TransactionTypeService } /* NewTransactionTypeController Initializes TransactionTypeController. + Args: *services.TransactionTypeService: Transaction Type service *gin.RouterGroup: Gin Router Group Returns: *TransactionTypeController: Controller for "transaction-types" route interactions */ -func NewTransactionTypeController(as *services.TransactionTypeService, s *gin.RouterGroup) *TransactionTypeController { - wc := new(TransactionTypeController) - wc.TransactionTypeService = as +func NewTransactionTypeController(as *service.TransactionTypeService, routeGroups *common.RouteGroups) *TransactionTypeController { + wc := &TransactionTypeController{ + service: as, + } - s.POST("", wc.New) - s.GET("", wc.GetAll) + routeGroups.TransactionType.POST("", wc.New) + routeGroups.TransactionType.GET("", wc.GetAll) return wc } @@ -39,13 +43,14 @@ New */ // ROUTE (POST /transaction-types) func (wc *TransactionTypeController) New(c *gin.Context) { - body := new(models.NewTransactionTypeBody) + body := new(model.NewTransactionTypeBody) if err := c.ShouldBind(body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + mdl := body.ToTransactionType() - wm, exception := wc.TransactionTypeService.New(c, body) + wm, exception := wc.service.New(c, mdl) if exception != nil { c.JSON(exception.StatusCode, exception) return @@ -62,7 +67,9 @@ GetAll func (wc *TransactionTypeController) GetAll(c *gin.Context) { embed, _ := c.GetQuery("embed") - wm, exception := wc.TransactionTypeService.GetAll(c, embed) + flt := filter.NewTransactionTypeFilter(model.Params{Embed: embed}) + + wm, exception := wc.service.GetAll(c, flt) if exception != nil { c.JSON(exception.StatusCode, exception) return diff --git a/pkg/controllers/auth.go b/pkg/controller/user.go similarity index 53% rename from pkg/controllers/auth.go rename to pkg/controller/user.go index a2f5661..a33ecc6 100644 --- a/pkg/controllers/auth.go +++ b/pkg/controller/user.go @@ -1,36 +1,40 @@ -package controllers +package controller import ( "net/http" + "wallet-api/pkg/filter" "wallet-api/pkg/middleware" - "wallet-api/pkg/models" - "wallet-api/pkg/services" + "wallet-api/pkg/model" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" "github.com/gin-gonic/gin" ) -type AuthController struct { - UsersService *services.UsersService +type UserController struct { + service *service.UserService } /* -NewAuthController +NewUserController + +Initializes UserController. -Initializes AuthController. Args: - *services.UsersService: Users service + *services.UserService: User service *gin.RouterGroup: Gin Router Group Returns: - *AuthController: Controller for "auth" interactions + *UserController: Controller for "auth" interactions */ -func NewAuthController(rs *services.UsersService, s *gin.RouterGroup) *AuthController { - rc := new(AuthController) - rc.UsersService = rs +func NewUserController(rs *service.UserService, routeGroups *common.RouteGroups) *UserController { + rc := &UserController{ + service: rs, + } - s.POST("login", rc.PostLogin) - s.POST("register", rc.PostRegister) - s.DELETE("deactivate", middleware.Auth, rc.Delete) - s.GET("check-token", rc.CheckToken) + routeGroups.Auth.POST("login", rc.PostLogin) + routeGroups.Auth.POST("register", rc.PostRegister) + routeGroups.Auth.DELETE("deactivate", middleware.Auth, rc.Delete) + routeGroups.Auth.GET("check-token", rc.CheckToken) return rc } @@ -41,13 +45,13 @@ PostLogin *gin.Context: Gin Application Context */ // ROUTE (POST /auth/login). -func (rc *AuthController) PostLogin(c *gin.Context) { - body := new(models.Login) +func (rc *UserController) PostLogin(c *gin.Context) { + body := new(model.Login) if err := c.ShouldBind(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - returnedUser, exceptionReturn := rc.UsersService.Login(c, body) + returnedUser, exceptionReturn := rc.service.Login(c, body) if exceptionReturn.Message != "" { c.JSON(exceptionReturn.StatusCode, exceptionReturn) @@ -63,15 +67,15 @@ PostRegister *gin.Context: Gin Application Context */ // ROUTE (POST /auth/register). -func (rc *AuthController) PostRegister(c *gin.Context) { - body := new(models.User) +func (rc *UserController) PostRegister(c *gin.Context) { + body := new(model.User) body.Init() body.IsActive = true if err := c.ShouldBind(body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - returnedUser, exceptionReturn := rc.UsersService.Create(c, body) + returnedUser, exceptionReturn := rc.service.Create(c, body) if exceptionReturn.Message != "" { c.JSON(exceptionReturn.StatusCode, exceptionReturn) @@ -87,12 +91,14 @@ Delete *gin.Context: Gin Application Context */ // ROUTE (DELETE /auth/deactivate). -func (rc *AuthController) Delete(c *gin.Context) { - auth := new(models.Auth) +func (rc *UserController) Delete(c *gin.Context) { authGet := c.MustGet("auth") - auth.Id = authGet.(*models.Auth).Id + userId := authGet.(*model.Auth).Id - mr, er := rc.UsersService.Deactivate(c, auth) + flt := filter.NewUserFilter(model.Params{}) + flt.UserId = userId + + mr, er := rc.service.Deactivate(c, flt) if er.Message != "" { c.JSON(er.StatusCode, er) @@ -108,9 +114,9 @@ CheckToken *gin.Context: Gin Application Context */ // ROUTE (GET /auth/check-token). -func (rc *AuthController) CheckToken(c *gin.Context) { +func (rc *UserController) CheckToken(c *gin.Context) { token, _ := c.GetQuery("token") - re := new(models.CheckToken) + re := new(model.CheckToken) _, err := middleware.CheckToken(token) diff --git a/pkg/controller/wallet-header.go b/pkg/controller/wallet-header.go new file mode 100644 index 0000000..708b3b2 --- /dev/null +++ b/pkg/controller/wallet-header.go @@ -0,0 +1,61 @@ +package controller + +import ( + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" + + "github.com/gin-gonic/gin" +) + +type WalletHeaderController struct { + service *service.WalletService +} + +/* +NewWalletHeaderController + +Initializes WalletHeaderController. + + Args: + *services.WalletService: Wallet service + *gin.RouterGroup: Gin Router Group + Returns: + *WalletHeaderController: Controller for "wallet/wallet-header" route interactions +*/ +func NewWalletHeaderController(as *service.WalletService, routeGroups *common.RouteGroups) *WalletHeaderController { + wc := &WalletHeaderController{ + service: as, + } + + routeGroups.WalletHeader.GET("", wc.Get) + + return wc +} + +/* +Get + Args: + *gin.Context: Gin Application Context +*/ +// ROUTE (GET /wallet/wallet-header) +func (wc *WalletHeaderController) Get(c *gin.Context) { + walletId, _ := c.GetQuery("walletId") + + auth := c.MustGet("auth") + userId := auth.(*model.Auth).Id + embed, _ := c.GetQuery("embed") + + flt := filter.NewWalletHeaderFilter(model.Params{Embed: embed}) + flt.UserId = userId + flt.WalletId = walletId + + wm, exception := wc.service.GetHeader(c, flt) + if exception != nil { + c.JSON(exception.StatusCode, exception) + return + } + + c.JSON(200, wm) +} diff --git a/pkg/controller/wallet.go b/pkg/controller/wallet.go new file mode 100644 index 0000000..dac08a0 --- /dev/null +++ b/pkg/controller/wallet.go @@ -0,0 +1,139 @@ +package controller + +import ( + "net/http" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/service" + "wallet-api/pkg/utl/common" + + "github.com/gin-gonic/gin" +) + +type WalletController struct { + service *service.WalletService +} + +/* +NewWalletController + +Initializes WalletController. + + Args: + *services.WalletService: Wallet service + *gin.RouterGroup: Gin Router Group + Returns: + *WalletController: Controller for "wallet" route interactions +*/ +func NewWalletController(as *service.WalletService, routeGroups *common.RouteGroups) *WalletController { + wc := &WalletController{ + service: as, + } + + routeGroups.Wallet.POST("", wc.New) + routeGroups.Wallet.GET("", wc.GetAll) + routeGroups.Wallet.PUT("/:id", wc.Edit) + routeGroups.Wallet.GET("/:id", wc.Get) + + return wc +} + +/* +New + Args: + *gin.Context: Gin Application Context +*/ +// ROUTE (POST /wallet) +func (wc *WalletController) New(c *gin.Context) { + body := new(model.NewWalletBody) + + if err := c.ShouldBind(body); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + get := c.MustGet("auth") + body.UserID = get.(*model.Auth).Id + + wm, exception := wc.service.New(c, body) + if exception != nil { + c.JSON(exception.StatusCode, exception) + return + } + c.JSON(200, wm) +} + +/* +GetAll + Args: + *gin.Context: Gin Application Context +*/ +// ROUTE (GET /wallet) +func (wc *WalletController) GetAll(c *gin.Context) { + auth := c.MustGet("auth") + userId := auth.(*model.Auth).Id + + embed, _ := c.GetQuery("embed") + flt := filter.NewWalletFilter(model.Params{Embed: embed}) + flt.UserId = userId + + fr, exception := wc.service.GetAll(c, flt) + if exception != nil { + c.JSON(exception.StatusCode, exception) + return + } + + c.JSON(200, fr) +} + +/* +Edit + Args: + *gin.Context: Gin Application Context +*/ +// ROUTE (PUT /wallet/:id) +func (wc *WalletController) Edit(c *gin.Context) { + body := new(model.WalletEdit) + if err := c.ShouldBind(body); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + id := c.Param("id") + + mdl := body.ToWallet() + mdl.Id = id + + wm, exception := wc.service.Edit(c, mdl) + if exception != nil { + c.JSON(exception.StatusCode, exception) + return + } + c.JSON(200, wm) +} + +/* +Get + Args: + *gin.Context: Gin Application Context +*/ +// ROUTE (GET /wallet/:id) +func (wc *WalletController) Get(c *gin.Context) { + params := new(model.Params) + + id := c.Param("id") + + embed, _ := c.GetQuery("embed") + params.Embed = embed + + flt := filter.NewWalletFilter(*params) + flt.Id = id + + fr, exception := wc.service.Get(c, flt) + if exception != nil { + c.JSON(exception.StatusCode, exception) + return + } + + c.JSON(200, fr) +} diff --git a/pkg/controllers/controllers.go b/pkg/controllers/controllers.go deleted file mode 100644 index 083754b..0000000 --- a/pkg/controllers/controllers.go +++ /dev/null @@ -1,44 +0,0 @@ -package controllers - -import ( - "fmt" - "strconv" - "strings" - "wallet-api/pkg/models" - "wallet-api/pkg/utl/common" - - "github.com/gin-gonic/gin" -) - -/* -FilteredResponse - -Gets query parameters and populates FilteredResponse model. - Args: - *gin.Context: Gin Application Context - Returns: - *models.FilteredResponse: Filtered response -*/ -func FilteredResponse(c *gin.Context) *models.FilteredResponse { - filtered := new(models.FilteredResponse) - page, _ := c.GetQuery("page") - rpp, _ := c.GetQuery("rpp") - sortBy, _ := c.GetQuery("sortBy") - - dividers := [5]string{"|", " ", ".", "/", ","} - - for _, div := range dividers { - sortArr := strings.Split(sortBy, div) - - if len(sortArr) >= 2 { - sortBy = fmt.Sprintf("%s %s", common.ToSnakeCase(sortArr[0]), strings.ToUpper(sortArr[1])) - } - } - - filtered.Embed, _ = c.GetQuery("embed") - filtered.Page, _ = strconv.Atoi(page) - filtered.Rpp, _ = strconv.Atoi(rpp) - filtered.SortBy = sortBy - - return filtered -} diff --git a/pkg/controllers/wallets-header.go b/pkg/controllers/wallets-header.go deleted file mode 100644 index da4be0d..0000000 --- a/pkg/controllers/wallets-header.go +++ /dev/null @@ -1,54 +0,0 @@ -package controllers - -import ( - "wallet-api/pkg/models" - "wallet-api/pkg/services" - - "github.com/gin-gonic/gin" -) - -type WalletsHeaderController struct { - WalletService *services.WalletService -} - -/* -NewWalletsHeaderController - -Initializes WalletsHeaderController. - Args: - *services.WalletService: Wallet service - *gin.RouterGroup: Gin Router Group - Returns: - *WalletsHeaderController: Controller for "wallet/wallet-header" route interactions -*/ -func NewWalletsHeaderController(as *services.WalletService, s *gin.RouterGroup) *WalletsHeaderController { - wc := new(WalletsHeaderController) - wc.WalletService = as - - s.GET("", wc.Get) - - return wc -} - -/* -Get - Args: - *gin.Context: Gin Application Context -*/ -// ROUTE (GET /wallet/wallet-header) -func (wc *WalletsHeaderController) Get(c *gin.Context) { - body := new(models.Auth) - - walletId, _ := c.GetQuery("walletId") - - auth := c.MustGet("auth") - body.Id = auth.(*models.Auth).Id - - wm, exception := wc.WalletService.GetHeader(c, body, walletId) - if exception != nil { - c.JSON(exception.StatusCode, exception) - return - } - - c.JSON(200, wm) -} diff --git a/pkg/controllers/wallets.go b/pkg/controllers/wallets.go deleted file mode 100644 index ba00ed0..0000000 --- a/pkg/controllers/wallets.go +++ /dev/null @@ -1,128 +0,0 @@ -package controllers - -import ( - "net/http" - "wallet-api/pkg/models" - "wallet-api/pkg/services" - - "github.com/gin-gonic/gin" -) - -type WalletsController struct { - WalletService *services.WalletService -} - -/* -NewWalletsController - -Initializes WalletsController. - Args: - *services.WalletService: Wallet service - *gin.RouterGroup: Gin Router Group - Returns: - *WalletsController: Controller for "wallet" route interactions -*/ -func NewWalletsController(as *services.WalletService, s *gin.RouterGroup) *WalletsController { - wc := new(WalletsController) - wc.WalletService = as - - s.POST("", wc.New) - s.GET("", wc.GetAll) - s.PUT("/:id", wc.Edit) - s.GET("/:id", wc.Get) - - return wc -} - -/* -New - Args: - *gin.Context: Gin Application Context -*/ -// ROUTE (POST /wallet) -func (wc *WalletsController) New(c *gin.Context) { - body := new(models.NewWalletBody) - - if err := c.ShouldBind(body); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - get := c.MustGet("auth") - body.UserID = get.(*models.Auth).Id - - wm, exception := wc.WalletService.New(c, body) - if exception != nil { - c.JSON(exception.StatusCode, exception) - return - } - c.JSON(200, wm) -} - -/* -GetAll - Args: - *gin.Context: Gin Application Context -*/ -// ROUTE (GET /wallet) -func (wc *WalletsController) GetAll(c *gin.Context) { - body := new(models.Auth) - auth := c.MustGet("auth") - body.Id = auth.(*models.Auth).Id - - fr := FilteredResponse(c) - - exception := wc.WalletService.GetAll(c, body, fr) - if exception != nil { - c.JSON(exception.StatusCode, exception) - return - } - - c.JSON(200, fr) -} - -/* -Edit - Args: - *gin.Context: Gin Application Context -*/ -// ROUTE (PUT /wallet/:id) -func (wc *WalletsController) Edit(c *gin.Context) { - body := new(models.WalletEdit) - if err := c.ShouldBind(body); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - id := c.Param("id") - - wm, exception := wc.WalletService.Edit(c, body, id) - if exception != nil { - c.JSON(exception.StatusCode, exception) - return - } - c.JSON(200, wm) -} - -/* -Get - Args: - *gin.Context: Gin Application Context -*/ -// ROUTE (GET /wallet/:id) -func (wc *WalletsController) Get(c *gin.Context) { - params := new(models.Params) - - id := c.Param("id") - - embed, _ := c.GetQuery("embed") - params.Embed = embed - - fr, exception := wc.WalletService.Get(c, id, params) - if exception != nil { - c.JSON(exception.StatusCode, exception) - return - } - - c.JSON(200, fr) -} diff --git a/pkg/filter/api.go b/pkg/filter/api.go new file mode 100644 index 0000000..167e427 --- /dev/null +++ b/pkg/filter/api.go @@ -0,0 +1,13 @@ +package filter + +import "wallet-api/pkg/model" + +type ApiFilter struct { + model.Params +} + +func NewApiFilter(params model.Params) *ApiFilter { + return &ApiFilter{ + Params: params, + } +} diff --git a/pkg/filter/filter.go b/pkg/filter/filter.go new file mode 100644 index 0000000..07924d9 --- /dev/null +++ b/pkg/filter/filter.go @@ -0,0 +1,37 @@ +package filter + +import ( + "go.uber.org/dig" + "wallet-api/pkg/model" +) + +/* +InitializeFilters + +Initializes Dependency Injection modules for filters + + Args: + *dig.Container: Dig Container +*/ +func InitializeFilters(c *dig.Container) { + c.Provide(NewApiFilter) + c.Provide(NewSubscriptionFilter) + c.Provide(NewSubscriptionTypeFilter) + c.Provide(NewTransactionFilter) + c.Provide(NewTransactionStatusFilter) + c.Provide(NewTransactionTypeFilter) + c.Provide(NewUserFilter) + c.Provide(NewWalletFilter) +} + +type BaseFilter struct { + model.Params + Id string + WalletId string + UserId string +} + +type BBa interface { + BaseFilter + some() bool +} diff --git a/pkg/filter/subscription.go b/pkg/filter/subscription.go new file mode 100644 index 0000000..d023cb8 --- /dev/null +++ b/pkg/filter/subscription.go @@ -0,0 +1,14 @@ +package filter + +import "wallet-api/pkg/model" + +type SubscriptionFilter struct { + model.Params + BaseFilter +} + +func NewSubscriptionFilter(params model.Params) *SubscriptionFilter { + return &SubscriptionFilter{ + Params: params, + } +} diff --git a/pkg/filter/subscriptionType.go b/pkg/filter/subscriptionType.go new file mode 100644 index 0000000..39eccac --- /dev/null +++ b/pkg/filter/subscriptionType.go @@ -0,0 +1,14 @@ +package filter + +import "wallet-api/pkg/model" + +type SubscriptionTypeFilter struct { + model.Params + BaseFilter +} + +func NewSubscriptionTypeFilter(params model.Params) *SubscriptionTypeFilter { + return &SubscriptionTypeFilter{ + Params: params, + } +} diff --git a/pkg/filter/transaction.go b/pkg/filter/transaction.go new file mode 100644 index 0000000..fb608ff --- /dev/null +++ b/pkg/filter/transaction.go @@ -0,0 +1,16 @@ +package filter + +import "wallet-api/pkg/model" + +type TransactionFilter struct { + model.Params + BaseFilter + NoPending bool + TransactionStatusId string +} + +func NewTransactionFilter(params model.Params) *TransactionFilter { + return &TransactionFilter{ + Params: params, + } +} diff --git a/pkg/filter/transactionStatus.go b/pkg/filter/transactionStatus.go new file mode 100644 index 0000000..6f11a92 --- /dev/null +++ b/pkg/filter/transactionStatus.go @@ -0,0 +1,15 @@ +package filter + +import "wallet-api/pkg/model" + +type TransactionStatusFilter struct { + model.Params + BaseFilter + Status string +} + +func NewTransactionStatusFilter(params model.Params) *TransactionStatusFilter { + return &TransactionStatusFilter{ + Params: params, + } +} diff --git a/pkg/filter/transactionType.go b/pkg/filter/transactionType.go new file mode 100644 index 0000000..b6c7375 --- /dev/null +++ b/pkg/filter/transactionType.go @@ -0,0 +1,14 @@ +package filter + +import "wallet-api/pkg/model" + +type TransactionTypeFilter struct { + model.Params + BaseFilter +} + +func NewTransactionTypeFilter(params model.Params) *TransactionTypeFilter { + return &TransactionTypeFilter{ + Params: params, + } +} diff --git a/pkg/filter/user.go b/pkg/filter/user.go new file mode 100644 index 0000000..3075707 --- /dev/null +++ b/pkg/filter/user.go @@ -0,0 +1,14 @@ +package filter + +import "wallet-api/pkg/model" + +type UserFilter struct { + model.Params + BaseFilter +} + +func NewUserFilter(params model.Params) *UserFilter { + return &UserFilter{ + Params: params, + } +} diff --git a/pkg/filter/wallet-header.go b/pkg/filter/wallet-header.go new file mode 100644 index 0000000..2de1ced --- /dev/null +++ b/pkg/filter/wallet-header.go @@ -0,0 +1,14 @@ +package filter + +import "wallet-api/pkg/model" + +type WalletHeaderFilter struct { + model.Params + BaseFilter +} + +func NewWalletHeaderFilter(params model.Params) *WalletHeaderFilter { + return &WalletHeaderFilter{ + Params: params, + } +} diff --git a/pkg/filter/wallet.go b/pkg/filter/wallet.go new file mode 100644 index 0000000..4c56bfd --- /dev/null +++ b/pkg/filter/wallet.go @@ -0,0 +1,14 @@ +package filter + +import "wallet-api/pkg/model" + +type WalletFilter struct { + model.Params + BaseFilter +} + +func NewWalletFilter(params model.Params) *WalletFilter { + return &WalletFilter{ + Params: params, + } +} diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index afa7616..a6d6644 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -4,7 +4,7 @@ import ( "errors" "os" "strings" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "wallet-api/pkg/utl/configs" "github.com/dgrijalva/jwt-go" @@ -16,11 +16,12 @@ import ( Auth Checks if token from header is valid and extracts the id. + Args: *gin.Context: Gin Application Context. */ func Auth(c *gin.Context) { - exceptionReturn := new(models.Exception) + exceptionReturn := new(model.Exception) tokenString := ExtractToken(c) token, err := CheckToken(tokenString) if err != nil { @@ -33,7 +34,7 @@ func Auth(c *gin.Context) { if ok && token.Valid { userId, _ := claims["id"].(string) - authModel := new(models.Auth) + authModel := new(model.Auth) authModel.Id = userId c.Set("auth", authModel) @@ -45,6 +46,7 @@ func Auth(c *gin.Context) { ExtractToken Extracts token from header + Args: *gin.Context: Gin Application Context. Returns: @@ -66,6 +68,7 @@ func ExtractToken(c *gin.Context) string { CheckToken Checks if token is valid + Args: string: Token to check Returns: diff --git a/pkg/middleware/secretCode.go b/pkg/middleware/secretCode.go index 519996f..e357b16 100644 --- a/pkg/middleware/secretCode.go +++ b/pkg/middleware/secretCode.go @@ -3,7 +3,7 @@ package middleware import ( "net/http" "os" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "wallet-api/pkg/utl/configs" "github.com/gin-gonic/gin" @@ -13,11 +13,12 @@ import ( SecretCode Checks if secret code from body is valid. - Args: - *gin.Context: Gin Application Context. + + Args: + *gin.Context: Gin Application Context. */ func SecretCode(c *gin.Context) { - exceptionReturn := new(models.Exception) + exceptionReturn := new(model.Exception) secretCode := ExtractCode(c) secret := os.Getenv("SECRET_CODE") if secret == "" { @@ -37,8 +38,9 @@ func SecretCode(c *gin.Context) { ExtractCode Extracts the secret code from body. - Args: - *gin.Context: Gin Application Context. + + Args: + *gin.Context: Gin Application Context. */ func ExtractCode(c *gin.Context) SecretCodeModel { secret := new(SecretCodeModel) diff --git a/pkg/migrate/10_create_table_transaction_status.go b/pkg/migrate/10_create_table_transaction_status.go index 0b9ff6a..05b9d52 100644 --- a/pkg/migrate/10_create_table_transaction_status.go +++ b/pkg/migrate/10_create_table_transaction_status.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" @@ -14,14 +14,15 @@ import ( CreateTableTransactionStatus Creates transaction_status table if it does not exist. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with table creation + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with table creation */ func CreateTableTransactionStatus(db pg.DB) error { models := []interface{}{ - (*models.TransactionStatus)(nil), + (*model.TransactionStatus)(nil), } for _, model := range models { diff --git a/pkg/migrate/11_populate_transaction_status.go b/pkg/migrate/11_populate_transaction_status.go index 6ca1009..85523ae 100644 --- a/pkg/migrate/11_populate_transaction_status.go +++ b/pkg/migrate/11_populate_transaction_status.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" ) @@ -12,15 +12,16 @@ import ( PopulateTransactionStatus Populates transaction_status table if it exists. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with populating table + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with populating table */ func PopulateTransactionStatus(db pg.DB) error { - completed := new(models.TransactionStatus) - pending := new(models.TransactionStatus) - deleted := new(models.TransactionStatus) + completed := new(model.TransactionStatus) + pending := new(model.TransactionStatus) + deleted := new(model.TransactionStatus) completed.Init() completed.Name = "Completed" diff --git a/pkg/migrate/1_create_table_api.go b/pkg/migrate/1_create_table_api.go index 0d07370..2f6d20b 100644 --- a/pkg/migrate/1_create_table_api.go +++ b/pkg/migrate/1_create_table_api.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" @@ -14,15 +14,16 @@ import ( CreateTableApi Creates api table if it does not exist. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with table creation + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with table creation */ func CreateTableApi(db pg.DB) error { models := []interface{}{ - (*models.ApiModel)(nil), + (*model.ApiModel)(nil), } for _, model := range models { diff --git a/pkg/migrate/2_create_table_users.go b/pkg/migrate/2_create_table_users.go index 164bb61..c3a9f23 100644 --- a/pkg/migrate/2_create_table_users.go +++ b/pkg/migrate/2_create_table_users.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" @@ -14,14 +14,15 @@ import ( CreateTableUsers Creates users table if it does not exist. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with table creation + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with table creation */ func CreateTableUsers(db pg.DB) error { models := []interface{}{ - (*models.User)(nil), + (*model.User)(nil), } for _, model := range models { diff --git a/pkg/migrate/3_create_table_wallets.go b/pkg/migrate/3_create_table_wallets.go index 51f4ebb..7269224 100644 --- a/pkg/migrate/3_create_table_wallets.go +++ b/pkg/migrate/3_create_table_wallets.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" @@ -14,14 +14,15 @@ import ( CreateTableWallets Creates wallets table if it does not exist. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with table creation + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with table creation */ func CreateTableWallets(db pg.DB) error { models := []interface{}{ - (*models.Wallet)(nil), + (*model.Wallet)(nil), } for _, model := range models { diff --git a/pkg/migrate/4_create_table_transaction_types.go b/pkg/migrate/4_create_table_transaction_types.go index 6fd742e..691d7b8 100644 --- a/pkg/migrate/4_create_table_transaction_types.go +++ b/pkg/migrate/4_create_table_transaction_types.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" @@ -14,14 +14,15 @@ import ( CreateTableTransactionTypes Creates transaction_types table if it does not exist. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with table creation + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with table creation */ func CreateTableTransactionTypes(db pg.DB) error { models := []interface{}{ - (*models.TransactionType)(nil), + (*model.TransactionType)(nil), } for _, model := range models { diff --git a/pkg/migrate/5_create_table_transactions.go b/pkg/migrate/5_create_table_transactions.go index d777869..3801db0 100644 --- a/pkg/migrate/5_create_table_transactions.go +++ b/pkg/migrate/5_create_table_transactions.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" @@ -14,14 +14,15 @@ import ( CreateTableTransactions Creates transactions table if it does not exist. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with table creation + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with table creation */ func CreateTableTransactions(db pg.DB) error { models := []interface{}{ - (*models.Transaction)(nil), + (*model.Transaction)(nil), } for _, model := range models { diff --git a/pkg/migrate/6_create_table_subscription_types.go b/pkg/migrate/6_create_table_subscription_types.go index 716ac63..890b887 100644 --- a/pkg/migrate/6_create_table_subscription_types.go +++ b/pkg/migrate/6_create_table_subscription_types.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" @@ -14,14 +14,15 @@ import ( CreateTableSubscriptionTypes Creates subscription_types table if it does not exist. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with table creation + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with table creation */ func CreateTableSubscriptionTypes(db pg.DB) error { models := []interface{}{ - (*models.SubscriptionType)(nil), + (*model.SubscriptionType)(nil), } for _, model := range models { diff --git a/pkg/migrate/7_create_table_subscriptions.go b/pkg/migrate/7_create_table_subscriptions.go index a7e5977..b6efb90 100644 --- a/pkg/migrate/7_create_table_subscriptions.go +++ b/pkg/migrate/7_create_table_subscriptions.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" @@ -13,14 +13,15 @@ import ( CreateTableSubscriptions Creates subscriptions table if it does not exist. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with table creation + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with table creation */ func CreateTableSubscriptions(db pg.DB) error { models := []interface{}{ - (*models.Subscription)(nil), + (*model.Subscription)(nil), } for _, model := range models { diff --git a/pkg/migrate/8_populate_subscription_types.go b/pkg/migrate/8_populate_subscription_types.go index c2703d4..74801ab 100644 --- a/pkg/migrate/8_populate_subscription_types.go +++ b/pkg/migrate/8_populate_subscription_types.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" ) @@ -12,16 +12,17 @@ import ( PopulateSubscriptionTypes Populates subscription_types table if it exists. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with populating table + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with populating table */ func PopulateSubscriptionTypes(db pg.DB) error { - daily := new(models.SubscriptionType) - weekly := new(models.SubscriptionType) - monthly := new(models.SubscriptionType) - yearly := new(models.SubscriptionType) + daily := new(model.SubscriptionType) + weekly := new(model.SubscriptionType) + monthly := new(model.SubscriptionType) + yearly := new(model.SubscriptionType) daily.Init() daily.Name = "Daily" diff --git a/pkg/migrate/9_populate_transaction_types.go b/pkg/migrate/9_populate_transaction_types.go index 7b3a27f..52dbeb9 100644 --- a/pkg/migrate/9_populate_transaction_types.go +++ b/pkg/migrate/9_populate_transaction_types.go @@ -3,7 +3,7 @@ package migrate import ( "fmt" "log" - "wallet-api/pkg/models" + "wallet-api/pkg/model" "github.com/go-pg/pg/v10" ) @@ -12,14 +12,15 @@ import ( PopulateTransactionTypes Populates transaction_types table if it exists. - Args: - *pg.DB: Postgres database client - Returns: - error: Returns if there is an error with populating table + + Args: + *pg.DB: Postgres database client + Returns: + error: Returns if there is an error with populating table */ func PopulateTransactionTypes(db pg.DB) error { - gain := new(models.TransactionType) - expense := new(models.TransactionType) + gain := new(model.TransactionType) + expense := new(model.TransactionType) gain.Init() gain.Name = "Gain" diff --git a/pkg/migrate/migrate.go b/pkg/migrate/migrate.go index 341b841..e23444e 100644 --- a/pkg/migrate/migrate.go +++ b/pkg/migrate/migrate.go @@ -13,7 +13,7 @@ Starts database migration. Returns: error: Returns if there is an error with populating table */ -func Start(conn *pg.DB, version string) { +func Start(conn *pg.DB, version string) []error { migration001 := Migration{ Version: "001", Migrations: []interface{}{ @@ -53,16 +53,23 @@ func Start(conn *pg.DB, version string) { migration004, } + var errors []error + for _, migrationCol := range migrationsMap { if version != "" && version == migrationCol.Version || version == "" { for _, migration := range migrationCol.Migrations { mgFunc, isFunc := migration.(func(pg.DB) error) if isFunc { - mgFunc(*conn) + err := mgFunc(*conn) + if err != nil { + errors = append(errors, err) + } } } } } + + return errors } type Migration struct { diff --git a/pkg/models/api.go b/pkg/model/api.go similarity index 80% rename from pkg/models/api.go rename to pkg/model/api.go index e670a4d..a280ab7 100644 --- a/pkg/models/api.go +++ b/pkg/model/api.go @@ -1,6 +1,7 @@ -package models +package model type ApiModel struct { + BaseModel tableName struct{} `pg:"api,alias:api"` Api string `json:"api"` } diff --git a/pkg/model/auth.go b/pkg/model/auth.go new file mode 100644 index 0000000..062d521 --- /dev/null +++ b/pkg/model/auth.go @@ -0,0 +1,19 @@ +package model + +type Token struct { + Token string `json:"token"` +} + +type Login struct { + Email string `json:"email" form:"email"` + Password string `json:"password" form:"password"` + RememberMe bool `json:"rememberMe" form:"rememberMe"` +} + +type Auth struct { + Id string +} + +type CheckToken struct { + Valid bool `json:"valid"` +} diff --git a/pkg/models/db.go b/pkg/model/db.go similarity index 96% rename from pkg/models/db.go rename to pkg/model/db.go index f80637a..01ad271 100644 --- a/pkg/models/db.go +++ b/pkg/model/db.go @@ -1,4 +1,4 @@ -package models +package model import ( "time" diff --git a/pkg/models/exception.go b/pkg/model/exception.go similarity index 90% rename from pkg/models/exception.go rename to pkg/model/exception.go index b5213a9..d784655 100644 --- a/pkg/models/exception.go +++ b/pkg/model/exception.go @@ -1,4 +1,4 @@ -package models +package model type Exception struct { ErrorCode string `json:"errorCode"` diff --git a/pkg/model/model.go b/pkg/model/model.go new file mode 100644 index 0000000..b8c6efe --- /dev/null +++ b/pkg/model/model.go @@ -0,0 +1,24 @@ +package model + +import ( + "github.com/gin-gonic/gin" +) + +type FilteredResponse struct { + Items interface{} `json:"items"` + Params +} + +type ResponseFunc func(*gin.Context) *[]interface{} + +type MessageResponse struct { + Message string `json:"message"` +} + +type Params struct { + SortBy string `json:"sortBy"` + Embed string `json:"embed"` + Page int `json:"page"` + Rpp int `json:"rpp"` + TotalRecords int `json:"totalRecords"` +} diff --git a/pkg/models/register.go b/pkg/model/register.go similarity index 98% rename from pkg/models/register.go rename to pkg/model/register.go index 0a90960..ff7b5f2 100644 --- a/pkg/models/register.go +++ b/pkg/model/register.go @@ -1,4 +1,4 @@ -package models +package model type User struct { tableName struct{} `pg:"users,alias:users"` @@ -20,6 +20,7 @@ type UserReturnInfo struct { Payload Maps User object to UserReturnInfo object. + Returns: *UserReturnInfo: mapped UserReturnInfo object */ diff --git a/pkg/models/subscriptions.go b/pkg/model/subscription.go similarity index 80% rename from pkg/models/subscriptions.go rename to pkg/model/subscription.go index e605a12..a8e2e27 100644 --- a/pkg/models/subscriptions.go +++ b/pkg/model/subscription.go @@ -1,7 +1,8 @@ -package models +package model import ( "encoding/json" + "math" "time" ) @@ -45,6 +46,41 @@ type NewSubscriptionBody struct { Amount json.Number `json:"amount" form:"amount"` } +func (body *SubscriptionEdit) ToSubscription() *Subscription { + amount, _ := body.Amount.Float64() + tm := new(Subscription) + tm.Id = body.Id + tm.EndDate = body.EndDate + tm.HasEnd = body.HasEnd + tm.Description = body.Description + tm.WalletID = body.WalletID + tm.Amount = float32(math.Round(amount*100) / 100) + + return tm +} + +func (body *NewSubscriptionBody) ToSubscription() *Subscription { + tm := new(Subscription) + amount, _ := body.Amount.Float64() + customRange, _ := body.CustomRange.Int64() + + tm.Init() + tm.WalletID = body.WalletID + tm.TransactionTypeID = body.TransactionTypeID + tm.SubscriptionTypeID = body.SubscriptionTypeID + tm.CustomRange = int(customRange) + tm.Description = body.Description + tm.StartDate = body.StartDate + tm.HasEnd = body.HasEnd + tm.EndDate = body.EndDate + tm.Amount = float32(math.Round(amount*100) / 100) + + if body.StartDate.IsZero() { + tm.StartDate = time.Now() + } + return tm +} + type SubscriptionEnd struct { Id string `json:"id" form:"id"` } @@ -53,6 +89,7 @@ type SubscriptionEnd struct { ToTrans Maps Subscription object to Transaction object. + Returns: *Transaction: mapped Transaction object */ @@ -74,6 +111,7 @@ func (cm *Subscription) ToTrans() *Transaction { HasNew Checks if Subscription reached new transaction interval. + Returns: bool: Is new transaction interval reached */ diff --git a/pkg/models/subscriptionTypes.go b/pkg/model/subscriptionType.go similarity index 62% rename from pkg/models/subscriptionTypes.go rename to pkg/model/subscriptionType.go index 84ce657..1bdd153 100644 --- a/pkg/models/subscriptionTypes.go +++ b/pkg/model/subscriptionType.go @@ -1,4 +1,4 @@ -package models +package model type SubscriptionType struct { tableName struct{} `pg:"subscriptionTypes,alias:subscriptionTypes"` @@ -11,3 +11,13 @@ type NewSubscriptionTypeBody struct { Name string `json:"name" form:"name"` Type string `json:"type" form:"type"` } + +func (body *NewSubscriptionTypeBody) ToSubscriptionType() *SubscriptionType { + tm := new(SubscriptionType) + + tm.Init() + tm.Name = body.Name + tm.Type = body.Type + + return tm +} \ No newline at end of file diff --git a/pkg/models/transactions.go b/pkg/model/transaction.go similarity index 70% rename from pkg/models/transactions.go rename to pkg/model/transaction.go index f177a68..38b3b82 100644 --- a/pkg/models/transactions.go +++ b/pkg/model/transaction.go @@ -1,7 +1,8 @@ -package models +package model import ( "encoding/json" + "math" "time" ) @@ -29,6 +30,23 @@ type NewTransactionBody struct { Amount json.Number `json:"amount" form:"amount"` } +func (body *NewTransactionBody) ToTransaction() *Transaction { + tm := new(Transaction) + amount, _ := body.Amount.Float64() + + tm.Init() + tm.WalletID = body.WalletID + tm.TransactionTypeID = body.TransactionTypeID + tm.Description = body.Description + tm.TransactionDate = body.TransactionDate + tm.Amount = float32(math.Round(amount*100) / 100) + + if body.TransactionDate.IsZero() { + tm.TransactionDate = time.Now() + } + return tm +} + type TransactionEdit struct { Id string `json:"id" form:"id"` WalletID string `json:"walletId" form:"walletId"` @@ -38,3 +56,18 @@ type TransactionEdit struct { Description string `json:"description" form:"description"` Amount json.Number `json:"amount" form:"amount"` } + +func (body *TransactionEdit) ToTransaction() *Transaction { + tm := new(Transaction) + amount, _ := body.Amount.Float64() + + tm.Id = body.Id + tm.Description = body.Description + tm.WalletID = body.WalletID + tm.TransactionTypeID = body.TransactionTypeID + tm.TransactionDate = body.TransactionDate + tm.TransactionStatusID = body.TransactionStatusID + tm.Amount = float32(math.Round(amount*100) / 100) + + return tm +} diff --git a/pkg/model/transactionStatus.go b/pkg/model/transactionStatus.go new file mode 100644 index 0000000..8e17dbc --- /dev/null +++ b/pkg/model/transactionStatus.go @@ -0,0 +1,21 @@ +package model + +type TransactionStatus struct { + tableName struct{} `pg:"transactionStatus,alias:transactionStatus"` + BaseModel + Name string `json:"name" pg:"name"` + Status string `json:"status" pg:"status,notnull"` +} + +type NewTransactionStatusBody struct { + Name string `json:"name" form:"name"` + Status string `json:"status" form:"status"` +} + +func (body *NewTransactionStatusBody) ToTransactionStatus() *TransactionStatus { + tm := new(TransactionStatus) + tm.Init() + tm.Name = body.Name + tm.Status = body.Status + return tm +} diff --git a/pkg/models/transactionTypes.go b/pkg/model/transactionType.go similarity index 62% rename from pkg/models/transactionTypes.go rename to pkg/model/transactionType.go index 32f8958..7566ac9 100644 --- a/pkg/models/transactionTypes.go +++ b/pkg/model/transactionType.go @@ -1,4 +1,4 @@ -package models +package model type TransactionType struct { tableName struct{} `pg:"transactionTypes,alias:transactionTypes"` @@ -11,3 +11,11 @@ type NewTransactionTypeBody struct { Name string `json:"name" form:"name"` Type string `json:"type" form:"type"` } + +func (body *NewTransactionTypeBody) ToTransactionType() *TransactionType { + tm := new(TransactionType) + tm.Init() + tm.Name = body.Name + tm.Type = body.Type + return tm +} diff --git a/pkg/models/wallets.go b/pkg/model/wallet.go similarity index 76% rename from pkg/models/wallets.go rename to pkg/model/wallet.go index c45ed24..5d0c39f 100644 --- a/pkg/models/wallets.go +++ b/pkg/model/wallet.go @@ -1,4 +1,4 @@ -package models +package model type Wallet struct { tableName struct{} `pg:"wallets,alias:wallets"` @@ -33,3 +33,18 @@ type WalletEdit struct { Id string `json:"id" form:"id"` Name string `json:"name" form:"name"` } + +func (body *NewWalletBody) ToWallet() *Wallet { + tm := new(Wallet) + tm.Init() + tm.Name = body.Name + tm.UserID = body.UserID + return tm +} + +func (body *WalletEdit) ToWallet() *Wallet { + tm := new(Wallet) + tm.Name = body.Name + tm.Id = body.Id + return tm +} diff --git a/pkg/models/auth.go b/pkg/models/auth.go deleted file mode 100644 index 2e04efc..0000000 --- a/pkg/models/auth.go +++ /dev/null @@ -1,19 +0,0 @@ -package models - -type Token struct { - Token string `json:"token"` -} - -type Login struct { - Email string `json:"email" form:"email"` - Password string `json:"password" form:"password"` - RememberMe bool `json:"rememberMe" form:"rememberMe"` -} - -type Auth struct { - Id string -} - -type CheckToken struct { - Valid bool `json:"valid"` -} diff --git a/pkg/models/models.go b/pkg/models/models.go deleted file mode 100644 index 6b79356..0000000 --- a/pkg/models/models.go +++ /dev/null @@ -1,22 +0,0 @@ -package models - -import "github.com/gin-gonic/gin" - -type FilteredResponse struct { - Items interface{} `json:"items"` - Params -} - -type ResponseFunc func(*gin.Context) *[]interface{} - -type MessageResponse struct { - Message string `json:"message"` -} - -type Params struct { - SortBy string `json:"sortBy"` - Embed string `json:"embed"` - Page int `json:"page"` - Rpp int `json:"rpp"` - TotalRecords int `json:"totalRecords"` -} \ No newline at end of file diff --git a/pkg/models/transactionStatus.go b/pkg/models/transactionStatus.go deleted file mode 100644 index 3843a42..0000000 --- a/pkg/models/transactionStatus.go +++ /dev/null @@ -1,13 +0,0 @@ -package models - -type TransactionStatus struct { - tableName struct{} `pg:"transactionStatus,alias:transactionStatus"` - BaseModel - Name string `json:"name" pg:"name"` - Status string `json:"status" pg:"status,notnull"` -} - -type NewTransactionStatusBody struct { - Name string `json:"name" form:"name"` - Status string `json:"status" form:"status"` -} diff --git a/pkg/repository/api.go b/pkg/repository/api.go new file mode 100644 index 0000000..79820cb --- /dev/null +++ b/pkg/repository/api.go @@ -0,0 +1,53 @@ +package repository + +import ( + "context" + "wallet-api/pkg/migrate" + "wallet-api/pkg/model" + + "github.com/go-pg/pg/v10" +) + +type ApiRepository struct { + db *pg.DB +} + +func NewApiRepository(db *pg.DB) *ApiRepository { + return &ApiRepository{ + db: db, + } +} + +/* +GetFirst + +Gets first row from API table. + + Args: + context.Context: Application context + Returns: + model.ApiModel: Api object from database. +*/ +func (as ApiRepository) GetFirst(ctx context.Context) model.ApiModel { + db := as.db.WithContext(ctx) + apiModel := model.ApiModel{Api: "Works"} + db.Model(&apiModel).First() + return apiModel +} + +/* +PostMigrate + +Starts database migration. + + Args: + context.Context: Application context + string: Migration version + Returns: + *model.MessageResponse: Message response object. + *model.Exception: Exception response object. +*/ +func (as ApiRepository) PostMigrate(ctx context.Context, version string) []error { + db := as.db.WithContext(ctx) + return migrate.Start(db, version) +} diff --git a/pkg/repository/repository.go b/pkg/repository/repository.go new file mode 100644 index 0000000..348275d --- /dev/null +++ b/pkg/repository/repository.go @@ -0,0 +1,61 @@ +package repository + +import ( + "go.uber.org/dig" + "wallet-api/pkg/model" + "wallet-api/pkg/utl/common" + + "github.com/go-pg/pg/v10" +) + +/* +InitializeRepositories + +Initializes Dependency Injection modules for repositories + + Args: + *dig.Container: Dig Container +*/ +func InitializeRepositories(c *dig.Container) { + c.Provide(NewApiRepository) + c.Provide(NewSubscriptionRepository) + c.Provide(NewSubscriptionTypeRepository) + c.Provide(NewTransactionRepository) + c.Provide(NewTransactionStatusRepository) + c.Provide(NewTransactionTypeRepository) + c.Provide(NewUserRepository) + c.Provide(NewWalletRepository) +} + +/* +FilteredResponse + +Adds filter to query and executes it. + + Args: + *pg.Query: postgres query + interface{}: model to be mapped from query execution. + *model.FilteredResponse: filter options. +*/ +func FilteredResponse(qry *pg.Query, mdl interface{}, params model.Params) (*model.FilteredResponse, error) { + filtered := new(model.FilteredResponse) + filtered.Params = params + if filtered.Page == 0 { + filtered.Page = 1 + } + if filtered.Rpp == 0 { + filtered.Rpp = 20 + } + if filtered.SortBy == "" { + filtered.SortBy = "date_created DESC" + } + qry = qry.Limit(filtered.Rpp).Offset((filtered.Page - 1) * filtered.Rpp).Order(filtered.SortBy) + common.GenerateEmbed(qry, filtered.Embed) + count, err := qry.SelectAndCount() + common.CheckError(err) + + filtered.TotalRecords = count + filtered.Items = mdl + + return filtered, err +} diff --git a/pkg/repository/subscription.go b/pkg/repository/subscription.go new file mode 100644 index 0000000..cbb8382 --- /dev/null +++ b/pkg/repository/subscription.go @@ -0,0 +1,284 @@ +package repository + +import ( + "context" + "fmt" + "github.com/go-pg/pg/v10/orm" + "time" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/utl/common" + + "github.com/go-pg/pg/v10" +) + +type SubscriptionRepository struct { + db *pg.DB +} + +func NewSubscriptionRepository(db *pg.DB) *SubscriptionRepository { + return &SubscriptionRepository{ + db: db, + } +} + +/* +New + +Inserts new row to subscription table. + + Args: + context.Context: Application context + *model.NewSubscriptionBody: Request body + Returns: + *model.Subscription: Created Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionRepository) New(ctx context.Context, tm *model.Subscription) (*model.Subscription, error) { + db := as.db.WithContext(ctx) + + tx, _ := db.Begin() + defer tx.Rollback() + + _, err := tx.Model(tm).Insert() + if err != nil { + return nil, err + } + tx.Commit() + + return tm, nil +} + +/* +Get + +Gets row from subscription table by id. + + Args: + context.Context: Application context + *model.Auth: Authentication model + string: subscription id to search + params: *model.Params + Returns: + *model.Subscription: Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionRepository) Get(ctx context.Context, flt filter.SubscriptionFilter) (*model.Subscription, error) { + db := as.db.WithContext(ctx) + tx, _ := db.Begin() + defer tx.Rollback() + + am := new(model.Subscription) + am.Id = flt.Id + + qry := tx.Model(am) + as.OnBeforeGetSubscriptionFilter(qry, &flt) + err := common.GenerateEmbed(qry, flt.Embed).WherePK().Select() + if err != nil { + return nil, err + } + + tx.Commit() + + return am, nil +} + +/* +GetAll + +Gets filtered rows from subscription table. + + Args: + context.Context: Application context + *model.Auth: Authentication object + string: Wallet id to search + *model.FilteredResponse: filter options + Returns: + *model.Exception: Exception payload. +*/ +func (as *SubscriptionRepository) GetAll(ctx context.Context, flt *filter.SubscriptionFilter) (*model.FilteredResponse, error) { + db := as.db.WithContext(ctx) + + tx, _ := db.Begin() + defer tx.Rollback() + + am := new([]model.Subscription) + query := tx.Model(am) + as.OnBeforeGetSubscriptionFilter(query, flt) + + filtered, err := FilteredResponse(query, am, flt.Params) + if err != nil { + return nil, err + } + tx.Commit() + + return filtered, nil +} + +/* +GetAllTx + +Gets filtered rows from subscription table. + + Args: + context.Context: Application context + *model.Auth: Authentication object + string: Wallet id to search + *model.FilteredResponse: filter options + Returns: + *model.Exception: Exception payload. +*/ +func (as *SubscriptionRepository) GetAllTx(tx *pg.Tx, flt *filter.SubscriptionFilter) (*[]model.Subscription, error) { + am := new([]model.Subscription) + query := tx.Model(am) + as.OnBeforeGetSubscriptionFilter(query, flt) + + common.GenerateEmbed(query, flt.Embed) + err := query.Select() + if err != nil { + return nil, err + } + + return am, nil +} + +/* +Edit + +Updates row from subscription table by id. + + Args: + context.Context: Application context + *model.SubscriptionEdit: Values to edit + string: id to search + Returns: + *model.Subscription: Edited Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionRepository) Edit(ctx context.Context, tm *model.Subscription) (*model.Subscription, error) { + db := as.db.WithContext(ctx) + + tx, _ := db.Begin() + defer tx.Rollback() + + _, err := tx.Model(tm).WherePK().UpdateNotZero() + if err != nil { + return nil, err + } + + tx.Commit() + + return tm, nil +} + +/* +End + +Updates row in subscription table by id. + +Ends subscription with current date. + + Args: + context.Context: Application context + string: id to search + Returns: + *model.Subscription: Created Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionRepository) End(ctx context.Context, tm *model.Subscription) (*model.Subscription, error) { + db := as.db.WithContext(ctx) + + tx, _ := db.Begin() + defer tx.Rollback() + + _, err := tx.Model(tm).WherePK().UpdateNotZero() + if err != nil { + return nil, err + } + + tx.Commit() + + return tm, nil +} + +/* +SubToTrans + +Generates and Inserts new Transaction rows from the subscription model. + + Args: + *model.Subscription: Subscription model to generate new transactions from + *pg.Tx: Postgres query context + Returns: + *model.Exception: Exception payload. +*/ +func (as *SubscriptionRepository) SubToTrans(subModel *model.Subscription, tx *pg.Tx) *model.Exception { + exceptionReturn := new(model.Exception) + + now := time.Now() + + currentYear, currentMonth, _ := now.Date() + currentLocation := now.Location() + + transactionStatus := new(model.TransactionStatus) + firstOfNextMonth := time.Date(currentYear, currentMonth+1, 1, 0, 0, 0, 0, currentLocation) + tx.Model(transactionStatus).Where("? = ?", pg.Ident("status"), "pending").Select() + + startDate := subModel.StartDate + stopDate := firstOfNextMonth + if subModel.HasEnd && subModel.EndDate.Before(firstOfNextMonth) { + stopDate = subModel.EndDate + } + + transactions := new([]model.Transaction) + + if subModel.SubscriptionType == nil { + st := new(model.SubscriptionType) + tx.Model(st).Where("? = ?", pg.Ident("id"), subModel.SubscriptionTypeID).Select() + subModel.SubscriptionType = st + } + + for startDate.Before(stopDate) { + trans := subModel.ToTrans() + trans.TransactionDate = startDate + trans.TransactionStatusID = transactionStatus.Id + if startDate.After(subModel.LastTransactionDate) && (startDate.Before(now) || startDate.Equal(now)) { + *transactions = append(*transactions, *trans) + } + if subModel.SubscriptionType.Type == "monthly" { + startDate = startDate.AddDate(0, subModel.CustomRange, 0) + } else if subModel.SubscriptionType.Type == "weekly" { + startDate = startDate.AddDate(0, 0, 7*subModel.CustomRange) + } else if subModel.SubscriptionType.Type == "daily" { + startDate = startDate.AddDate(0, 0, subModel.CustomRange) + } else { + startDate = startDate.AddDate(subModel.CustomRange, 0, 0) + } + } + + var err error + if len(*transactions) > 0 { + for _, trans := range *transactions { + _, err = tx.Model(&trans).Where("? = ?", pg.Ident("transaction_date"), trans.TransactionDate).Where("? = ?", pg.Ident("subscription_id"), trans.SubscriptionID).OnConflict("DO NOTHING").SelectOrInsert() + if err != nil { + _, err = tx.Model(subModel).Set("? = ?", pg.Ident("last_transaction_date"), trans.TransactionDate).WherePK().Update() + } + } + } + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400113" + exceptionReturn.Message = fmt.Sprintf("Error updating row in \"subscription\" table: %s", err) + return exceptionReturn + } + return nil +} + +func (as *SubscriptionRepository) OnBeforeGetSubscriptionFilter(qry *orm.Query, flt *filter.SubscriptionFilter) { + if flt.UserId != "" { + qry.Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), flt.UserId) + } + if flt.WalletId != "" { + qry.Where("? = ?", pg.Ident("wallet_id"), flt.WalletId) + } +} diff --git a/pkg/repository/subscriptionType.go b/pkg/repository/subscriptionType.go new file mode 100644 index 0000000..477445a --- /dev/null +++ b/pkg/repository/subscriptionType.go @@ -0,0 +1,68 @@ +package repository + +import ( + "context" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/utl/common" + + "github.com/go-pg/pg/v10" +) + +type SubscriptionTypeRepository struct { + db *pg.DB +} + +func NewSubscriptionTypeRepository(db *pg.DB) *SubscriptionTypeRepository { + return &SubscriptionTypeRepository{ + db: db, + } +} + +/* +New + +Inserts new row to subscription type table. + + Args: + context.Context: Application context + *model.NewSubscriptionTypeBody: Values to create new row + Returns: + *model.SubscriptionType: Created row from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionTypeRepository) New(ctx context.Context, tm *model.SubscriptionType) (*model.SubscriptionType, error) { + db := as.db.WithContext(ctx) + + _, err := db.Model(tm).Insert() + if err != nil { + return nil, err + } + + return tm, nil +} + +/* +GetAll + +Gets all rows from subscription type table. + + Args: + context.Context: Application context + string: Relations to embed + Returns: + *[]model.SubscriptionType: List of subscription type objects. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionTypeRepository) GetAll(ctx context.Context, flt *filter.SubscriptionTypeFilter) (*[]model.SubscriptionType, error) { + wm := new([]model.SubscriptionType) + db := as.db.WithContext(ctx) + + query := db.Model(wm) + err := common.GenerateEmbed(query, flt.Embed).Select() + if err != nil { + return nil, err + } + + return wm, nil +} diff --git a/pkg/repository/transaction.go b/pkg/repository/transaction.go new file mode 100644 index 0000000..289fd70 --- /dev/null +++ b/pkg/repository/transaction.go @@ -0,0 +1,316 @@ +package repository + +import ( + "context" + "fmt" + "github.com/go-pg/pg/v10/orm" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/utl/common" + + "github.com/go-pg/pg/v10" +) + +type TransactionRepository struct { + db *pg.DB + subscriptionRepository *SubscriptionRepository + transactionStatusRepository *TransactionStatusRepository +} + +func NewTransactionRepository(db *pg.DB, ss *SubscriptionRepository, tsr *TransactionStatusRepository) *TransactionRepository { + return &TransactionRepository{ + db: db, + subscriptionRepository: ss, + transactionStatusRepository: tsr, + } +} + +/* +New row into transaction table + +Inserts + + Args: + context.Context: Application context + *model.NewTransactionBody: Transaction body object + Returns: + *model.Transaction: Transaction object + *model.Exception: Exception payload. +*/ +func (as *TransactionRepository) New(ctx context.Context, tm *model.Transaction, tx *pg.Tx) (*model.Transaction, error) { + var commit = false + if tx == nil { + commit = true + db := as.db.WithContext(ctx) + tx, _ = db.Begin() + + } + + if commit { + defer tx.Rollback() + } + + _, err := tx.Model(tm).Insert() + if err != nil { + return nil, err + } + if commit { + tx.Commit() + } + + return tm, nil +} + +/* +GetAll + +Gets all rows from subscription type table. + Args: + context.Context: Application context + string: Relations to embed + Returns: + *model.Exception: Exception payload. +*/ +// Gets filtered rows from transaction table. +func (as *TransactionRepository) GetAll(ctx context.Context, flt *filter.TransactionFilter) (*model.FilteredResponse, *model.Exception) { + db := as.db.WithContext(ctx) + + exceptionReturn := new(model.Exception) + wm := new([]model.Transaction) + + tx, _ := db.Begin() + defer tx.Rollback() + + if flt.NoPending { + tsFlt := filter.NewTransactionStatusFilter(model.Params{}) + tsFlt.Status = "completed" + transactionStatus, err := as.transactionStatusRepository.GetTx(tx, tsFlt) + + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400117" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactionStatus\" table: %s", err) + return nil, exceptionReturn + } + + flt.TransactionStatusId = transactionStatus.Id + } + + query := tx.Model(wm) + + as.OnBeforeGetTransactionFilter(query, flt) + filtered, err := FilteredResponse(query, wm, flt.Params) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400118" + exceptionReturn.Message = fmt.Sprintf("Error selecting row(s) in \"transaction\" table: %s", err) + return nil, exceptionReturn + } + + tx.Commit() + return filtered, nil +} + +/* +GetAllTx + +Gets filtered rows from transaction table. + + Args: + context.Context: Application context + *model.Auth: Authentication object + string: Wallet id to search + *model.FilteredResponse: filter options + Returns: + *model.Exception: Exception payload. +*/ +func (as *TransactionRepository) GetAllTx(tx *pg.Tx, flt *filter.TransactionFilter) (*[]model.Transaction, error) { + am := new([]model.Transaction) + query := tx.Model(am) + as.OnBeforeGetTransactionFilter(query, flt) + + common.GenerateEmbed(query, flt.Embed) + err := query.Select() + if err != nil { + return nil, err + } + + return am, nil +} + +/* +Check + +Checks subscriptions and create transactions. + Args: + context.Context: Application context + string: Relations to embed + Returns: + *model.Exception: Exception payload. +*/ +// Gets filtered rows from transaction table. +func (as *TransactionRepository) Check(ctx context.Context, flt *filter.TransactionFilter) (*model.FilteredResponse, *model.Exception) { + db := as.db.WithContext(ctx) + + wm := new([]model.Transaction) + exceptionReturn := new(model.Exception) + filtered := new(model.FilteredResponse) + + tx, _ := db.Begin() + defer tx.Rollback() + + tsFlt := filter.NewTransactionStatusFilter(model.Params{}) + tsFlt.Status = "pending" + transactionStatus, err := as.transactionStatusRepository.GetTx(tx, tsFlt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400119" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactionStatus\" table: %s", err) + return nil, exceptionReturn + } + flt.TransactionStatusId = transactionStatus.Id + + smFlt := filter.NewSubscriptionFilter(model.Params{}) + smFlt.Id = flt.Id + smFlt.WalletId = flt.WalletId + sm, err := as.subscriptionRepository.GetAllTx(tx, smFlt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400137" + exceptionReturn.Message = fmt.Sprintf("Error selecting rows in \"subscription\" table: %s", err) + return nil, exceptionReturn + } + + for _, sub := range *sm { + if sub.HasNew() { + as.subscriptionRepository.SubToTrans(&sub, tx) + } + } + + qry := tx.Model(wm) + as.OnBeforeGetTransactionFilter(qry, flt) + filtered, err = FilteredResponse(qry, wm, flt.Params) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400120" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transaction\" table: %s", err) + return nil, exceptionReturn + } + + tx.Commit() + return filtered, nil +} + +/* +Edit + +Updates row in transaction table by id. + + Args: + context.Context: Application context + *model.TransactionEdit: Object to edit + string: id to search + Returns: + *model.Transaction: Transaction object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionRepository) Edit(ctx context.Context, tm *model.Transaction) (*model.Transaction, error) { + db := as.db.WithContext(ctx) + + tx, _ := db.Begin() + defer tx.Rollback() + + _, err := tx.Model(tm).WherePK().UpdateNotZero() + + if err != nil { + return nil, err + } + + err = tx.Model(tm).WherePK().Select() + if err != nil { + return nil, err + } + + tx.Commit() + + return tm, nil +} + +/* +BulkEdit + +Updates row in transaction table by id. + + Args: + context.Context: Application context + ?[]model.Transaction Bulk Edit: Object to edit + string: id to search + Returns: + *model.Transaction: Transaction object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionRepository) BulkEdit(ctx context.Context, transactions *[]model.Transaction) (*[]model.Transaction, error) { + db := as.db.WithContext(ctx) + tx, _ := db.Begin() + defer tx.Rollback() + + _, err := tx.Model(transactions).WherePK().UpdateNotZero() + if err != nil { + return nil, err + } + + tx.Commit() + + return transactions, nil +} + +/* +Get + +Gets row from transaction table by id. + + Args: + context.Context: Application context + *model.Auth: Authentication object + string: id to search + *model.Params: url query parameters + Returns: + *model.Transaction: Transaction object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionRepository) Get(ctx context.Context, flt *filter.TransactionFilter) (*model.Transaction, error) { + db := as.db.WithContext(ctx) + wm := new(model.Transaction) + wm.Id = flt.Id + + tx, _ := db.Begin() + defer tx.Rollback() + + qry := tx.Model(wm) + as.OnBeforeGetTransactionFilter(qry, flt) + err := common.GenerateEmbed(qry, flt.Embed).WherePK().Select() + if err != nil { + return nil, err + } + + tx.Commit() + + return wm, nil +} + +func (as *TransactionRepository) OnBeforeGetTransactionFilter(qry *orm.Query, flt *filter.TransactionFilter) { + if flt.WalletId != "" { + qry.Where("? = ?", pg.Ident("wallet_id"), flt.WalletId) + } + if flt.UserId != "" { + qry.Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), flt.UserId) + } + if flt.TransactionStatusId != "" { + qry.Where("? = ?", pg.Ident("transaction_status_id"), flt.TransactionStatusId) + } +} + +func (as *TransactionRepository) CreateTx(ctx context.Context) (*pg.Tx, error) { + db := as.db.WithContext(ctx) + return db.Begin() +} diff --git a/pkg/repository/transactionStatus.go b/pkg/repository/transactionStatus.go new file mode 100644 index 0000000..778cc4b --- /dev/null +++ b/pkg/repository/transactionStatus.go @@ -0,0 +1,156 @@ +package repository + +import ( + "context" + "github.com/go-pg/pg/v10/orm" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/utl/common" + + "github.com/go-pg/pg/v10" +) + +type TransactionStatusRepository struct { + db *pg.DB +} + +func NewTransactionStatusRepository(db *pg.DB) *TransactionStatusRepository { + return &TransactionStatusRepository{ + db: db, + } +} + +/* +New + +Inserts new row to transaction status table. + + Args: + context.Context: Application context + *model.NewTransactionStatusBody: object to create + Returns: + *model.TransactionType: Transaction Type object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionStatusRepository) New(ctx context.Context, tm *model.TransactionStatus) (*model.TransactionStatus, error) { + db := as.db.WithContext(ctx) + + _, err := db.Model(tm).Insert() + if err != nil { + return nil, err + } + + return tm, nil +} + +/* +GetAll + +Gets all rows from transaction status table. + + Args: + context.Context: Application context + string: Relations to embed + Returns: + *[]model.TransactionStatus: List of Transaction status objects from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionStatusRepository) GetAll(ctx context.Context, flt *filter.TransactionStatusFilter, tx *pg.Tx) (*[]model.TransactionStatus, error) { + var commit = false + if tx == nil { + commit = true + db := as.db.WithContext(ctx) + tx, _ = db.Begin() + } + + if commit { + defer tx.Rollback() + } + + wm := new([]model.TransactionStatus) + + query := tx.Model(wm) + as.OnBeforeGetTransactionStatusFilter(query, flt) + err := common.GenerateEmbed(query, flt.Embed).Select() + if err != nil { + return nil, err + } + + if commit { + tx.Commit() + } + + return wm, nil +} + +/* +Get + +Gets row from transactionStatus table by id. + + Args: + context.Context: Application context + *model.Auth: Authentication model + string: transactionStatus id to search + params: *model.Params + Returns: + *model.Subscription: Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionStatusRepository) Get(ctx context.Context, flt *filter.TransactionStatusFilter, tx *pg.Tx) (*model.TransactionStatus, error) { + am := new(model.TransactionStatus) + commit := false + if tx == nil { + commit = true + db := as.db.WithContext(ctx) + tx, _ = db.Begin() + defer tx.Rollback() + } + + qry := tx.Model(am) + err := common.GenerateEmbed(qry, flt.Embed).Select() + if err != nil { + return nil, err + } + + if commit { + tx.Commit() + } + + return am, nil +} + +/* +GetTx + +Gets row from transactionStatus table by id. + + Args: + context.Context: Application context + *model.Auth: Authentication model + string: transactionStatus id to search + params: *model.Params + Returns: + *model.Subscription: Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionStatusRepository) GetTx(tx *pg.Tx, flt *filter.TransactionStatusFilter) (*model.TransactionStatus, error) { + am := new(model.TransactionStatus) + qry := tx.Model(am) + as.OnBeforeGetTransactionStatusFilter(qry, flt) + err := common.GenerateEmbed(qry, flt.Embed).Select() + if err != nil { + return nil, err + } + + return am, nil +} + +func (as *TransactionStatusRepository) OnBeforeGetTransactionStatusFilter(qry *orm.Query, flt *filter.TransactionStatusFilter) { + if flt.Id != "" { + qry.Where("? = ?", pg.Ident("id"), flt.Id) + } + if flt.Status != "" { + qry.Where("? = ?", pg.Ident("status"), flt.Status) + } +} diff --git a/pkg/repository/transactionType.go b/pkg/repository/transactionType.go new file mode 100644 index 0000000..a69a9ff --- /dev/null +++ b/pkg/repository/transactionType.go @@ -0,0 +1,69 @@ +package repository + +import ( + "context" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/utl/common" + + "github.com/go-pg/pg/v10" +) + +type TransactionTypeRepository struct { + db *pg.DB +} + +func NewTransactionTypeRepository(db *pg.DB) *TransactionTypeRepository { + return &TransactionTypeRepository{ + db: db, + } +} + +/* +New + +Inserts new row to transaction type table. + + Args: + context.Context: Application context + *model.NewTransactionTypeBody: object to create + Returns: + *model.TransactionType: Transaction Type object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionTypeRepository) New(ctx context.Context, tm *model.TransactionType) (*model.TransactionType, error) { + db := as.db.WithContext(ctx) + + _, err := db.Model(tm).Insert() + if err != nil { + return nil, err + } + + return tm, nil +} + +/* +GetAll + +Gets all rows from transaction type table. + + Args: + context.Context: Application context + string: Relations to embed + Returns: + *[]model.TransactionType: List of Transaction type objects from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionTypeRepository) GetAll(ctx context.Context, flt *filter.TransactionTypeFilter) (*[]model.TransactionType, error) { + db := as.db.WithContext(ctx) + + wm := new([]model.TransactionType) + + query := db.Model(wm) + err := common.GenerateEmbed(query, flt.Embed).Select() + if err != nil { + return nil, err + } + + return wm, nil +} diff --git a/pkg/repository/user.go b/pkg/repository/user.go new file mode 100644 index 0000000..0fa4b97 --- /dev/null +++ b/pkg/repository/user.go @@ -0,0 +1,316 @@ +package repository + +import ( + "context" + "os" + "time" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/utl/common" + "wallet-api/pkg/utl/configs" + + jwt "github.com/dgrijalva/jwt-go" + "golang.org/x/crypto/bcrypt" + + "github.com/go-pg/pg/v10" +) + +type UserRepository struct { + db *pg.DB +} + +func NewUserRepository(db *pg.DB) *UserRepository { + return &UserRepository{ + db: db, + } +} + +/* +Get + +Gets row from transaction table by id. + + Args: + context.Context: Application context + *model.Auth: Authentication object + string: id to search + *model.Params: url query parameters + Returns: + *model.Transaction: Transaction object from database. + *model.Exception: Exception payload. +*/ +func (us *UserRepository) Get(ctx context.Context, flt *filter.UserFilter, tx *pg.Tx) (*model.User, error) { + wm := new(model.User) + wm.Id = flt.Id + + commit := false + if tx == nil { + commit = true + db := us.db.WithContext(ctx) + tx, _ := db.Begin() + defer tx.Rollback() + } + + qry := tx.Model(wm) + err := common.GenerateEmbed(qry, flt.Embed).WherePK().Select() + if err != nil { + return nil, err + } + + if commit { + tx.Commit() + } + + return wm, nil +} + +/* +Edit + +Updates row in transaction table by id. + + Args: + context.Context: Application context + *model.TransactionEdit: Object to edit + string: id to search + Returns: + *model.Transaction: Transaction object from database. + *model.Exception: Exception payload. +*/ +func (us *UserRepository) Edit(ctx context.Context, tm *model.User, tx *pg.Tx) (*model.User, error) { + commit := false + if tx == nil { + commit = true + db := us.db.WithContext(ctx) + tx, _ := db.Begin() + defer tx.Rollback() + } + + _, err := tx.Model(tm).WherePK().UpdateNotZero() + + if err != nil { + return nil, err + } + + err = tx.Model(tm).WherePK().Select() + if err != nil { + return nil, err + } + + if commit { + tx.Commit() + } + + return tm, nil +} + +/* +Check + +Inserts new row to users table. + + Args: + context.Context: Application context + *model.User: User object to create + Returns: + *model.User: User object from database + *model.Exception +*/ +func (us *UserRepository) Check(ctx context.Context, tx *pg.Tx, checkBody *model.User) (*model.User, error) { + check := new(model.User) + + commit := false + if tx == nil { + commit = true + db := us.db.WithContext(ctx) + tx, _ := db.Begin() + defer tx.Rollback() + } + + err := tx.Model(check).Where("? = ?", pg.Ident("username"), checkBody.Username).WhereOr("? = ?", pg.Ident("email"), checkBody.Email).Select() + if err != nil { + return nil, err + } + if commit { + tx.Commit() + } + return check, nil +} + +/* +Create + +Inserts new row to users table. + + Args: + context.Context: Application context + *model.User: User object to create + Returns: + *model.User: User object from database + *model.Exception +*/ +func (us *UserRepository) Create(ctx context.Context, tx *pg.Tx, registerBody *model.User) (*model.User, error) { + commit := false + if tx == nil { + commit = true + db := us.db.WithContext(ctx) + tx, _ := db.Begin() + defer tx.Rollback() + } + + _, err := tx.Model(registerBody).Insert() + + if err != nil { + return nil, err + } + + if commit { + tx.Commit() + } + + return registerBody, nil +} + +func (us *UserRepository) CreateTx(ctx context.Context) (*pg.Tx, error) { + db := us.db.WithContext(ctx) + return db.Begin() +} + +/* +Login + +Gets row from users table by email and valid password. + + Args: + context.Context: Application context + *model.Login: object to search + Returns: + *model.Token: new session token + *model.Exception +*/ +func (us *UserRepository) Login(ctx context.Context, loginBody *model.Login) (*model.Token, *model.Exception) { + db := us.db.WithContext(ctx) + + check := new(model.User) + exceptionReturn := new(model.Exception) + tokenPayload := new(model.Token) + + tx, _ := db.Begin() + defer tx.Rollback() + + tx.Model(check).Where("? = ?", pg.Ident("email"), loginBody.Email).Select() + + if check.Email == "" { + exceptionReturn.Message = "Email not found" + exceptionReturn.ErrorCode = "400103" + exceptionReturn.StatusCode = 400 + return tokenPayload, exceptionReturn + } + + if !check.IsActive { + exceptionReturn.Message = "Can't log in. User is deactivated." + exceptionReturn.ErrorCode = "400106" + exceptionReturn.StatusCode = 400 + return tokenPayload, exceptionReturn + } + + if bcrypt.CompareHashAndPassword([]byte(check.Password), []byte(loginBody.Password)) != nil { + exceptionReturn.Message = "Incorrect password" + exceptionReturn.ErrorCode = "400104" + exceptionReturn.StatusCode = 400 + return tokenPayload, exceptionReturn + } + + token, err := CreateToken(check, loginBody.RememberMe) + common.CheckError(err) + + tokenPayload.Token = token + + tx.Commit() + + return tokenPayload, exceptionReturn +} + +/* +Deactivate + +Updates row in users table. + +IsActive column is set to false + + Args: + context.Context: Application context + *model.Auth: Authentication object + Returns: + *model.MessageResponse + *model.Exception +*/ +func (us *UserRepository) Deactivate(ctx context.Context, auth *model.Auth) (*model.MessageResponse, *model.Exception) { + db := us.db.WithContext(ctx) + + mm := new(model.MessageResponse) + me := new(model.Exception) + um := new(model.User) + + tx, _ := db.Begin() + defer tx.Rollback() + + err := tx.Model(um).Where("? = ?", pg.Ident("id"), auth.Id).Select() + + if err != nil { + me.ErrorCode = "404101" + me.Message = "User not found" + me.StatusCode = 404 + return mm, me + } + um.IsActive = false + _, err = tx.Model(um).Where("? = ?", pg.Ident("id"), auth.Id).Update() + + if err != nil { + me.ErrorCode = "400105" + me.Message = "Could not deactivate user" + me.StatusCode = 400 + return mm, me + } + + mm.Message = "User successfully deactivated." + + tx.Commit() + + return mm, me +} + +/* +CreateToken + +Generates new jwt token. + +It encodes the user id. Based on rememberMe it is valid through 48hours or 2hours. + + Args: + *model.User: User object to encode + bool: Should function generate longer lasting token (48hrs) + Returns: + string: Generated token + error: Error that occured in the process +*/ +func CreateToken(user *model.User, rememberMe bool) (string, error) { + atClaims := jwt.MapClaims{} + atClaims["authorized"] = true + atClaims["id"] = user.Id + if rememberMe { + atClaims["exp"] = time.Now().Add(time.Hour * 48).Unix() + } else { + atClaims["exp"] = time.Now().Add(time.Hour * 2).Unix() + } + + secret := os.Getenv("ACCESS_SECRET") + if secret == "" { + secret = configs.Secret + } + + at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims) + token, err := at.SignedString([]byte(secret)) + + return token, err +} diff --git a/pkg/services/wallets.go b/pkg/repository/wallet.go similarity index 56% rename from pkg/services/wallets.go rename to pkg/repository/wallet.go index fb99554..cf22d7c 100644 --- a/pkg/services/wallets.go +++ b/pkg/repository/wallet.go @@ -1,45 +1,45 @@ -package services +package repository import ( "context" "fmt" "time" - "wallet-api/pkg/models" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" "wallet-api/pkg/utl/common" "github.com/go-pg/pg/v10" ) -type WalletService struct { - Db *pg.DB - Ss *SubscriptionService +type WalletRepository struct { + db *pg.DB + subscriptionRepository *SubscriptionRepository +} + +func NewWalletRepository(db *pg.DB, ss *SubscriptionRepository) *WalletRepository { + return &WalletRepository{ + db: db, + subscriptionRepository: ss, + } } /* New Inserts row to wallets table. - Args: - context.Context: Application context - *models.NewWalletBody: Object to be inserted - Returns: - *models.Wallet: Wallet object from database. - *models.Exception: Exception payload. -*/ -func (as *WalletService) New(ctx context.Context, am *models.NewWalletBody) (*models.Wallet, *models.Exception) { - db := as.Db.WithContext(ctx) - exceptionReturn := new(models.Exception) - walletModel := new(models.Wallet) - walletModel.Init() - walletModel.UserID = am.UserID - walletModel.Name = am.Name + Args: + context.Context: Application context + *model.NewWalletBody: Object to be inserted + Returns: + *model.Wallet: Wallet object from database. + *model.Exception: Exception payload. +*/ +func (as *WalletRepository) New(ctx context.Context, walletModel *model.Wallet) (*model.Wallet, error) { + db := as.db.WithContext(ctx) _, err := db.Model(walletModel).Insert() if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400126" - exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"wallets\" table: %s", err) - return nil, exceptionReturn + return nil, err } return walletModel, nil } @@ -48,31 +48,24 @@ func (as *WalletService) New(ctx context.Context, am *models.NewWalletBody) (*mo Edit Updates row in wallets table by id. - Args: - context.Context: Application context - *models.WalletEdit: Object to be edited - string: id to search - Returns: - *models.Wallet: Wallet object from database. - *models.Exception: Exception payload. -*/ -func (as *WalletService) Edit(ctx context.Context, body *models.WalletEdit, id string) (*models.Wallet, *models.Exception) { - db := as.Db.WithContext(ctx) - exceptionReturn := new(models.Exception) - tm := new(models.Wallet) - tm.Id = id - tm.Name = body.Name + Args: + context.Context: Application context + *model.WalletEdit: Object to be edited + string: id to search + Returns: + *model.Wallet: Wallet object from database. + *model.Exception: Exception payload. +*/ +func (as *WalletRepository) Edit(ctx context.Context, tm *model.Wallet) (*model.Wallet, error) { + db := as.db.WithContext(ctx) tx, _ := db.Begin() defer tx.Rollback() _, err := tx.Model(tm).WherePK().UpdateNotZero() if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400127" - exceptionReturn.Message = fmt.Sprintf("Error updating row in \"wallets\" table: %s", err) - return nil, exceptionReturn + return nil, err } tx.Commit() @@ -84,31 +77,28 @@ func (as *WalletService) Edit(ctx context.Context, body *models.WalletEdit, id s Get Gets row in wallets table by id. - Args: - context.Context: Application context - string: id to search - *models.Params: url query parameters - Returns: - *models.Wallet: Wallet object from database - *models.Exception: Exception payload. -*/ -func (as *WalletService) Get(ctx context.Context, id string, params *models.Params) (*models.Wallet, *models.Exception) { - db := as.Db.WithContext(ctx) - exceptionReturn := new(models.Exception) - wm := new(models.Wallet) - wm.Id = id + Args: + context.Context: Application context + string: id to search + *model.Params: url query parameters + Returns: + *model.Wallet: Wallet object from database + *model.Exception: Exception payload. +*/ +func (as *WalletRepository) Get(ctx context.Context, flt *filter.WalletFilter) (*model.Wallet, error) { + db := as.db.WithContext(ctx) + + wm := new(model.Wallet) + wm.Id = flt.Id tx, _ := db.Begin() defer tx.Rollback() qry := tx.Model(wm) - err := common.GenerateEmbed(qry, params.Embed).WherePK().Select() + err := common.GenerateEmbed(qry, flt.Embed).WherePK().Select() if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400128" - exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"wallets\" table: %s", err) - return nil, exceptionReturn + return nil, err } tx.Commit() @@ -120,27 +110,28 @@ func (as *WalletService) Get(ctx context.Context, id string, params *models.Para GetAll Gets filtered rows from wallets table. - Args: - context.Context: Application context - *models.Auth: Authentication object - *models.FilteredResponse: filter options - Returns: - *models.Exception: Exception payload. -*/ -func (as *WalletService) GetAll(ctx context.Context, am *models.Auth, filtered *models.FilteredResponse) *models.Exception { - exceptionReturn := new(models.Exception) - db := as.Db.WithContext(ctx) - wm := new([]models.Wallet) - query := db.Model(wm).Where("? = ?", pg.Ident("user_id"), am.Id) - err := FilteredResponse(query, wm, filtered) + Args: + context.Context: Application context + *model.Auth: Authentication object + *model.FilteredResponse: filter options + Returns: + *model.Exception: Exception payload. +*/ +func (as *WalletRepository) GetAll(ctx context.Context, flt *filter.WalletFilter) (*model.FilteredResponse, *model.Exception) { + exceptionReturn := new(model.Exception) + db := as.db.WithContext(ctx) + wm := new([]model.Wallet) + + query := db.Model(wm).Where("? = ?", pg.Ident("user_id"), flt.UserId) + filtered, err := FilteredResponse(query, wm, flt.Params) if err != nil { exceptionReturn.StatusCode = 400 exceptionReturn.ErrorCode = "400134" exceptionReturn.Message = fmt.Sprintf("Error selecting rows in \"wallets\" table: %s", err) - return exceptionReturn + return nil, exceptionReturn } - return nil + return filtered, nil } /* @@ -149,23 +140,24 @@ GetHeader Gets row from wallets table. Calculates previous month, current and next month totals. - Args: - context.Context: Application context - *models.Auth: Authentication object - string: wallet id to search - Returns: - *models.WalletHeader: generated wallet header object - *models.Exception: Exception payload. -*/ -func (as *WalletService) GetHeader(ctx context.Context, am *models.Auth, walletId string) (*models.WalletHeader, *models.Exception) { - db := as.Db.WithContext(ctx) - wm := new(models.WalletHeader) - wallets := new([]models.WalletTransactions) - transactions := new([]models.Transaction) - subscriptions := new([]models.Subscription) - transactionStatus := new(models.TransactionStatus) - exceptionReturn := new(models.Exception) + Args: + context.Context: Application context + *model.Auth: Authentication object + string: wallet id to search + Returns: + *model.WalletHeader: generated wallet header object + *model.Exception: Exception payload. +*/ +func (as *WalletRepository) GetHeader(ctx context.Context, flt *filter.WalletHeaderFilter) (*model.WalletHeader, *model.Exception) { + db := as.db.WithContext(ctx) + + wm := new(model.WalletHeader) + wallets := new([]model.WalletTransactions) + transactions := new([]model.Transaction) + subscriptions := new([]model.Subscription) + transactionStatus := new(model.TransactionStatus) + exceptionReturn := new(model.Exception) tx, _ := db.Begin() defer tx.Rollback() @@ -177,9 +169,9 @@ func (as *WalletService) GetHeader(ctx context.Context, am *models.Auth, walletI exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactionStatuses\" table: %s", err) return nil, exceptionReturn } - query2 := tx.Model(subscriptions).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id).Relation("TransactionType").Relation("SubscriptionType") - if walletId != "" { - query2.Where("? = ?", pg.Ident("wallet_id"), walletId) + query2 := tx.Model(subscriptions).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), flt.UserId).Relation("TransactionType").Relation("SubscriptionType") + if flt.WalletId != "" { + query2.Where("? = ?", pg.Ident("wallet_id"), flt.WalletId) } query2.Select() if err != nil { @@ -198,9 +190,9 @@ func (as *WalletService) GetHeader(ctx context.Context, am *models.Auth, walletI firstOfNextMonth := time.Date(currentYear, currentMonth+1, 1, 0, 0, 0, 0, currentLocation) firstOfMonthAfterNext := time.Date(currentYear, currentMonth+2, 1, 0, 0, 0, 0, currentLocation) - query := tx.Model(transactions).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id).Relation("TransactionType") - if walletId != "" { - query.Where("? = ?", pg.Ident("wallet_id"), walletId) + query := tx.Model(transactions).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), flt.UserId).Relation("TransactionType") + if flt.WalletId != "" { + query.Where("? = ?", pg.Ident("wallet_id"), flt.WalletId) } query = query.Where("? = ?", pg.Ident("transaction_status_id"), transactionStatus.Id) query.Select() @@ -275,25 +267,31 @@ func (as *WalletService) GetHeader(ctx context.Context, am *models.Auth, walletI } wm.Currency = "USD" - wm.WalletId = walletId + wm.WalletId = flt.WalletId return wm, nil } +func (as *WalletRepository) CreateTx(ctx context.Context) (*pg.Tx, error) { + db := as.db.WithContext(ctx) + return db.Begin() +} + /* addWhere Appends Transaction to the belonging walletId. If missing, it creates the item list. - Args: - *[]models.WalletTransactions: list to append to - string: wallet id to check - models.Transaction: Transaction to append - Returns: - *models.Exception: Exception payload. + + Args: + *[]model.WalletTransactions: list to append to + string: wallet id to check + model.Transaction: Transaction to append + Returns: + *model.Exception: Exception payload. */ -func addWhere(s *[]models.WalletTransactions, walletId string, e models.Transaction) { +func addWhere(s *[]model.WalletTransactions, walletId string, e model.Transaction) { var exists bool for a := range *s { if (*s)[a].WalletId == walletId { @@ -302,7 +300,7 @@ func addWhere(s *[]models.WalletTransactions, walletId string, e models.Transact } } if !exists { - var walletTransaction models.WalletTransactions + var walletTransaction model.WalletTransactions walletTransaction.WalletId = walletId walletTransaction.Transactions = append(walletTransaction.Transactions, e) *s = append(*s, walletTransaction) diff --git a/pkg/service/api.go b/pkg/service/api.go new file mode 100644 index 0000000..fd2278d --- /dev/null +++ b/pkg/service/api.go @@ -0,0 +1,47 @@ +package service + +import ( + "context" + "wallet-api/pkg/model" + "wallet-api/pkg/repository" +) + +type ApiService struct { + repository *repository.ApiRepository +} + +func NewApiService(repository *repository.ApiRepository) *ApiService { + return &ApiService{ + repository: repository, + } +} + +/* +GetFirst + +Gets first row from API table. + + Args: + context.Context: Application context + Returns: + model.ApiModel: Api object from database. +*/ +func (as ApiService) GetFirst(ctx context.Context) model.ApiModel { + return as.repository.GetFirst(ctx) +} + +/* +PostMigrate + +Starts database migration. + + Args: + context.Context: Application context + string: Migration version + Returns: + *model.MessageResponse: Message response object. + *model.Exception: Exception response object. +*/ +func (as ApiService) PostMigrate(ctx context.Context, version string) []error { + return as.repository.PostMigrate(ctx, version) +} diff --git a/pkg/service/service.go b/pkg/service/service.go new file mode 100644 index 0000000..7eaa3cc --- /dev/null +++ b/pkg/service/service.go @@ -0,0 +1,28 @@ +package service + +import ( + "wallet-api/pkg/repository" + + "go.uber.org/dig" +) + +/* +InitializeServices + +Initializes Dependency Injection modules for services + + Args: + *dig.Container: Dig Container +*/ +func InitializeServices(c *dig.Container) { + repository.InitializeRepositories(c) + + c.Provide(NewApiService) + c.Provide(NewSubscriptionService) + c.Provide(NewSubscriptionTypeService) + c.Provide(NewTransactionService) + c.Provide(NewTransactionStatusService) + c.Provide(NewTransactionTypeService) + c.Provide(NewUserService) + c.Provide(NewWalletService) +} diff --git a/pkg/service/subscription.go b/pkg/service/subscription.go new file mode 100644 index 0000000..2f59401 --- /dev/null +++ b/pkg/service/subscription.go @@ -0,0 +1,161 @@ +package service + +import ( + "context" + "fmt" + "time" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/repository" +) + +type SubscriptionService struct { + repository *repository.SubscriptionRepository +} + +func NewSubscriptionService(repository *repository.SubscriptionRepository) *SubscriptionService { + return &SubscriptionService{ + repository: repository, + } +} + +/* +New + +Inserts new row to subscription table. + + Args: + context.Context: Application context + *model.NewSubscriptionBody: Request body + Returns: + *model.Subscription: Created Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionService) New(ctx context.Context, tm *model.Subscription) (*model.Subscription, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.New(ctx, tm) + + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400109" + exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"subscription\" table: %s", err) + return nil, exceptionReturn + } + return response, nil +} + +/* +Get + +Gets row from subscription table by id. + + Args: + context.Context: Application context + *model.Auth: Authentication model + string: subscription id to search + params: *model.Params + Returns: + *model.Subscription: Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionService) Get(ctx context.Context, flt filter.SubscriptionFilter) (*model.Subscription, *model.Exception) { + exceptionReturn := new(model.Exception) + response, err := as.repository.Get(ctx, flt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400129" + exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"subscription\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} + +/* +GetAll + +Gets filtered rows from subscription table. + + Args: + context.Context: Application context + *model.Auth: Authentication object + string: Wallet id to search + *model.FilteredResponse: filter options + Returns: + *model.Exception: Exception payload. +*/ +func (as *SubscriptionService) GetAll(ctx context.Context, flt *filter.SubscriptionFilter) (*model.FilteredResponse, *model.Exception) { + exceptionReturn := new(model.Exception) + + filtered, err := as.repository.GetAll(ctx, flt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400110" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"subscription\" table: %s", err) + return nil, exceptionReturn + } + + return filtered, nil +} + +/* +Edit + +Updates row from subscription table by id. + + Args: + context.Context: Application context + *model.SubscriptionEdit: Values to edit + string: id to search + Returns: + *model.Subscription: Edited Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionService) Edit(ctx context.Context, tm *model.Subscription) (*model.Subscription, *model.Exception) { + + exceptionReturn := new(model.Exception) + + response, err := as.repository.Edit(ctx, tm) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400111" + exceptionReturn.Message = fmt.Sprintf("Error updating row in \"subscription\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} + +/* +End + +Updates row in subscription table by id. + +Ends subscription with current date. + + Args: + context.Context: Application context + string: id to search + Returns: + *model.Subscription: Created Subscription row object from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionService) End(ctx context.Context, id string) (*model.Subscription, *model.Exception) { + exceptionReturn := new(model.Exception) + + tm := new(model.Subscription) + tm.Id = id + tm.EndDate = time.Now() + tm.HasEnd = true + + response, err := as.repository.End(ctx, tm) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400112" + exceptionReturn.Message = fmt.Sprintf("Error updating row in \"subscription\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} diff --git a/pkg/service/subscriptionType.go b/pkg/service/subscriptionType.go new file mode 100644 index 0000000..e1e27d8 --- /dev/null +++ b/pkg/service/subscriptionType.go @@ -0,0 +1,71 @@ +package service + +import ( + "context" + "fmt" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/repository" +) + +type SubscriptionTypeService struct { + repository *repository.SubscriptionTypeRepository +} + +func NewSubscriptionTypeService(repository *repository.SubscriptionTypeRepository) *SubscriptionTypeService { + return &SubscriptionTypeService{ + repository: repository, + } +} + +/* +New + +Inserts new row to subscription type table. + + Args: + context.Context: Application context + *model.NewSubscriptionTypeBody: Values to create new row + Returns: + *model.SubscriptionType: Created row from database. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionTypeService) New(ctx context.Context, tm *model.SubscriptionType) (*model.SubscriptionType, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.New(ctx, tm) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400114" + exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"subscriptionTypes\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} + +/* +GetAll + +Gets all rows from subscription type table. + + Args: + context.Context: Application context + string: Relations to embed + Returns: + *[]model.SubscriptionType: List of subscription type objects. + *model.Exception: Exception payload. +*/ +func (as *SubscriptionTypeService) GetAll(ctx context.Context, flt *filter.SubscriptionTypeFilter) (*[]model.SubscriptionType, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.GetAll(ctx, flt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400135" + exceptionReturn.Message = fmt.Sprintf("Error selecting rows in \"subscriptionTypes\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go new file mode 100644 index 0000000..1c27d12 --- /dev/null +++ b/pkg/service/transaction.go @@ -0,0 +1,207 @@ +package service + +import ( + "context" + "fmt" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/repository" + "wallet-api/pkg/utl/common" +) + +type TransactionService struct { + repository *repository.TransactionRepository + subscriptionRepository *repository.SubscriptionRepository + transactionStatusRepository *repository.TransactionStatusRepository +} + +func NewTransactionService(repository *repository.TransactionRepository, sr *repository.SubscriptionRepository, tsr *repository.TransactionStatusRepository) *TransactionService { + return &TransactionService{ + repository: repository, + subscriptionRepository: sr, + transactionStatusRepository: tsr, + } +} + +/* +New row into transaction table + +Inserts + + Args: + context.Context: Application context + *model.NewTransactionBody: Transaction body object + Returns: + *model.Transaction: Transaction object + *model.Exception: Exception payload. +*/ +func (as *TransactionService) New(ctx context.Context, tm *model.Transaction) (*model.Transaction, *model.Exception) { + exceptionReturn := new(model.Exception) + + tx, err := as.repository.CreateTx(ctx) + defer tx.Rollback() + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400136" + exceptionReturn.Message = fmt.Sprintf("Error beginning transaction: %s", err) + return nil, exceptionReturn + } + + tsFlt := filter.NewTransactionStatusFilter(model.Params{}) + tsFlt.Status = "completed" + transactionStatuses, err := as.transactionStatusRepository.GetAll(ctx, tsFlt, tx) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400138" + exceptionReturn.Message = fmt.Sprintf("Error fetching transactionStatus: %s", err) + return nil, exceptionReturn + } + + var transactionStatus = common.Find[model.TransactionStatus](transactionStatuses, func(status *model.TransactionStatus) bool { + return status.Status == "completed" + }) + tm.TransactionStatusID = transactionStatus.Id + + response, err := as.repository.New(ctx, tm, tx) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400116" + exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"transaction\" table: %s", err) + return nil, exceptionReturn + } + tx.Commit() + + return response, nil +} + +/* +GetAll + +Gets all rows from subscription type table. + Args: + context.Context: Application context + string: Relations to embed + Returns: + *model.Exception: Exception payload. +*/ +// Gets filtered rows from transaction table. +func (as *TransactionService) GetAll(ctx context.Context, flt *filter.TransactionFilter) (*model.FilteredResponse, *model.Exception) { + exceptionReturn := new(model.Exception) + + filtered, err := as.repository.GetAll(ctx, flt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400118" + exceptionReturn.Message = fmt.Sprintf("Error selecting row(s) in \"transaction\" table: %s", err) + return nil, exceptionReturn + } + return filtered, nil +} + +/* +Check + +Checks subscriptions and create transactions. + Args: + context.Context: Application context + string: Relations to embed + Returns: + *model.Exception: Exception payload. +*/ +// Gets filtered rows from transaction table. +func (as *TransactionService) Check(ctx context.Context, flt *filter.TransactionFilter) (*model.FilteredResponse, *model.Exception) { + exceptionReturn := new(model.Exception) + + filtered, err := as.repository.Check(ctx, flt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400120" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transaction\" table: %s", err) + return nil, exceptionReturn + } + return filtered, nil +} + +/* +Edit + +Updates row in transaction table by id. + + Args: + context.Context: Application context + *model.TransactionEdit: Object to edit + string: id to search + Returns: + *model.Transaction: Transaction object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionService) Edit(ctx context.Context, tm *model.Transaction) (*model.Transaction, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.Edit(ctx, tm) + + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400107" + exceptionReturn.Message = fmt.Sprintf("Error updating row in \"transaction\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} + +/* +BulkEdit + +Updates row in transaction table by id. + + Args: + context.Context: Application context + ?[]model.Transaction Bulk Edit: Object to edit + string: id to search + Returns: + *model.Transaction: Transaction object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionService) BulkEdit(ctx context.Context, transactions *[]model.Transaction) (*[]model.Transaction, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.BulkEdit(ctx, transactions) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400121" + exceptionReturn.Message = fmt.Sprintf("Error updating rows in \"transactions\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} + +/* +Get + +Gets row from transaction table by id. + + Args: + context.Context: Application context + *model.Auth: Authentication object + string: id to search + *model.Params: url query parameters + Returns: + *model.Transaction: Transaction object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionService) Get(ctx context.Context, flt *filter.TransactionFilter) (*model.Transaction, *model.Exception) { + + exceptionReturn := new(model.Exception) + + response, err := as.repository.Get(ctx, flt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400122" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactions\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} diff --git a/pkg/service/transactionStatus.go b/pkg/service/transactionStatus.go new file mode 100644 index 0000000..e0606c8 --- /dev/null +++ b/pkg/service/transactionStatus.go @@ -0,0 +1,91 @@ +package service + +import ( + "context" + "fmt" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/repository" +) + +type TransactionStatusService struct { + repository *repository.TransactionStatusRepository +} + +func NewTransactionStatusService(repository *repository.TransactionStatusRepository) *TransactionStatusService { + return &TransactionStatusService{ + repository: repository, + } +} + +/* +New + +Inserts new row to transaction status table. + + Args: + context.Context: Application context + *model.NewTransactionStatusBody: object to create + Returns: + *model.TransactionType: Transaction Type object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionStatusService) New(ctx context.Context, tm *model.TransactionStatus) (*model.TransactionStatus, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.New(ctx, tm) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400123" + exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"transactionStatus\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} + +/* +GetAll + +Gets all rows from transaction status table. + + Args: + context.Context: Application context + string: Relations to embed + Returns: + *[]model.TransactionStatus: List of Transaction status objects from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionStatusService) GetAll(ctx context.Context, flt *filter.TransactionStatusFilter) (*[]model.TransactionStatus, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.GetAll(ctx, flt, nil) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400124" + exceptionReturn.Message = fmt.Sprintf("Error selecting rows in \"transactionStatus\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} + +func (as *TransactionStatusService) Get(ctx context.Context, flt *filter.TransactionStatusFilter) (*model.TransactionStatus, *model.Exception) { + transactionStatus := new(model.TransactionStatus) + exceptionReturn := new(model.Exception) + if flt.Id != "" { + transactionStatus.Id = flt.Id + } + if flt.Status != "" { + transactionStatus.Status = flt.Status + } + response, err := as.repository.Get(ctx, flt, nil) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400129" + exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"subscription\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} diff --git a/pkg/service/transactionType.go b/pkg/service/transactionType.go new file mode 100644 index 0000000..447df56 --- /dev/null +++ b/pkg/service/transactionType.go @@ -0,0 +1,71 @@ +package service + +import ( + "context" + "fmt" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/repository" +) + +type TransactionTypeService struct { + repository *repository.TransactionTypeRepository +} + +func NewTransactionTypeService(repository *repository.TransactionTypeRepository) *TransactionTypeService { + return &TransactionTypeService{ + repository: repository, + } +} + +/* +New + +Inserts new row to transaction type table. + + Args: + context.Context: Application context + *model.NewTransactionTypeBody: object to create + Returns: + *model.TransactionType: Transaction Type object from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionTypeService) New(ctx context.Context, tm *model.TransactionType) (*model.TransactionType, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.New(ctx, tm) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400125" + exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"transactionTypes\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} + +/* +GetAll + +Gets all rows from transaction type table. + + Args: + context.Context: Application context + string: Relations to embed + Returns: + *[]model.TransactionType: List of Transaction type objects from database. + *model.Exception: Exception payload. +*/ +func (as *TransactionTypeService) GetAll(ctx context.Context, flt *filter.TransactionTypeFilter) (*[]model.TransactionType, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.GetAll(ctx, flt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400133" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactionTypes\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} diff --git a/pkg/services/users.go b/pkg/service/user.go similarity index 50% rename from pkg/services/users.go rename to pkg/service/user.go index 27590ec..e448451 100644 --- a/pkg/services/users.go +++ b/pkg/service/user.go @@ -1,90 +1,109 @@ -package services +package service import ( "context" "os" "time" - "wallet-api/pkg/models" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/repository" "wallet-api/pkg/utl/common" "wallet-api/pkg/utl/configs" jwt "github.com/dgrijalva/jwt-go" "golang.org/x/crypto/bcrypt" - - "github.com/go-pg/pg/v10" ) -type UsersService struct { - Db *pg.DB +type UserService struct { + repository *repository.UserRepository +} + +func NewUserService(repository *repository.UserRepository) *UserService { + return &UserService{ + repository: repository, + } } /* Create Inserts new row to users table. - Args: - context.Context: Application context - *models.User: User object to create - Returns: - *models.User: User object from database - *models.Exception + + Args: + context.Context: Application context + *model.User: User object to create + Returns: + *model.User: User object from database + *model.Exception */ -func (us *UsersService) Create(ctx context.Context, registerBody *models.User) (*models.User, *models.Exception) { - db := us.Db.WithContext(ctx) +func (us *UserService) Create(ctx context.Context, registerBody *model.User) (*model.User, *model.Exception) { + exceptionReturn := new(model.Exception) - check := new(models.User) - exceptionReturn := new(models.Exception) - - tx, _ := db.Begin() + tx, _ := us.repository.CreateTx(ctx) defer tx.Rollback() - tx.Model(check).Where("? = ?", pg.Ident("username"), registerBody.Username).WhereOr("? = ?", pg.Ident("email"), registerBody.Email).Select() + check, err := us.repository.Check(ctx, tx, registerBody) + if err != nil { + exceptionReturn.Message = "Error checking user" + exceptionReturn.ErrorCode = "400139" + exceptionReturn.StatusCode = 400 + return nil, exceptionReturn + } + if check.Username != "" || check.Email != "" { exceptionReturn.Message = "User already exists" exceptionReturn.ErrorCode = "400101" exceptionReturn.StatusCode = 400 - return check, exceptionReturn + return nil, exceptionReturn } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(registerBody.Password), bcrypt.DefaultCost) common.CheckError(err) registerBody.Password = string(hashedPassword) - _, err = tx.Model(registerBody).Insert() - + us.repository.Create(ctx, tx, registerBody) if err != nil { exceptionReturn.Message = "Error creating user" exceptionReturn.ErrorCode = "400102" exceptionReturn.StatusCode = 400 + return nil, exceptionReturn } tx.Commit() - return registerBody, exceptionReturn + return registerBody, nil } /* Login Gets row from users table by email and valid password. - Args: - context.Context: Application context - *models.Login: object to search - Returns: - *models.Token: new session token - *models.Exception + + Args: + context.Context: Application context + *model.Login: object to search + Returns: + *model.Token: new session token + *model.Exception */ -func (us *UsersService) Login(ctx context.Context, loginBody *models.Login) (*models.Token, *models.Exception) { - db := us.Db.WithContext(ctx) +func (us *UserService) Login(ctx context.Context, body *model.Login) (*model.Token, *model.Exception) { + exceptionReturn := new(model.Exception) + tokenPayload := new(model.Token) + loginBody := new(model.User) - check := new(models.User) - exceptionReturn := new(models.Exception) - tokenPayload := new(models.Token) + loginBody.Email = body.Email + loginBody.Username = body.Email - tx, _ := db.Begin() + tx, _ := us.repository.CreateTx(ctx) defer tx.Rollback() - tx.Model(check).Where("? = ?", pg.Ident("email"), loginBody.Email).Select() + check, err := us.repository.Check(ctx, tx, loginBody) + if err != nil { + exceptionReturn.Message = "Error checking user" + exceptionReturn.ErrorCode = "400139" + exceptionReturn.StatusCode = 400 + return nil, exceptionReturn + } if check.Email == "" { exceptionReturn.Message = "Email not found" @@ -100,20 +119,18 @@ func (us *UsersService) Login(ctx context.Context, loginBody *models.Login) (*mo return tokenPayload, exceptionReturn } - if bcrypt.CompareHashAndPassword([]byte(check.Password), []byte(loginBody.Password)) != nil { + if bcrypt.CompareHashAndPassword([]byte(check.Password), []byte(body.Password)) != nil { exceptionReturn.Message = "Incorrect password" exceptionReturn.ErrorCode = "400104" exceptionReturn.StatusCode = 400 return tokenPayload, exceptionReturn } - token, err := CreateToken(check, loginBody.RememberMe) + token, err := CreateToken(check, body.RememberMe) common.CheckError(err) tokenPayload.Token = token - tx.Commit() - return tokenPayload, exceptionReturn } @@ -123,46 +140,43 @@ Deactivate Updates row in users table. IsActive column is set to false - Args: - context.Context: Application context - *models.Auth: Authentication object - Returns: - *models.MessageResponse - *models.Exception + + Args: + context.Context: Application context + *model.Auth: Authentication object + Returns: + *model.MessageResponse + *model.Exception */ -func (us *UsersService) Deactivate(ctx context.Context, auth *models.Auth) (*models.MessageResponse, *models.Exception) { - db := us.Db.WithContext(ctx) +func (us *UserService) Deactivate(ctx context.Context, flt *filter.UserFilter) (*model.MessageResponse, *model.Exception) { + mm := new(model.MessageResponse) + me := new(model.Exception) - mm := new(models.MessageResponse) - me := new(models.Exception) - um := new(models.User) - - tx, _ := db.Begin() + tx, _ := us.repository.CreateTx(ctx) defer tx.Rollback() - err := tx.Model(um).Where("? = ?", pg.Ident("id"), auth.Id).Select() - + um, err := us.repository.Get(ctx, flt, tx) if err != nil { me.ErrorCode = "404101" me.Message = "User not found" me.StatusCode = 404 - return mm, me + return nil, me } - um.IsActive = false - _, err = tx.Model(um).Where("? = ?", pg.Ident("id"), auth.Id).Update() + um.IsActive = false + _, err = us.repository.Edit(ctx, um, tx) if err != nil { me.ErrorCode = "400105" me.Message = "Could not deactivate user" me.StatusCode = 400 - return mm, me + return nil, me } mm.Message = "User successfully deactivated." tx.Commit() - return mm, me + return mm, nil } /* @@ -171,14 +185,15 @@ CreateToken Generates new jwt token. It encodes the user id. Based on rememberMe it is valid through 48hours or 2hours. - Args: - *models.User: User object to encode - bool: Should function generate longer lasting token (48hrs) - Returns: - string: Generated token - error: Error that occured in the process + + Args: + *model.User: User object to encode + bool: Should function generate longer lasting token (48hrs) + Returns: + string: Generated token + error: Error that occured in the process */ -func CreateToken(user *models.User, rememberMe bool) (string, error) { +func CreateToken(user *model.User, rememberMe bool) (string, error) { atClaims := jwt.MapClaims{} atClaims["authorized"] = true atClaims["id"] = user.Id diff --git a/pkg/service/wallet.go b/pkg/service/wallet.go new file mode 100644 index 0000000..242d3ac --- /dev/null +++ b/pkg/service/wallet.go @@ -0,0 +1,297 @@ +package service + +import ( + "context" + "fmt" + "time" + "wallet-api/pkg/filter" + "wallet-api/pkg/model" + "wallet-api/pkg/repository" +) + +type WalletService struct { + repository *repository.WalletRepository + subscriptionRepository *repository.SubscriptionRepository + transactionStatusRepository *repository.TransactionStatusRepository + transactionRepository *repository.TransactionRepository +} + +func NewWalletService(repository *repository.WalletRepository, subscriptionRepository *repository.SubscriptionRepository, transactionStatusRepository *repository.TransactionStatusRepository, transactionRepository *repository.TransactionRepository) *WalletService { + return &WalletService{ + repository: repository, + subscriptionRepository: subscriptionRepository, + transactionStatusRepository: transactionStatusRepository, + transactionRepository: transactionRepository, + } +} + +/* +New + +Inserts row to wallets table. + + Args: + context.Context: Application context + *model.NewWalletBody: Object to be inserted + Returns: + *model.Wallet: Wallet object from database. + *model.Exception: Exception payload. +*/ +func (as *WalletService) New(ctx context.Context, am *model.NewWalletBody) (*model.Wallet, *model.Exception) { + exceptionReturn := new(model.Exception) + walletModel := new(model.Wallet) + walletModel.Init() + walletModel.UserID = am.UserID + walletModel.Name = am.Name + response, err := as.repository.New(ctx, walletModel) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400126" + exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"wallets\" table: %s", err) + return nil, exceptionReturn + } + return response, nil +} + +/* +Edit + +Updates row in wallets table by id. + + Args: + context.Context: Application context + *model.WalletEdit: Object to be edited + string: id to search + Returns: + *model.Wallet: Wallet object from database. + *model.Exception: Exception payload. +*/ +func (as *WalletService) Edit(ctx context.Context, tm *model.Wallet) (*model.Wallet, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.Edit(ctx, tm) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400127" + exceptionReturn.Message = fmt.Sprintf("Error updating row in \"wallets\" table: %s", err) + return nil, exceptionReturn + } + return response, nil +} + +/* +Get + +Gets row in wallets table by id. + + Args: + context.Context: Application context + string: id to search + *model.Params: url query parameters + Returns: + *model.Wallet: Wallet object from database + *model.Exception: Exception payload. +*/ +func (as *WalletService) Get(ctx context.Context, flt *filter.WalletFilter) (*model.Wallet, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.Get(ctx, flt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400128" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"wallets\" table: %s", err) + return nil, exceptionReturn + } + + return response, nil +} + +/* +GetAll + +Gets filtered rows from wallets table. + + Args: + context.Context: Application context + *model.Auth: Authentication object + *model.FilteredResponse: filter options + Returns: + *model.Exception: Exception payload. +*/ +func (as *WalletService) GetAll(ctx context.Context, flt *filter.WalletFilter) (*model.FilteredResponse, *model.Exception) { + exceptionReturn := new(model.Exception) + + response, err := as.repository.GetAll(ctx, flt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400134" + exceptionReturn.Message = fmt.Sprintf("Error selecting rows in \"wallets\" table: %s", err) + return nil, exceptionReturn + } + return response, nil +} + +/* +GetHeader + +Gets row from wallets table. + +Calculates previous month, current and next month totals. + + Args: + context.Context: Application context + *model.Auth: Authentication object + string: wallet id to search + Returns: + *model.WalletHeader: generated wallet header object + *model.Exception: Exception payload. +*/ +func (as *WalletService) GetHeader(ctx context.Context, flt *filter.WalletHeaderFilter) (*model.WalletHeader, *model.Exception) { + wm := new(model.WalletHeader) + wallets := new([]model.WalletTransactions) + transactions := new([]model.Transaction) + subscriptions := new([]model.Subscription) + exceptionReturn := new(model.Exception) + + tx, _ := as.repository.CreateTx(ctx) + defer tx.Rollback() + + trStFlt := filter.NewTransactionStatusFilter(model.Params{}) + trStFlt.Status = "completed" + transactionStatuses, err := as.transactionStatusRepository.GetAll(ctx, trStFlt, tx) + transactionStatus := (*transactionStatuses)[0] + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400130" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactionStatuses\" table: %s", err) + return nil, exceptionReturn + } + + subFlt := filter.NewSubscriptionFilter(model.Params{Embed: "TransactionType,SubscriptionType"}) + subFlt.WalletId = flt.WalletId + subscriptions, err = as.subscriptionRepository.GetAllTx(tx, subFlt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400131" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"subscriptions\" table: %s", err) + return nil, exceptionReturn + } + + now := time.Now() + + currentYear, currentMonth, _ := now.Date() + currentLocation := now.Location() + + firstOfMonth := time.Date(currentYear, currentMonth, 1, 0, 0, 0, 0, currentLocation) + firstOfNextMonth := time.Date(currentYear, currentMonth+1, 1, 0, 0, 0, 0, currentLocation) + firstOfMonthAfterNext := time.Date(currentYear, currentMonth+2, 1, 0, 0, 0, 0, currentLocation) + + trFlt := filter.NewTransactionFilter(model.Params{Embed: "TransactionType,Wallet"}) + trFlt.WalletId = flt.WalletId + trFlt.TransactionStatusId = transactionStatus.Id + transactions, err = as.transactionRepository.GetAllTx(tx, trFlt) + if err != nil { + exceptionReturn.StatusCode = 400 + exceptionReturn.ErrorCode = "400132" + exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactions\" table: %s", err) + return nil, exceptionReturn + } + tx.Commit() + + for _, sub := range *subscriptions { + stopDate := firstOfMonthAfterNext + if sub.HasEnd && sub.EndDate.Before(firstOfMonthAfterNext) { + stopDate = sub.EndDate + } + startDate := sub.StartDate + for startDate.Before(stopDate) { + trans := sub.ToTrans() + trans.TransactionDate = startDate + if startDate.After(firstOfNextMonth) || startDate.Equal(firstOfNextMonth) { + *transactions = append(*transactions, *trans) + } + if sub.SubscriptionType.Type == "monthly" { + startDate = startDate.AddDate(0, sub.CustomRange, 0) + } else if sub.SubscriptionType.Type == "weekly" { + startDate = startDate.AddDate(0, 0, 7*sub.CustomRange) + } else if sub.SubscriptionType.Type == "daily" { + startDate = startDate.AddDate(0, 0, sub.CustomRange) + } else { + startDate = startDate.AddDate(sub.CustomRange, 0, 0) + } + } + } + + for _, trans := range *transactions { + addWhere(wallets, trans.WalletID, trans) + } + + for i, wallet := range *wallets { + for _, trans := range wallet.Transactions { + // tzFirstOfMonthAfterNext := firstOfMonthAfterNext.In(trans.TransactionDate.Location()) + // tzFirstOfNextMonth := firstOfNextMonth.In(trans.TransactionDate.Location()) + // tzFirstOfMonth := firstOfMonth.In(trans.TransactionDate.Location()) + if trans.TransactionDate.Before(firstOfNextMonth) && trans.TransactionDate.After(firstOfMonth) || trans.TransactionDate.Equal(firstOfMonth) { + if trans.TransactionType.Type == "expense" { + (*wallets)[i].CurrentBalance -= trans.Amount + } else { + (*wallets)[i].CurrentBalance += trans.Amount + } + } else if trans.TransactionDate.Before(firstOfMonthAfterNext) && trans.TransactionDate.After(firstOfNextMonth) { + if trans.TransactionType.Type == "expense" { + (*wallets)[i].NextMonth -= trans.Amount + } else { + (*wallets)[i].NextMonth += trans.Amount + } + } else if trans.TransactionDate.Before(firstOfMonth) { + if trans.TransactionType.Type == "expense" { + (*wallets)[i].LastMonth -= trans.Amount + } else { + (*wallets)[i].LastMonth += trans.Amount + } + } + + } + } + + for _, wallet := range *wallets { + wm.LastMonth += wallet.LastMonth + wm.CurrentBalance += wallet.CurrentBalance + wallet.LastMonth + wm.NextMonth += wallet.NextMonth + wallet.CurrentBalance + wallet.LastMonth + } + + wm.Currency = "USD" + wm.WalletId = flt.WalletId + + return wm, nil +} + +/* +addWhere + +Appends Transaction to the belonging walletId. + +If missing, it creates the item list. + + Args: + *[]model.WalletTransactions: list to append to + string: wallet id to check + model.Transaction: Transaction to append + Returns: + *model.Exception: Exception payload. +*/ +func addWhere(s *[]model.WalletTransactions, walletId string, e model.Transaction) { + var exists bool + for a := range *s { + if (*s)[a].WalletId == walletId { + (*s)[a].Transactions = append((*s)[a].Transactions, e) + exists = true + } + } + if !exists { + var walletTransaction model.WalletTransactions + walletTransaction.WalletId = walletId + walletTransaction.Transactions = append(walletTransaction.Transactions, e) + *s = append(*s, walletTransaction) + } +} diff --git a/pkg/services/api.go b/pkg/services/api.go deleted file mode 100644 index 16f42b7..0000000 --- a/pkg/services/api.go +++ /dev/null @@ -1,52 +0,0 @@ -package services - -import ( - "context" - "wallet-api/pkg/migrate" - "wallet-api/pkg/models" - - "github.com/go-pg/pg/v10" -) - -type ApiService struct { - Db *pg.DB -} - -/* -GetFirst - -Gets first row from API table. - Args: - context.Context: Application context - Returns: - models.ApiModel: Api object from database. -*/ -func (as *ApiService) GetFirst(ctx context.Context) models.ApiModel { - db := as.Db.WithContext(ctx) - - apiModel := models.ApiModel{Api: "Works"} - db.Model(&apiModel).First() - return apiModel -} - -/* -PostMigrate - -Starts database migration. - Args: - context.Context: Application context - string: Migration version - Returns: - *models.MessageResponse: Message response object. - *models.Exception: Exception response object. -*/ -func (as *ApiService) PostMigrate(ctx context.Context, version string) (*models.MessageResponse, *models.Exception) { - db := as.Db.WithContext(ctx) - - mr := new(models.MessageResponse) - er := new(models.Exception) - - migrate.Start(db, version) - - return mr, er -} diff --git a/pkg/services/services.go b/pkg/services/services.go deleted file mode 100644 index c87a50f..0000000 --- a/pkg/services/services.go +++ /dev/null @@ -1,38 +0,0 @@ -package services - -import ( - "wallet-api/pkg/models" - "wallet-api/pkg/utl/common" - - "github.com/go-pg/pg/v10" -) - -/* -FilteredResponse - -Adds filters to query and executes it. - Args: - *pg.Query: postgres query - interface{}: model to be mapped from query execution. - *models.FilteredResponse: filter options. -*/ -func FilteredResponse(qry *pg.Query, mdl interface{}, filtered *models.FilteredResponse) error { - if filtered.Page == 0 { - filtered.Page = 1 - } - if filtered.Rpp == 0 { - filtered.Rpp = 20 - } - if filtered.SortBy == "" { - filtered.SortBy = "date_created DESC" - } - qry = qry.Limit(filtered.Rpp).Offset((filtered.Page - 1) * filtered.Rpp).Order(filtered.SortBy) - common.GenerateEmbed(qry, filtered.Embed) - count, err := qry.SelectAndCount() - common.CheckError(err) - - filtered.TotalRecords = count - filtered.Items = mdl - - return err -} diff --git a/pkg/services/subscriptionTypes.go b/pkg/services/subscriptionTypes.go deleted file mode 100644 index eb637d7..0000000 --- a/pkg/services/subscriptionTypes.go +++ /dev/null @@ -1,75 +0,0 @@ -package services - -import ( - "context" - "fmt" - "wallet-api/pkg/models" - "wallet-api/pkg/utl/common" - - "github.com/go-pg/pg/v10" -) - -type SubscriptionTypeService struct { - Db *pg.DB -} - -/* -New - -Inserts new row to subscription type table. - Args: - context.Context: Application context - *models.NewSubscriptionTypeBody: Values to create new row - Returns: - *models.SubscriptionType: Created row from database. - *models.Exception: Exception payload. -*/ -func (as *SubscriptionTypeService) New(ctx context.Context, body *models.NewSubscriptionTypeBody) (*models.SubscriptionType, *models.Exception) { - db := as.Db.WithContext(ctx) - - tm := new(models.SubscriptionType) - exceptionReturn := new(models.Exception) - - tm.Init() - tm.Name = body.Name - tm.Type = body.Type - - _, err := db.Model(tm).Insert() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400114" - exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"subscriptionTypes\" table: %s", err) - return nil, exceptionReturn - } - - return tm, nil -} - -/* -GetAll - -Gets all rows from subscription type table. - Args: - context.Context: Application context - string: Relations to embed - Returns: - *[]models.SubscriptionType: List of subscription type objects. - *models.Exception: Exception payload. -*/ -func (as *SubscriptionTypeService) GetAll(ctx context.Context, embed string) (*[]models.SubscriptionType, *models.Exception) { - db := as.Db.WithContext(ctx) - - wm := new([]models.SubscriptionType) - exceptionReturn := new(models.Exception) - - query := db.Model(wm) - err := common.GenerateEmbed(query, embed).Select() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400135" - exceptionReturn.Message = fmt.Sprintf("Error selecting rows in \"subscriptionTypes\" table: %s", err) - return nil, exceptionReturn - } - - return wm, nil -} diff --git a/pkg/services/subscriptions.go b/pkg/services/subscriptions.go deleted file mode 100644 index a1ce86f..0000000 --- a/pkg/services/subscriptions.go +++ /dev/null @@ -1,294 +0,0 @@ -package services - -import ( - "context" - "fmt" - "math" - "time" - "wallet-api/pkg/models" - "wallet-api/pkg/utl/common" - - "github.com/go-pg/pg/v10" -) - -type SubscriptionService struct { - Db *pg.DB -} - -/* -New - -Inserts new row to subscription table. - Args: - context.Context: Application context - *models.NewSubscriptionBody: Request body - Returns: - *models.Subscription: Created Subscription row object from database. - *models.Exception: Exception payload. -*/ -func (as *SubscriptionService) New(ctx context.Context, body *models.NewSubscriptionBody) (*models.Subscription, *models.Exception) { - db := as.Db.WithContext(ctx) - - tm := new(models.Subscription) - exceptionReturn := new(models.Exception) - - amount, _ := body.Amount.Float64() - customRange, _ := body.CustomRange.Int64() - - tm.Init() - tm.WalletID = body.WalletID - tm.TransactionTypeID = body.TransactionTypeID - tm.SubscriptionTypeID = body.SubscriptionTypeID - tm.CustomRange = int(customRange) - tm.Description = body.Description - tm.StartDate = body.StartDate - tm.HasEnd = body.HasEnd - tm.EndDate = body.EndDate - tm.Amount = float32(math.Round(amount*100) / 100) - - if body.StartDate.IsZero() { - tm.StartDate = time.Now() - } - - tx, _ := db.Begin() - defer tx.Rollback() - - _, err := tx.Model(tm).Insert() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400109" - exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"subscription\" table: %s", err) - return nil, exceptionReturn - } - tx.Commit() - - return tm, nil -} - -/* -Get - -Gets row from subscription table by id. - Args: - context.Context: Application context - *models.Auth: Authentication model - string: subscription id to search - params: *models.Params - Returns: - *models.Subscription: Subscription row object from database. - *models.Exception: Exception payload. -*/ -func (as *SubscriptionService) Get(ctx context.Context, am *models.Auth, id string, params *models.Params) (*models.Subscription, *models.Exception) { - db := as.Db.WithContext(ctx) - - exceptionReturn := new(models.Exception) - wm := new(models.Subscription) - wm.Id = id - - tx, _ := db.Begin() - defer tx.Rollback() - - qry := tx.Model(wm) - err := common.GenerateEmbed(qry, params.Embed).WherePK().Select() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400129" - exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"subscription\" table: %s", err) - return nil, exceptionReturn - } - - tx.Commit() - - return wm, nil -} - -/* -GetAll - -Gets filtered rows from subscription table. - Args: - context.Context: Application context - *models.Auth: Authentication object - string: Wallet id to search - *models.FilteredResponse: filter options - Returns: - *models.Exception: Exception payload. -*/ -func (as *SubscriptionService) GetAll(ctx context.Context, am *models.Auth, walletId string, filtered *models.FilteredResponse) *models.Exception { - db := as.Db.WithContext(ctx) - - wm := new([]models.Subscription) - exceptionReturn := new(models.Exception) - - tx, _ := db.Begin() - defer tx.Rollback() - - query := tx.Model(wm).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id) - if walletId != "" { - query = query.Where("? = ?", pg.Ident("wallet_id"), walletId) - } - - err := FilteredResponse(query, wm, filtered) - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400110" - exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"subscription\" table: %s", err) - return exceptionReturn - } - tx.Commit() - - return nil -} - -/* -Edit - -Updates row from subscription table by id. - Args: - context.Context: Application context - *models.SubscriptionEdit: Values to edit - string: id to search - Returns: - *models.Subscription: Edited Subscription row object from database. - *models.Exception: Exception payload. -*/ -func (as *SubscriptionService) Edit(ctx context.Context, body *models.SubscriptionEdit, id string) (*models.Subscription, *models.Exception) { - db := as.Db.WithContext(ctx) - - amount, _ := body.Amount.Float64() - exceptionReturn := new(models.Exception) - - tm := new(models.Subscription) - tm.Id = id - tm.EndDate = body.EndDate - tm.HasEnd = body.HasEnd - tm.Description = body.Description - tm.WalletID = body.WalletID - tm.Amount = float32(math.Round(amount*100) / 100) - - tx, _ := db.Begin() - defer tx.Rollback() - - _, err := tx.Model(tm).WherePK().UpdateNotZero() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400111" - exceptionReturn.Message = fmt.Sprintf("Error updating row in \"subscription\" table: %s", err) - return nil, exceptionReturn - } - - tx.Commit() - - return tm, nil -} - -/* -End - -Updates row in subscription table by id. - -Ends subscription with current date. - Args: - context.Context: Application context - string: id to search - Returns: - *models.Subscription: Created Subscription row object from database. - *models.Exception: Exception payload. -*/ -func (as *SubscriptionService) End(ctx context.Context, id string) (*models.Subscription, *models.Exception) { - db := as.Db.WithContext(ctx) - exceptionReturn := new(models.Exception) - - tm := new(models.Subscription) - tm.Id = id - tm.EndDate = time.Now() - tm.HasEnd = true - - tx, _ := db.Begin() - defer tx.Rollback() - - _, err := tx.Model(tm).WherePK().UpdateNotZero() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400112" - exceptionReturn.Message = fmt.Sprintf("Error updating row in \"subscription\" table: %s", err) - return nil, exceptionReturn - } - - tx.Commit() - - return tm, nil -} - -/* -SubToTrans - -Generates and Inserts new Transaction rows from the subscription model. - Args: - *models.Subscription: Subscription model to generate new transactions from - *pg.Tx: Postgres query context - Returns: - *models.Exception: Exception payload. -*/ -func (as *SubscriptionService) SubToTrans(subModel *models.Subscription, tx *pg.Tx) *models.Exception { - exceptionReturn := new(models.Exception) - - now := time.Now() - - currentYear, currentMonth, _ := now.Date() - currentLocation := now.Location() - - transactionStatus := new(models.TransactionStatus) - firstOfNextMonth := time.Date(currentYear, currentMonth+1, 1, 0, 0, 0, 0, currentLocation) - tx.Model(transactionStatus).Where("? = ?", pg.Ident("status"), "pending").Select() - //tzFirstOfNextMonth := firstOfNextMonth.In(subModel.StartDate.Location()) - - startDate := subModel.StartDate - stopDate := firstOfNextMonth - if subModel.HasEnd && subModel.EndDate.Before(firstOfNextMonth) { - stopDate = subModel.EndDate - } - - transactions := new([]models.Transaction) - - if subModel.SubscriptionType == nil { - st := new(models.SubscriptionType) - tx.Model(st).Where("? = ?", pg.Ident("id"), subModel.SubscriptionTypeID).Select() - subModel.SubscriptionType = st - } - - for startDate.Before(stopDate) { - trans := subModel.ToTrans() - trans.TransactionDate = startDate - trans.TransactionStatusID = transactionStatus.Id - if startDate.After(subModel.LastTransactionDate) && (startDate.Before(now) || startDate.Equal(now)) { - *transactions = append(*transactions, *trans) - } - if subModel.SubscriptionType.Type == "monthly" { - startDate = startDate.AddDate(0, subModel.CustomRange, 0) - } else if subModel.SubscriptionType.Type == "weekly" { - startDate = startDate.AddDate(0, 0, 7*subModel.CustomRange) - } else if subModel.SubscriptionType.Type == "daily" { - startDate = startDate.AddDate(0, 0, subModel.CustomRange) - } else { - startDate = startDate.AddDate(subModel.CustomRange, 0, 0) - } - } - - var err error - if len(*transactions) > 0 { - for _, trans := range *transactions { - _, err = tx.Model(&trans).Where("? = ?", pg.Ident("transaction_date"), trans.TransactionDate).Where("? = ?", pg.Ident("subscription_id"), trans.SubscriptionID).OnConflict("DO NOTHING").SelectOrInsert() - if err != nil { - _, err = tx.Model(subModel).Set("? = ?", pg.Ident("last_transaction_date"), trans.TransactionDate).WherePK().Update() - } - } - } - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400113" - exceptionReturn.Message = fmt.Sprintf("Error updating row in \"subscription\" table: %s", err) - return exceptionReturn - } - return nil -} diff --git a/pkg/services/transactionStatus.go b/pkg/services/transactionStatus.go deleted file mode 100644 index 08ea176..0000000 --- a/pkg/services/transactionStatus.go +++ /dev/null @@ -1,75 +0,0 @@ -package services - -import ( - "context" - "fmt" - "wallet-api/pkg/models" - "wallet-api/pkg/utl/common" - - "github.com/go-pg/pg/v10" -) - -type TransactionStatusService struct { - Db *pg.DB -} - -/* -New - -Inserts new row to transaction status table. - Args: - context.Context: Application context - *models.NewTransactionStatusBody: object to create - Returns: - *models.TransactionType: Transaction Type object from database. - *models.Exception: Exception payload. -*/ -func (as *TransactionStatusService) New(ctx context.Context, body *models.NewTransactionStatusBody) (*models.TransactionStatus, *models.Exception) { - db := as.Db.WithContext(ctx) - - tm := new(models.TransactionStatus) - exceptionReturn := new(models.Exception) - - tm.Init() - tm.Name = body.Name - tm.Status = body.Status - - _, err := db.Model(tm).Insert() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400123" - exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"transactionStatus\" table: %s", err) - return nil, exceptionReturn - } - - return tm, nil -} - -/* -GetAll - -Gets all rows from transaction status table. - Args: - context.Context: Application context - string: Relations to embed - Returns: - *[]models.TransactionStatus: List of Transaction status objects from database. - *models.Exception: Exception payload. -*/ -func (as *TransactionStatusService) GetAll(ctx context.Context, embed string) (*[]models.TransactionStatus, *models.Exception) { - db := as.Db.WithContext(ctx) - - wm := new([]models.TransactionStatus) - exceptionReturn := new(models.Exception) - - query := db.Model(wm) - err := common.GenerateEmbed(query, embed).Select() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400124" - exceptionReturn.Message = fmt.Sprintf("Error selecting rows in \"transactionStatus\" table: %s", err) - return nil, exceptionReturn - } - - return wm, nil -} diff --git a/pkg/services/transactionTypes.go b/pkg/services/transactionTypes.go deleted file mode 100644 index aa67324..0000000 --- a/pkg/services/transactionTypes.go +++ /dev/null @@ -1,75 +0,0 @@ -package services - -import ( - "context" - "fmt" - "wallet-api/pkg/models" - "wallet-api/pkg/utl/common" - - "github.com/go-pg/pg/v10" -) - -type TransactionTypeService struct { - Db *pg.DB -} - -/* -New - -Inserts new row to transaction type table. - Args: - context.Context: Application context - *models.NewTransactionTypeBody: object to create - Returns: - *models.TransactionType: Transaction Type object from database. - *models.Exception: Exception payload. -*/ -func (as *TransactionTypeService) New(ctx context.Context, body *models.NewTransactionTypeBody) (*models.TransactionType, *models.Exception) { - db := as.Db.WithContext(ctx) - - tm := new(models.TransactionType) - exceptionReturn := new(models.Exception) - - tm.Init() - tm.Name = body.Name - tm.Type = body.Type - - _, err := db.Model(tm).Insert() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400125" - exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"transactionTypes\" table: %s", err) - return nil, exceptionReturn - } - - return tm, nil -} - -/* -GetAll - -Gets all rows from transaction type table. - Args: - context.Context: Application context - string: Relations to embed - Returns: - *[]models.TransactionType: List of Transaction type objects from database. - *models.Exception: Exception payload. -*/ -func (as *TransactionTypeService) GetAll(ctx context.Context, embed string) (*[]models.TransactionType, *models.Exception) { - db := as.Db.WithContext(ctx) - - wm := new([]models.TransactionType) - exceptionReturn := new(models.Exception) - - query := db.Model(wm) - err := common.GenerateEmbed(query, embed).Select() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400133" - exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactionTypes\" table: %s", err) - return nil, exceptionReturn - } - - return wm, nil -} diff --git a/pkg/services/transactions.go b/pkg/services/transactions.go deleted file mode 100644 index 4185a32..0000000 --- a/pkg/services/transactions.go +++ /dev/null @@ -1,318 +0,0 @@ -package services - -import ( - "context" - "fmt" - "math" - "time" - "wallet-api/pkg/models" - "wallet-api/pkg/utl/common" - - "github.com/go-pg/pg/v10" -) - -type TransactionService struct { - Db *pg.DB - Ss *SubscriptionService -} - -/* -New new row into transaction table - -Inserts - Args: - context.Context: Application context - *models.NewTransactionBody: Transaction body object - Returns: - *models.Transaction: Transaction object - *models.Exception: Exception payload. -*/ -func (as *TransactionService) New(ctx context.Context, body *models.NewTransactionBody) (*models.Transaction, *models.Exception) { - db := as.Db.WithContext(ctx) - exceptionReturn := new(models.Exception) - - tm := new(models.Transaction) - transactionStatus := new(models.TransactionStatus) - - tx, _ := db.Begin() - defer tx.Rollback() - - err := tx.Model(transactionStatus).Where("? = ?", pg.Ident("status"), "completed").Select() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400115" - exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactionsStatus\" table: %s", err) - return nil, exceptionReturn - } - - amount, _ := body.Amount.Float64() - - tm.Init() - tm.WalletID = body.WalletID - tm.TransactionTypeID = body.TransactionTypeID - tm.Description = body.Description - tm.TransactionDate = body.TransactionDate - tm.Amount = float32(math.Round(amount*100) / 100) - tm.TransactionStatusID = transactionStatus.Id - - if body.TransactionDate.IsZero() { - tm.TransactionDate = time.Now() - } - - _, err = tx.Model(tm).Insert() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400116" - exceptionReturn.Message = fmt.Sprintf("Error inserting row in \"transaction\" table: %s", err) - return nil, exceptionReturn - } - tx.Commit() - - return tm, nil -} - -/* -GetAll - -Gets all rows from subscription type table. - Args: - context.Context: Application context - string: Relations to embed - Returns: - *models.Exception: Exception payload. -*/ -// Gets filtered rows from transaction table. -func (as *TransactionService) GetAll(ctx context.Context, am *models.Auth, walletId string, filtered *models.FilteredResponse, noPending bool) *models.Exception { - db := as.Db.WithContext(ctx) - - exceptionReturn := new(models.Exception) - wm := new([]models.Transaction) - transactionStatus := new(models.TransactionStatus) - - tx, _ := db.Begin() - defer tx.Rollback() - - err := tx.Model(transactionStatus).Where("? = ?", pg.Ident("status"), "completed").Select() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400117" - exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactionStatus\" table: %s", err) - return exceptionReturn - } - - query := tx.Model(wm).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id) - if walletId != "" { - query = query.Where("? = ?", pg.Ident("wallet_id"), walletId) - } - if noPending { - query = query.Where("? = ?", pg.Ident("transaction_status_id"), transactionStatus.Id) - } - - err = FilteredResponse(query, wm, filtered) - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400118" - exceptionReturn.Message = fmt.Sprintf("Error selecting row(s) in \"transaction\" table: %s", err) - return exceptionReturn - } - - tx.Commit() - return nil -} - -/* -Check - -Checks subscriptions and create transacitons. - Args: - context.Context: Application context - string: Relations to embed - Returns: - *models.Exception: Exception payload. -*/ -// Gets filtered rows from transaction table. -func (as *TransactionService) Check(ctx context.Context, am *models.Auth, walletId string, filtered *models.FilteredResponse) *models.Exception { - db := as.Db.WithContext(ctx) - - wm := new([]models.Transaction) - sm := new([]models.Subscription) - transactionStatus := new(models.TransactionStatus) - exceptionReturn := new(models.Exception) - - tx, _ := db.Begin() - defer tx.Rollback() - - err := tx.Model(transactionStatus).Where("? = ?", pg.Ident("status"), "pending").Select() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400119" - exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactionStatus\" table: %s", err) - return exceptionReturn - } - query2 := tx.Model(sm).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id) - if walletId != "" { - query2 = query2.Where("? = ?", pg.Ident("wallet_id"), walletId) - } - query2.Select() - - for _, sub := range *sm { - if sub.HasNew() { - as.Ss.SubToTrans(&sub, tx) - } - } - - query := tx.Model(wm).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id) - if walletId != "" { - query = query.Where("? = ?", pg.Ident("wallet_id"), walletId) - } - query = query.Where("? = ?", pg.Ident("transaction_status_id"), transactionStatus.Id) - - err = FilteredResponse(query, wm, filtered) - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400120" - exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transaction\" table: %s", err) - return exceptionReturn - } - - tx.Commit() - return nil -} - -/* -Edit - -Updates row in transaction table by id. - Args: - context.Context: Application context - *models.TransactionEdit: Object to edit - string: id to search - Returns: - *models.Transaction: Transaction object from database. - *models.Exception: Exception payload. -*/ -func (as *TransactionService) Edit(ctx context.Context, body *models.TransactionEdit, id string) (*models.Transaction, *models.Exception) { - db := as.Db.WithContext(ctx) - - amount, _ := body.Amount.Float64() - - exceptionReturn := new(models.Exception) - - tm := new(models.Transaction) - tm.Id = id - tm.Description = body.Description - tm.WalletID = body.WalletID - tm.TransactionTypeID = body.TransactionTypeID - tm.TransactionDate = body.TransactionDate - tm.TransactionStatusID = body.TransactionStatusID - tm.Amount = float32(math.Round(amount*100) / 100) - - tx, _ := db.Begin() - defer tx.Rollback() - - _, err := tx.Model(tm).WherePK().UpdateNotZero() - - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400107" - exceptionReturn.Message = fmt.Sprintf("Error updating row in \"transaction\" table: %s", err) - return nil, exceptionReturn - } - - err = tx.Model(tm).WherePK().Select() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400108" - exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transaction\" table: %s", err) - return nil, exceptionReturn - } - - tx.Commit() - - return tm, nil -} - -/* -Bulk Edit - -Updates row in transaction table by id. - Args: - context.Context: Application context - ?[]models.Transaction Bulk Edit: Object to edit - string: id to search - Returns: - *models.Transaction: Transaction object from database. - *models.Exception: Exception payload. -*/ -func (as *TransactionService) BulkEdit(ctx context.Context, body *[]models.TransactionEdit) (*[]models.Transaction, *models.Exception) { - db := as.Db.WithContext(ctx) - tx, _ := db.Begin() - defer tx.Rollback() - - transactions := new([]models.Transaction) - exceptionReturn := new(models.Exception) - - for _, transaction := range *body { - - amount, _ := transaction.Amount.Float64() - - tm := new(models.Transaction) - tm.Id = transaction.Id - tm.Description = transaction.Description - tm.WalletID = transaction.WalletID - tm.TransactionTypeID = transaction.TransactionTypeID - tm.TransactionDate = transaction.TransactionDate - tm.Amount = float32(math.Round(amount*100) / 100) - - *transactions = append(*transactions, *tm) - } - - _, err := tx.Model(transactions).WherePK().UpdateNotZero() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400121" - exceptionReturn.Message = fmt.Sprintf("Error updating rows in \"transactions\" table: %s", err) - return nil, exceptionReturn - } - - tx.Commit() - - return transactions, nil -} - -/* -Get - -Gets row from transaction table by id. - Args: - context.Context: Application context - *models.Auth: Authentication object - string: id to search - *model.Params: url query parameters - Returns: - *models.Transaction: Transaction object from database. - *models.Exception: Exception payload. -*/ -func (as *TransactionService) Get(ctx context.Context, am *models.Auth, id string, params *models.Params) (*models.Transaction, *models.Exception) { - db := as.Db.WithContext(ctx) - - exceptionReturn := new(models.Exception) - wm := new(models.Transaction) - wm.Id = id - - tx, _ := db.Begin() - defer tx.Rollback() - - qry := tx.Model(wm) - err := common.GenerateEmbed(qry, params.Embed).WherePK().Select() - if err != nil { - exceptionReturn.StatusCode = 400 - exceptionReturn.ErrorCode = "400122" - exceptionReturn.Message = fmt.Sprintf("Error selecting row in \"transactions\" table: %s", err) - return nil, exceptionReturn - } - - tx.Commit() - - return wm, nil -} diff --git a/pkg/utl/common/common.go b/pkg/utl/common/common.go index 4c2ee83..f7a0b0c 100644 --- a/pkg/utl/common/common.go +++ b/pkg/utl/common/common.go @@ -1,6 +1,7 @@ package common import ( + "github.com/gin-gonic/gin" "log" "net" "os" @@ -8,6 +9,18 @@ import ( "strings" ) +type RouteGroups struct { + Api *gin.RouterGroup + Auth *gin.RouterGroup + Wallet *gin.RouterGroup + WalletHeader *gin.RouterGroup + Transaction *gin.RouterGroup + TransactionType *gin.RouterGroup + Subscription *gin.RouterGroup + SubscriptionType *gin.RouterGroup + TransactionStatus *gin.RouterGroup +} + func CheckError(err error) { if err != nil { log.Printf("Error occured. %v", err) @@ -38,4 +51,13 @@ func GetIP() string { } } return "" -} \ No newline at end of file +} + +func Find[T any](lst *[]T, callback func(item *T) bool) *T { + for _, item := range *lst { + if callback(&item) { + return &item + } + } + return nil +}