diff options
author | Mo Tarbin <mhed.t91@gmail.com> | 2024-06-30 21:41:41 -0400 |
---|---|---|
committer | Mo Tarbin <mhed.t91@gmail.com> | 2024-06-30 21:41:41 -0400 |
commit | c13dd9addbf89f716e4ef5cfdf1d673139ffcb68 (patch) | |
tree | bc09646ce1d6d3a402abb4694e19da51b57204f6 /internal/thing/handler.go | |
download | donetick-c13dd9addbf89f716e4ef5cfdf1d673139ffcb68.tar.gz donetick-c13dd9addbf89f716e4ef5cfdf1d673139ffcb68.tar.bz2 donetick-c13dd9addbf89f716e4ef5cfdf1d673139ffcb68.zip |
Move to Donetick Org, first commit
Diffstat (limited to '')
-rw-r--r-- | internal/thing/handler.go | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/internal/thing/handler.go b/internal/thing/handler.go new file mode 100644 index 0000000..5166157 --- /dev/null +++ b/internal/thing/handler.go @@ -0,0 +1,281 @@ +package thing + +import ( + "strconv" + "time" + + auth "donetick.com/core/internal/authorization" + chRepo "donetick.com/core/internal/chore/repo" + cRepo "donetick.com/core/internal/circle/repo" + nRepo "donetick.com/core/internal/notifier/repo" + nps "donetick.com/core/internal/notifier/service" + tModel "donetick.com/core/internal/thing/model" + tRepo "donetick.com/core/internal/thing/repo" + "donetick.com/core/logging" + jwt "github.com/appleboy/gin-jwt/v2" + "github.com/gin-gonic/gin" +) + +type Handler struct { + choreRepo *chRepo.ChoreRepository + circleRepo *cRepo.CircleRepository + nPlanner *nps.NotificationPlanner + nRepo *nRepo.NotificationRepository + tRepo *tRepo.ThingRepository +} + +type ThingRequest struct { + ID int `json:"id"` + Name string `json:"name" binding:"required"` + Type string `json:"type" binding:"required"` + State string `json:"state"` +} + +func NewHandler(cr *chRepo.ChoreRepository, circleRepo *cRepo.CircleRepository, + np *nps.NotificationPlanner, nRepo *nRepo.NotificationRepository, tRepo *tRepo.ThingRepository) *Handler { + return &Handler{ + choreRepo: cr, + circleRepo: circleRepo, + nPlanner: np, + nRepo: nRepo, + tRepo: tRepo, + } +} + +func (h *Handler) CreateThing(c *gin.Context) { + log := logging.FromContext(c) + currentUser, ok := auth.CurrentUser(c) + if !ok { + c.JSON(401, gin.H{"error": "Unauthorized"}) + return + } + + var req ThingRequest + if err := c.BindJSON(&req); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + thing := &tModel.Thing{ + Name: req.Name, + UserID: currentUser.ID, + Type: req.Type, + State: req.State, + } + if !isValidThingState(thing) { + c.JSON(400, gin.H{"error": "Invalid state"}) + return + } + log.Debug("Creating thing", thing) + if err := h.tRepo.UpsertThing(c, thing); err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(201, gin.H{ + "res": thing, + }) +} + +func (h *Handler) UpdateThingState(c *gin.Context) { + currentUser, ok := auth.CurrentUser(c) + if !ok { + c.JSON(401, gin.H{"error": "Unauthorized"}) + return + } + + thingIDRaw := c.Param("id") + thingID, err := strconv.Atoi(thingIDRaw) + if err != nil { + c.JSON(400, gin.H{"error": "Invalid thing id"}) + return + } + + val := c.Query("value") + if val == "" { + c.JSON(400, gin.H{"error": "state or increment query param is required"}) + return + } + thing, err := h.tRepo.GetThingByID(c, thingID) + if thing.UserID != currentUser.ID { + c.JSON(403, gin.H{"error": "Forbidden"}) + return + } + if err != nil { + c.JSON(500, gin.H{"error": "Unable to find thing"}) + return + } + thing.State = val + if !isValidThingState(thing) { + c.JSON(400, gin.H{"error": "Invalid state"}) + return + } + + if err := h.tRepo.UpdateThingState(c, thing); err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + + shouldReturn := EvaluateTriggerAndScheduleDueDate(h, c, thing) + if shouldReturn { + return + } + + c.JSON(200, gin.H{ + "res": thing, + }) +} + +func EvaluateTriggerAndScheduleDueDate(h *Handler, c *gin.Context, thing *tModel.Thing) bool { + thingChores, err := h.tRepo.GetThingChoresByThingId(c, thing.ID) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return true + } + for _, tc := range thingChores { + triggered := EvaluateThingChore(tc, thing.State) + if triggered { + h.choreRepo.SetDueDateIfNotExisted(c, tc.ChoreID, time.Now().UTC()) + } + } + return false +} + +func (h *Handler) UpdateThing(c *gin.Context) { + currentUser, ok := auth.CurrentUser(c) + if !ok { + c.JSON(401, gin.H{"error": "Unauthorized"}) + return + } + + var req ThingRequest + if err := c.BindJSON(&req); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + + thing, err := h.tRepo.GetThingByID(c, req.ID) + + if err != nil { + c.JSON(500, gin.H{"error": "Unable to find thing"}) + return + } + if thing.UserID != currentUser.ID { + c.JSON(403, gin.H{"error": "Forbidden"}) + return + } + thing.Name = req.Name + thing.Type = req.Type + if req.State != "" { + thing.State = req.State + if !isValidThingState(thing) { + c.JSON(400, gin.H{"error": "Invalid state"}) + return + } + } + + if err := h.tRepo.UpsertThing(c, thing); err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(200, gin.H{ + "res": thing, + }) +} + +func (h *Handler) GetAllThings(c *gin.Context) { + currentUser, ok := auth.CurrentUser(c) + if !ok { + c.JSON(401, gin.H{"error": "Unauthorized"}) + return + } + + things, err := h.tRepo.GetUserThings(c, currentUser.ID) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(200, gin.H{ + "res": things, + }) +} + +func (h *Handler) GetThingHistory(c *gin.Context) { + currentUser, ok := auth.CurrentUser(c) + if !ok { + c.JSON(401, gin.H{"error": "Unauthorized"}) + return + } + + thingIDRaw := c.Param("id") + thingID, err := strconv.Atoi(thingIDRaw) + if err != nil { + c.JSON(400, gin.H{"error": "Invalid thing id"}) + return + } + + thing, err := h.tRepo.GetThingByID(c, thingID) + if err != nil { + c.JSON(500, gin.H{"error": "Unable to find thing"}) + return + } + if thing.UserID != currentUser.ID { + c.JSON(403, gin.H{"error": "Forbidden"}) + return + } + offsetRaw := c.Query("offset") + offset, err := strconv.Atoi(offsetRaw) + if err != nil { + c.JSON(400, gin.H{"error": "Invalid offset"}) + return + } + history, err := h.tRepo.GetThingHistoryWithOffset(c, thingID, offset, 10) + if err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(200, gin.H{ + "res": history, + }) +} + +func (h *Handler) DeleteThing(c *gin.Context) { + currentUser, ok := auth.CurrentUser(c) + if !ok { + c.JSON(401, gin.H{"error": "Unauthorized"}) + return + } + + thingIDRaw := c.Param("id") + thingID, err := strconv.Atoi(thingIDRaw) + if err != nil { + c.JSON(400, gin.H{"error": "Invalid thing id"}) + return + } + + thing, err := h.tRepo.GetThingByID(c, thingID) + if err != nil { + c.JSON(500, gin.H{"error": "Unable to find thing"}) + return + } + if thing.UserID != currentUser.ID { + c.JSON(403, gin.H{"error": "Forbidden"}) + return + } + if err := h.tRepo.DeleteThing(c, thingID); err != nil { + c.JSON(500, gin.H{"error": err.Error()}) + return + } + c.JSON(200, gin.H{}) +} +func Routes(r *gin.Engine, h *Handler, auth *jwt.GinJWTMiddleware) { + + thingRoutes := r.Group("things") + thingRoutes.Use(auth.MiddlewareFunc()) + { + thingRoutes.POST("", h.CreateThing) + thingRoutes.PUT("/:id/state", h.UpdateThingState) + thingRoutes.PUT("", h.UpdateThing) + thingRoutes.GET("", h.GetAllThings) + thingRoutes.GET("/:id/history", h.GetThingHistory) + thingRoutes.DELETE("/:id", h.DeleteThing) + } +} |