Move to Donetick Org, first commit
This commit is contained in:
commit
c13dd9addb
42 changed files with 7463 additions and 0 deletions
281
internal/thing/handler.go
Normal file
281
internal/thing/handler.go
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
57
internal/thing/helper.go
Normal file
57
internal/thing/helper.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package thing
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
tModel "donetick.com/core/internal/thing/model"
|
||||
)
|
||||
|
||||
func isValidThingState(thing *tModel.Thing) bool {
|
||||
switch thing.Type {
|
||||
case "number":
|
||||
_, err := strconv.Atoi(thing.State)
|
||||
return err == nil
|
||||
case "text":
|
||||
return true
|
||||
case "boolean":
|
||||
return thing.State == "true" || thing.State == "false"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func EvaluateThingChore(tchore *tModel.ThingChore, newState string) bool {
|
||||
if tchore.Condition == "" {
|
||||
return newState == tchore.TriggerState
|
||||
}
|
||||
|
||||
switch tchore.Condition {
|
||||
case "eq":
|
||||
return newState == tchore.TriggerState
|
||||
case "neq":
|
||||
return newState != tchore.TriggerState
|
||||
}
|
||||
|
||||
newStateInt, err := strconv.Atoi(newState)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
TargetStateInt, err := strconv.Atoi(tchore.TriggerState)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch tchore.Condition {
|
||||
case "gt":
|
||||
return newStateInt > TargetStateInt
|
||||
case "lt":
|
||||
return newStateInt < TargetStateInt
|
||||
case "gte":
|
||||
return newStateInt >= TargetStateInt
|
||||
case "lte":
|
||||
return newStateInt <= TargetStateInt
|
||||
default:
|
||||
return newState == tchore.TriggerState
|
||||
}
|
||||
|
||||
}
|
30
internal/thing/model/model.go
Normal file
30
internal/thing/model/model.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type Thing struct {
|
||||
ID int `json:"id" gorm:"primary_key"`
|
||||
UserID int `json:"userID" gorm:"column:user_id"`
|
||||
CircleID int `json:"circleId" gorm:"column:circle_id"`
|
||||
Name string `json:"name" gorm:"column:name"`
|
||||
State string `json:"state" gorm:"column:state"`
|
||||
Type string `json:"type" gorm:"column:type"`
|
||||
ThingChores []ThingChore `json:"thingChores" gorm:"foreignkey:ThingID;references:ID"`
|
||||
UpdatedAt *time.Time `json:"updatedAt" gorm:"column:updated_at"`
|
||||
CreatedAt *time.Time `json:"createdAt" gorm:"column:created_at"`
|
||||
}
|
||||
|
||||
type ThingHistory struct {
|
||||
ID int `json:"id" gorm:"primary_key"`
|
||||
ThingID int `json:"thingId" gorm:"column:thing_id"`
|
||||
State string `json:"state" gorm:"column:state"`
|
||||
UpdatedAt *time.Time `json:"updatedAt" gorm:"column:updated_at"`
|
||||
CreatedAt *time.Time `json:"createdAt" gorm:"column:created_at"`
|
||||
}
|
||||
|
||||
type ThingChore struct {
|
||||
ThingID int `json:"thingId" gorm:"column:thing_id;primaryKey;uniqueIndex:idx_thing_user"`
|
||||
ChoreID int `json:"choreId" gorm:"column:chore_id;primaryKey;uniqueIndex:idx_thing_user"`
|
||||
TriggerState string `json:"triggerState" gorm:"column:trigger_state"`
|
||||
Condition string `json:"condition" gorm:"column:condition"`
|
||||
}
|
117
internal/thing/repo/repository.go
Normal file
117
internal/thing/repo/repository.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
package chore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
config "donetick.com/core/config"
|
||||
tModel "donetick.com/core/internal/thing/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ThingRepository struct {
|
||||
db *gorm.DB
|
||||
dbType string
|
||||
}
|
||||
|
||||
func NewThingRepository(db *gorm.DB, cfg *config.Config) *ThingRepository {
|
||||
return &ThingRepository{db: db, dbType: cfg.Database.Type}
|
||||
}
|
||||
|
||||
func (r *ThingRepository) UpsertThing(c context.Context, thing *tModel.Thing) error {
|
||||
return r.db.WithContext(c).Model(&thing).Save(thing).Error
|
||||
}
|
||||
|
||||
func (r *ThingRepository) UpdateThingState(c context.Context, thing *tModel.Thing) error {
|
||||
// update the state of the thing where the id is the same:
|
||||
if err := r.db.WithContext(c).Model(&thing).Where("id = ?", thing.ID).Updates(map[string]interface{}{
|
||||
"state": thing.State,
|
||||
"updated_at": time.Now().UTC(),
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// Create history Record of the thing :
|
||||
createdAt := time.Now().UTC()
|
||||
thingHistory := &tModel.ThingHistory{
|
||||
ThingID: thing.ID,
|
||||
State: thing.State,
|
||||
CreatedAt: &createdAt,
|
||||
UpdatedAt: &createdAt,
|
||||
}
|
||||
|
||||
if err := r.db.WithContext(c).Create(thingHistory).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (r *ThingRepository) GetThingByID(c context.Context, thingID int) (*tModel.Thing, error) {
|
||||
var thing tModel.Thing
|
||||
if err := r.db.WithContext(c).Model(&tModel.Thing{}).Preload("ThingChores").First(&thing, thingID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &thing, nil
|
||||
}
|
||||
|
||||
func (r *ThingRepository) GetThingByChoreID(c context.Context, choreID int) (*tModel.Thing, error) {
|
||||
var thing tModel.Thing
|
||||
if err := r.db.WithContext(c).Model(&tModel.Thing{}).Joins("left join thing_chores on things.id = thing_chores.thing_id").First(&thing, "thing_chores.chore_id = ?", choreID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &thing, nil
|
||||
}
|
||||
|
||||
func (r *ThingRepository) AssociateThingWithChore(c context.Context, thingID int, choreID int, triggerState string, condition string) error {
|
||||
|
||||
return r.db.WithContext(c).Save(&tModel.ThingChore{ThingID: thingID, ChoreID: choreID, TriggerState: triggerState, Condition: condition}).Error
|
||||
}
|
||||
|
||||
func (r *ThingRepository) DissociateThingWithChore(c context.Context, thingID int, choreID int) error {
|
||||
return r.db.WithContext(c).Where("thing_id = ? AND chore_id = ?", thingID, choreID).Delete(&tModel.ThingChore{}).Error
|
||||
}
|
||||
|
||||
func (r *ThingRepository) GetThingHistoryWithOffset(c context.Context, thingID int, offset int, limit int) ([]*tModel.ThingHistory, error) {
|
||||
var thingHistory []*tModel.ThingHistory
|
||||
if err := r.db.WithContext(c).Model(&tModel.ThingHistory{}).Where("thing_id = ?", thingID).Order("created_at desc").Offset(offset).Limit(limit).Find(&thingHistory).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return thingHistory, nil
|
||||
}
|
||||
|
||||
func (r *ThingRepository) GetUserThings(c context.Context, userID int) ([]*tModel.Thing, error) {
|
||||
var things []*tModel.Thing
|
||||
if err := r.db.WithContext(c).Model(&tModel.Thing{}).Where("user_id = ?", userID).Find(&things).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return things, nil
|
||||
}
|
||||
|
||||
func (r *ThingRepository) DeleteThing(c context.Context, thingID int) error {
|
||||
// one transaction to delete the thing and its history :
|
||||
return r.db.WithContext(c).Transaction(func(tx *gorm.DB) error {
|
||||
if err := r.db.WithContext(c).Where("thing_id = ?", thingID).Delete(&tModel.ThingHistory{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.db.WithContext(c).Delete(&tModel.Thing{}, thingID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// get ThingChores by thingID:
|
||||
func (r *ThingRepository) GetThingChoresByThingId(c context.Context, thingID int) ([]*tModel.ThingChore, error) {
|
||||
var thingChores []*tModel.ThingChore
|
||||
if err := r.db.WithContext(c).Model(&tModel.ThingChore{}).Where("thing_id = ?", thingID).Find(&thingChores).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return thingChores, nil
|
||||
}
|
||||
|
||||
// func (r *ThingRepository) GetChoresByThingId(c context.Context, thingID int) ([]*chModel.Chore, error) {
|
||||
// var chores []*chModel.Chore
|
||||
// if err := r.db.WithContext(c).Model(&chModel.Chore{}).Joins("left join thing_chores on chores.id = thing_chores.chore_id").Where("thing_chores.thing_id = ?", thingID).Find(&chores).Error; err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return chores, nil
|
||||
// }
|
175
internal/thing/webhook.go
Normal file
175
internal/thing/webhook.go
Normal file
|
@ -0,0 +1,175 @@
|
|||
package thing
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"donetick.com/core/config"
|
||||
chRepo "donetick.com/core/internal/chore/repo"
|
||||
cRepo "donetick.com/core/internal/circle/repo"
|
||||
tModel "donetick.com/core/internal/thing/model"
|
||||
tRepo "donetick.com/core/internal/thing/repo"
|
||||
uRepo "donetick.com/core/internal/user/repo"
|
||||
"donetick.com/core/internal/utils"
|
||||
"donetick.com/core/logging"
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
choreRepo *chRepo.ChoreRepository
|
||||
circleRepo *cRepo.CircleRepository
|
||||
thingRepo *tRepo.ThingRepository
|
||||
userRepo *uRepo.UserRepository
|
||||
tRepo *tRepo.ThingRepository
|
||||
}
|
||||
|
||||
func NewWebhook(cr *chRepo.ChoreRepository, circleRepo *cRepo.CircleRepository,
|
||||
thingRepo *tRepo.ThingRepository, userRepo *uRepo.UserRepository, tRepo *tRepo.ThingRepository) *Webhook {
|
||||
return &Webhook{
|
||||
choreRepo: cr,
|
||||
circleRepo: circleRepo,
|
||||
thingRepo: thingRepo,
|
||||
userRepo: userRepo,
|
||||
tRepo: tRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Webhook) UpdateThingState(c *gin.Context) {
|
||||
thing, shouldReturn := validateUserAndThing(c, h)
|
||||
if shouldReturn {
|
||||
return
|
||||
}
|
||||
|
||||
state := c.Query("state")
|
||||
if state == "" {
|
||||
c.JSON(400, gin.H{"error": "Invalid state value"})
|
||||
return
|
||||
}
|
||||
|
||||
thing.State = state
|
||||
if !isValidThingState(thing) {
|
||||
c.JSON(400, gin.H{"error": "Invalid state for thing"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.thingRepo.UpdateThingState(c, thing); err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{})
|
||||
}
|
||||
|
||||
func (h *Webhook) ChangeThingState(c *gin.Context) {
|
||||
thing, shouldReturn := validateUserAndThing(c, h)
|
||||
if shouldReturn {
|
||||
return
|
||||
}
|
||||
addRemoveRaw := c.Query("op")
|
||||
setRaw := c.Query("set")
|
||||
|
||||
if addRemoveRaw == "" && setRaw == "" {
|
||||
c.JSON(400, gin.H{"error": "Invalid increment value"})
|
||||
return
|
||||
}
|
||||
var xValue int
|
||||
var err error
|
||||
if addRemoveRaw != "" {
|
||||
xValue, err = strconv.Atoi(addRemoveRaw)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": "Invalid increment value"})
|
||||
return
|
||||
}
|
||||
currentState, err := strconv.Atoi(thing.State)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": "Invalid state for thing"})
|
||||
return
|
||||
}
|
||||
newState := currentState + xValue
|
||||
thing.State = strconv.Itoa(newState)
|
||||
}
|
||||
if setRaw != "" {
|
||||
thing.State = setRaw
|
||||
}
|
||||
|
||||
if !isValidThingState(thing) {
|
||||
c.JSON(400, gin.H{"error": "Invalid state for thing"})
|
||||
return
|
||||
}
|
||||
if err := h.thingRepo.UpdateThingState(c, thing); err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
shouldReturn1 := WebhookEvaluateTriggerAndScheduleDueDate(h, c, thing)
|
||||
if shouldReturn1 {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{"state": thing.State})
|
||||
}
|
||||
|
||||
func WebhookEvaluateTriggerAndScheduleDueDate(h *Webhook, c *gin.Context, thing *tModel.Thing) bool {
|
||||
// handler should be interface to not duplicate both WebhookEvaluateTriggerAndScheduleDueDate and EvaluateTriggerAndScheduleDueDate
|
||||
// this is bad code written Saturday at 2:25 AM
|
||||
|
||||
log := logging.FromContext(c)
|
||||
|
||||
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 {
|
||||
errSave := h.choreRepo.SetDueDate(c, tc.ChoreID, time.Now().UTC())
|
||||
if errSave != nil {
|
||||
log.Error("Error setting due date for chore ", errSave)
|
||||
log.Error("Chore ID ", tc.ChoreID, " Thing ID ", thing.ID, " State ", thing.State)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validateUserAndThing(c *gin.Context, h *Webhook) (*tModel.Thing, bool) {
|
||||
apiToken := c.GetHeader("secretkey")
|
||||
if apiToken == "" {
|
||||
c.JSON(401, gin.H{"error": "Unauthorized"})
|
||||
return nil, true
|
||||
}
|
||||
thingID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return nil, true
|
||||
}
|
||||
user, err := h.userRepo.GetUserByToken(c, apiToken)
|
||||
if err != nil {
|
||||
c.JSON(401, gin.H{"error": "Unauthorized"})
|
||||
return nil, true
|
||||
}
|
||||
thing, err := h.thingRepo.GetThingByID(c, thingID)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": "Invalid thing id"})
|
||||
return nil, true
|
||||
}
|
||||
if thing.UserID != user.ID {
|
||||
c.JSON(401, gin.H{"error": "Unauthorized"})
|
||||
return nil, true
|
||||
}
|
||||
return thing, false
|
||||
}
|
||||
|
||||
func Webhooks(cfg *config.Config, w *Webhook, r *gin.Engine, auth *jwt.GinJWTMiddleware) {
|
||||
|
||||
thingsAPI := r.Group("webhooks/things")
|
||||
|
||||
thingsAPI.Use(utils.TimeoutMiddleware(cfg.Server.WriteTimeout))
|
||||
{
|
||||
thingsAPI.GET("/:id/state/change", w.ChangeThingState)
|
||||
thingsAPI.GET("/:id/state", w.UpdateThingState)
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue