diff --git a/shoppingbasketapp/config.go b/shoppingbasketapp/config.go index 991d4f84..c87b97df 100644 --- a/shoppingbasketapp/config.go +++ b/shoppingbasketapp/config.go @@ -2,10 +2,12 @@ package shoppingbasketapp import ( "git.gocasts.ir/ebhomengo/niki/adapter/redis" + "git.gocasts.ir/ebhomengo/niki/pkg/httpserver" "git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/repository" ) type Config struct { - Redis redis.Config `koanf:"redis" json:"redis"` - Repo repository.Repo `koanf:"repo" json:"repo"` + Redis redis.Config `koanf:"redis" json:"redis"` + Repo repository.Repo `koanf:"repo" json:"repo"` + HTTPServer httpserver.Config `koanf:"http_server" json:"http_server"` } diff --git a/shoppingbasketapp/delivery/http/handler.go b/shoppingbasketapp/delivery/http/handler.go new file mode 100644 index 00000000..f593c076 --- /dev/null +++ b/shoppingbasketapp/delivery/http/handler.go @@ -0,0 +1,117 @@ +package http + +import ( + "git.gocasts.ir/ebhomengo/niki/pkg/claim" + httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg" + "git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/service/cart" + "git.gocasts.ir/ebhomengo/niki/types" + "github.com/labstack/echo/v4" + "net/http" + "strconv" +) + +type Handler struct { + svc cart.Service +} + +func NewHandler(svc cart.Service) Handler { + return Handler{svc: svc} +} + +func (h Handler) addToBasket(c echo.Context) error { + claims := claim.GetClaimsFromEchoContext(c) + + var req cart.AddToCartRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{ + "error": "invalid request body", + }) + } + + req.UserID = types.ID(claims.UserID) + if err := h.svc.AddToBasket(c.Request().Context(), req); err != nil { + msg, code := httpmsg.Error(err) + return c.JSON(code, msg) + } + + return c.NoContent(http.StatusNoContent) +} + +func (h Handler) getCart(c echo.Context) error { + claims := claim.GetClaimsFromEchoContext(c) + + res, err := h.svc.GetCart(c.Request().Context(), types.ID(claims.UserID)) + if err != nil { + msg, code := httpmsg.Error(err) + return c.JSON(code, msg) + } + + return c.JSON(http.StatusOK, res) +} + +func (h Handler) removeCart(c echo.Context) error { + claims := claim.GetClaimsFromEchoContext(c) + + if err := h.svc.ClearCart(c.Request().Context(), types.ID(claims.UserID)); err != nil { + msg, code := httpmsg.Error(err) + return c.JSON(code, msg) + } + + return c.NoContent(http.StatusNoContent) +} + +func (h Handler) removeItem(c echo.Context) error { + claims := claim.GetClaimsFromEchoContext(c) + p := c.Param("productID") + + pID, err := strconv.Atoi(p) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{ + "error": "invalid product id", + }) + } + + var req cart.RemoveFromCartRequest + + req.UserID = types.ID(claims.UserID) + req.ProductID = types.ID(pID) + + if err := h.svc.RemoveFromCart(c.Request().Context(), req); err != nil { + msg, code := httpmsg.Error(err) + return c.JSON(code, msg) + } + + return c.NoContent(http.StatusNoContent) +} + +func (h Handler) updateQuantity(c echo.Context) error { + claims := claim.GetClaimsFromEchoContext(c) + p := c.Param("productID") + + pID, err := strconv.Atoi(p) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{ + "error": "invalid product id", + }) + } + + qStr := c.Param("quantity") + q, err := strconv.Atoi(qStr) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{ + "error": "invalid quantity", + }) + } + + var req cart.UpdateQuantityRequest + req.UserID = types.ID(claims.UserID) + req.ProductID = types.ID(pID) + req.Quantity = q + + if err := h.svc.UpdateQuantity(c.Request().Context(), req); err != nil { + msg, code := httpmsg.Error(err) + return c.JSON(code, msg) + } + + return c.NoContent(http.StatusNoContent) +} diff --git a/shoppingbasketapp/delivery/http/health_check.go b/shoppingbasketapp/delivery/http/health_check.go new file mode 100644 index 00000000..aa0a0254 --- /dev/null +++ b/shoppingbasketapp/delivery/http/health_check.go @@ -0,0 +1,12 @@ +package http + +import ( + "github.com/labstack/echo/v4" + "net/http" +) + +func (s Server) healthCheck(c echo.Context) error { + return c.JSON(http.StatusOK, echo.Map{ + "message": "everything is good!", + }) +} diff --git a/shoppingbasketapp/delivery/http/server.go b/shoppingbasketapp/delivery/http/server.go new file mode 100644 index 00000000..cfe5fe8c --- /dev/null +++ b/shoppingbasketapp/delivery/http/server.go @@ -0,0 +1,43 @@ +package http + +import ( + "context" + "git.gocasts.ir/ebhomengo/niki/pkg/httpserver" +) + +type Server struct { + handler Handler + HTTPServer *httpserver.Server +} + +func NewServer(handler Handler, hS *httpserver.Server) Server { + return Server{handler: handler, HTTPServer: hS} +} + +func (s Server) Serve() error { + s.registerRoutes() + if err := s.HTTPServer.Start(); err != nil { + return err + } + + return nil +} + +func (s Server) Stop(ctx context.Context) error { + return s.HTTPServer.Stop(ctx) +} + +func (s Server) registerRoutes() { + router := s.HTTPServer.GetRouter() + + router.GET("shoppingbasket/health-check", s.healthCheck) + + r := router.Group("shoppingbasket/cart") // Authentication is required + + r.GET("/", s.handler.getCart) + r.DELETE("/", s.handler.removeCart) + + r.POST("/items", s.handler.addToBasket) + r.DELETE("/items/:productID", s.handler.removeItem) + r.PUT("/items/:productID/:quantity", s.handler.updateQuantity) +}