From bb507036db30f44039f288eaddaa08cfc3604006 Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Tue, 14 Apr 2026 15:42:30 +0200 Subject: [PATCH] change add-product endpoint + remove-product --- app/delivery/web/api/restricted/carts.go | 80 ++++++++++++++++++- app/repos/cartsRepo/cartsRepo.go | 72 ++++++++++++++--- app/service/cartsService/cartsService.go | 16 +++- app/utils/const_data/consts.go | 1 + app/utils/responseErrors/responseErrors.go | 8 ++ .../carts/add-product-to-cart (1).yml | 5 +- .../b2b_daniel/carts/add-product-to-cart.yml | 5 +- 7 files changed, 172 insertions(+), 15 deletions(-) diff --git a/app/delivery/web/api/restricted/carts.go b/app/delivery/web/api/restricted/carts.go index a787620..34600be 100644 --- a/app/delivery/web/api/restricted/carts.go +++ b/app/delivery/web/api/restricted/carts.go @@ -29,10 +29,12 @@ func CartsHandlerRoutes(r fiber.Router) fiber.Router { handler := NewCartsHandler() r.Get("/add-new-cart", handler.AddNewCart) + r.Delete("/remove-cart", handler.RemoveCart) r.Get("/change-cart-name", handler.ChangeCartName) r.Get("/retrieve-carts-info", handler.RetrieveCartsInfo) r.Get("/retrieve-cart", handler.RetrieveCart) r.Get("/add-product-to-cart", handler.AddProduct) + r.Delete("/remove-product-from-cart", handler.RemoveProduct) return r } @@ -53,6 +55,29 @@ func (h *CartsHandler) AddNewCart(c fiber.Ctx) error { return c.JSON(response.Make(&new_cart, 0, i18n.T_(c, response.Message_OK))) } +func (h *CartsHandler) RemoveCart(c fiber.Ctx) error { + userID, ok := localeExtractor.GetUserID(c) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + cart_id_attribute := c.Query("cart_id") + cart_id, err := strconv.Atoi(cart_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + err = h.cartsService.RemoveCart(userID, uint(cart_id)) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK))) +} + func (h *CartsHandler) ChangeCartName(c fiber.Ctx) error { userID, ok := localeExtractor.GetUserID(c) if !ok { @@ -117,6 +142,7 @@ func (h *CartsHandler) RetrieveCart(c fiber.Ctx) error { return c.JSON(response.Make(cart, 0, i18n.T_(c, response.Message_OK))) } +// adds or sets given amount of products to the cart func (h *CartsHandler) AddProduct(c fiber.Ctx) error { userID, ok := localeExtractor.GetUserID(c) if !ok { @@ -159,7 +185,59 @@ func (h *CartsHandler) AddProduct(c fiber.Ctx) error { JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) } - err = h.cartsService.AddProduct(userID, uint(cart_id), uint(product_id), product_attribute_id, uint(amount)) + set_amount_attribute := c.Query("set_amount") + set_amount, err := strconv.ParseBool(set_amount_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + err = h.cartsService.AddProduct(userID, uint(cart_id), uint(product_id), product_attribute_id, amount, set_amount) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK))) +} + +// removes product from the cart. +func (h *CartsHandler) RemoveProduct(c fiber.Ctx) error { + userID, ok := localeExtractor.GetUserID(c) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + cart_id_attribute := c.Query("cart_id") + cart_id, err := strconv.Atoi(cart_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + product_id_attribute := c.Query("product_id") + product_id, err := strconv.Atoi(product_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + product_attribute_id_attribute := c.Query("product_attribute_id") + var product_attribute_id *uint + if product_attribute_id_attribute == "" { + product_attribute_id = nil + } else { + val, err := strconv.Atoi(product_attribute_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + uval := uint(val) + product_attribute_id = &uval + } + + err = h.cartsService.RemoveProduct(userID, uint(cart_id), uint(product_id), product_attribute_id) if err != nil { return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) diff --git a/app/repos/cartsRepo/cartsRepo.go b/app/repos/cartsRepo/cartsRepo.go index 4141dcb..0f4c5ab 100644 --- a/app/repos/cartsRepo/cartsRepo.go +++ b/app/repos/cartsRepo/cartsRepo.go @@ -1,9 +1,13 @@ package cartsRepo import ( + "errors" + "git.ma-al.com/goc_daniel/b2b/app/db" "git.ma-al.com/goc_daniel/b2b/app/model" constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data" + "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" + "gorm.io/gorm" ) type UICartsRepo interface { @@ -15,7 +19,8 @@ type UICartsRepo interface { RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, error) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) CheckProductExists(product_id uint, product_attribute_id *uint) (bool, error) - AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error + AddProduct(cart_id uint, product_id uint, product_attribute_id *uint, amount uint, set_amount bool) error + RemoveProduct(cart_id uint, product_id uint, product_attribute_id *uint) error } type CartsRepo struct{} @@ -129,14 +134,61 @@ func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id } } -func (repo *CartsRepo) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error { - product := model.CartProduct{ - CartID: cart_id, - ProductID: product_id, - ProductAttributeID: product_attribute_id, - Amount: amount, - } - err := db.DB.Create(&product).Error +func (repo *CartsRepo) AddProduct(cart_id uint, product_id uint, product_attribute_id *uint, amount uint, set_amount bool) error { + var product model.CartProduct - return err + err := db.DB. + Where(&model.CartProduct{ + CartID: cart_id, + ProductID: product_id, + ProductAttributeID: product_attribute_id, + }). + First(&product).Error + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + if amount < 1 { + return responseErrors.ErrAmountMustBePositive + } else if amount > constdata.MAX_AMOUNT_OF_PRODUCT_IN_CART { + return responseErrors.ErrAmountMustBeReasonable + } + + product = model.CartProduct{ + CartID: cart_id, + ProductID: product_id, + ProductAttributeID: product_attribute_id, + Amount: amount, + } + + return db.DB.Create(&product).Error + } + + // Some other DB error + return err + } + + // Product already exists in cart + if set_amount { + product.Amount = amount + } else { + product.Amount = product.Amount + amount + } + + if product.Amount < 1 { + return responseErrors.ErrAmountMustBePositive + } else if product.Amount > constdata.MAX_AMOUNT_OF_PRODUCT_IN_CART { + return responseErrors.ErrAmountMustBeReasonable + } + + return db.DB.Save(&product).Error +} + +func (repo *CartsRepo) RemoveProduct(cart_id uint, product_id uint, product_attribute_id *uint) error { + return db.DB. + Where(&model.CartProduct{ + CartID: cart_id, + ProductID: product_id, + ProductAttributeID: product_attribute_id, + }). + Delete(&model.CartProduct{}).Error } diff --git a/app/service/cartsService/cartsService.go b/app/service/cartsService/cartsService.go index 2ed4006..6d75b3c 100644 --- a/app/service/cartsService/cartsService.go +++ b/app/service/cartsService/cartsService.go @@ -74,7 +74,7 @@ func (s *CartsService) RetrieveCart(user_id uint, cart_id uint) (*model.Customer return s.repo.RetrieveCart(user_id, cart_id) } -func (s *CartsService) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error { +func (s *CartsService) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount int, set_amount bool) error { exists, err := s.repo.UserHasCart(user_id, cart_id) if err != nil { return err @@ -91,5 +91,17 @@ func (s *CartsService) AddProduct(user_id uint, cart_id uint, product_id uint, p return responseErrors.ErrProductOrItsVariationDoesNotExist } - return s.repo.AddProduct(user_id, cart_id, product_id, product_attribute_id, amount) + return s.repo.AddProduct(cart_id, product_id, product_attribute_id, uint(amount), set_amount) +} + +func (s *CartsService) RemoveProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint) error { + exists, err := s.repo.UserHasCart(user_id, cart_id) + if err != nil { + return err + } + if !exists { + return responseErrors.ErrUserHasNoSuchCart + } + + return s.repo.RemoveProduct(cart_id, product_id, product_attribute_id) } diff --git a/app/utils/const_data/consts.go b/app/utils/const_data/consts.go index 30390a6..5860000 100644 --- a/app/utils/const_data/consts.go +++ b/app/utils/const_data/consts.go @@ -15,6 +15,7 @@ var CATEGORY_BLACKLIST = []uint{250} const MAX_AMOUNT_OF_CARTS_PER_USER = 10 const DEFAULT_NEW_CART_NAME = "new cart" +const MAX_AMOUNT_OF_PRODUCT_IN_CART = 1024 const MAX_AMOUNT_OF_ADDRESSES_PER_USER = 10 diff --git a/app/utils/responseErrors/responseErrors.go b/app/utils/responseErrors/responseErrors.go index c0aee88..1eb735e 100644 --- a/app/utils/responseErrors/responseErrors.go +++ b/app/utils/responseErrors/responseErrors.go @@ -65,6 +65,8 @@ var ( ErrMaxAmtOfCartsReached = errors.New("maximal amount of carts reached") ErrUserHasNoSuchCart = errors.New("user does not have cart with given id") ErrProductOrItsVariationDoesNotExist = errors.New("product or its variation with given ids does not exist") + ErrAmountMustBePositive = errors.New("amount must be positive") + ErrAmountMustBeReasonable = errors.New("amount must be reasonable") // Typed errors for orders handler ErrEmptyCart = errors.New("the cart is empty") @@ -205,6 +207,10 @@ func GetErrorCode(c fiber.Ctx, err error) string { return i18n.T_(c, "error.err_user_has_no_such_cart") case errors.Is(err, ErrProductOrItsVariationDoesNotExist): return i18n.T_(c, "error.err_product_or_its_variation_does_not_exist") + case errors.Is(err, ErrAmountMustBePositive): + return i18n.T_(c, "error.err_amount_must_be_positive") + case errors.Is(err, ErrAmountMustBeReasonable): + return i18n.T_(c, "error.err_amount_must_be_reasonable") case errors.Is(err, ErrEmptyCart): return i18n.T_(c, "error.err_cart_is_empty") @@ -292,6 +298,8 @@ func GetErrorStatus(err error) int { errors.Is(err, ErrMaxAmtOfCartsReached), errors.Is(err, ErrUserHasNoSuchCart), errors.Is(err, ErrProductOrItsVariationDoesNotExist), + errors.Is(err, ErrAmountMustBePositive), + errors.Is(err, ErrAmountMustBeReasonable), errors.Is(err, ErrEmptyCart), errors.Is(err, ErrUserHasNoSuchOrder), errors.Is(err, ErrInvalidReductionType), diff --git a/bruno/b2b_daniel/carts/add-product-to-cart (1).yml b/bruno/b2b_daniel/carts/add-product-to-cart (1).yml index eb7a5a1..f8b93c3 100644 --- a/bruno/b2b_daniel/carts/add-product-to-cart (1).yml +++ b/bruno/b2b_daniel/carts/add-product-to-cart (1).yml @@ -5,7 +5,7 @@ info: http: method: GET - url: http://localhost:3000/api/v1/restricted/carts/add-product-to-cart?cart_id=1&product_id=51&amount=1 + url: http://localhost:3000/api/v1/restricted/carts/add-product-to-cart?cart_id=1&product_id=51&amount=1&set_amount=false params: - name: cart_id value: "1" @@ -16,6 +16,9 @@ http: - name: amount value: "1" type: query + - name: set_amount + value: "false" + type: query auth: inherit settings: diff --git a/bruno/b2b_daniel/carts/add-product-to-cart.yml b/bruno/b2b_daniel/carts/add-product-to-cart.yml index ff780c1..9337ab5 100644 --- a/bruno/b2b_daniel/carts/add-product-to-cart.yml +++ b/bruno/b2b_daniel/carts/add-product-to-cart.yml @@ -5,7 +5,7 @@ info: http: method: GET - url: http://localhost:3000/api/v1/restricted/carts/add-product-to-cart?cart_id=1&product_id=51&product_attribute_id=1115&amount=1 + url: http://localhost:3000/api/v1/restricted/carts/add-product-to-cart?cart_id=1&product_id=51&product_attribute_id=1115&amount=1&set_amount=true params: - name: cart_id value: "1" @@ -19,6 +19,9 @@ http: - name: amount value: "1" type: query + - name: set_amount + value: "true" + type: query auth: inherit settings: