diff --git a/README.md b/README.md index 41355ce..e955e6c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # Allowance Planner 2000 -An improved Allowance Planner app. \ No newline at end of file +An improved Allowance Planner app. diff --git a/backend/api_test.go b/backend/api_test.go index 940b8c9..68f4708 100644 --- a/backend/api_test.go +++ b/backend/api_test.go @@ -492,6 +492,34 @@ func TestGetUserByAllowanceByIdBadAllowanceId(t *testing.T) { e.GET("/user/1/allowance/bad").Expect().Status(400) } +func TestPutAllowanceById(t *testing.T) { + e := startServer(t) + + // Create a new allowance + requestBody := map[string]interface{}{ + "name": TestAllowanceName, + "target": 5000, + "weight": 10, + } + resp := e.POST("/user/1/allowance").WithJSON(requestBody).Expect().Status(201).JSON().Object() + allowanceId := int(resp.Value("id").Number().Raw()) + + // Update the allowance + updateRequest := map[string]interface{}{ + "name": "Updated Allowance", + "target": 6000, + "weight": 15, + } + e.PUT("/user/1/allowance/" + strconv.Itoa(allowanceId)).WithJSON(updateRequest).Expect().Status(200) + + // Verify the allowance is updated + result := e.GET("/user/1/allowance/" + strconv.Itoa(allowanceId)).Expect().Status(200).JSON().Object() + result.Value("id").IsEqual(allowanceId) + result.Value("name").IsEqual("Updated Allowance") + result.Value("target").IsEqual(6000) + result.Value("weight").IsEqual(15) +} + func getDelta(base time.Time, delta float64) (time.Time, time.Time) { start := base.Add(-time.Duration(delta) * time.Second) end := base.Add(time.Duration(delta) * time.Second) diff --git a/backend/db.go b/backend/db.go index 1a1fcd8..e171cdf 100644 --- a/backend/db.go +++ b/backend/db.go @@ -160,6 +160,33 @@ func (db *Db) DeleteAllowance(userId int, allowanceId int) error { return nil } +func (db *Db) UpdateAllowance(userId int, allowanceId int, allowance *UpdateAllowanceRequest) error { + // Check if the allowance exists for the user + count := 0 + err := db.db.Query("select count(*) from allowances where id = ? and user_id = ?"). + Bind(allowanceId, userId).ScanSingle(&count) + if err != nil { + return err + } + if count == 0 { + return errors.New("allowance not found") + } + + tx, err := db.db.Begin() + if err != nil { + return err + } + defer tx.MustRollback() + + err = tx.Query("update allowances set name=?, target=?, weight=? where id = ? and user_id = ?"). + Bind(allowance.Name, allowance.Target, allowance.Weight, allowanceId, userId). + Exec() + if err != nil { + return err + } + return tx.Commit() +} + func (db *Db) CreateTask(task *CreateTaskRequest) (int, error) { tx, err := db.db.Begin() if err != nil { diff --git a/backend/dto.go b/backend/dto.go index e305014..78f75f6 100644 --- a/backend/dto.go +++ b/backend/dto.go @@ -44,6 +44,12 @@ type CreateAllowanceRequest struct { Weight int `json:"weight"` } +type UpdateAllowanceRequest struct { + Name string `json:"name"` + Target int `json:"target"` + Weight int `json:"weight"` +} + type CreateGoalResponse struct { ID int `json:"id"` } diff --git a/backend/main.go b/backend/main.go index 3260856..16b20a3 100644 --- a/backend/main.go +++ b/backend/main.go @@ -232,6 +232,52 @@ func deleteUserAllowance(c *gin.Context) { c.IndentedJSON(http.StatusOK, gin.H{"message": "History deleted successfully"}) } +func putUserAllowance(c *gin.Context) { + userIdStr := c.Param("userId") + allowanceIdStr := c.Param("allowanceId") + + userId, err := strconv.Atoi(userIdStr) + if err != nil { + log.Printf(ErrInvalidUserID+": %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID}) + return + } + + allowanceId, err := strconv.Atoi(allowanceIdStr) + if err != nil { + log.Printf("Invalid allowance ID: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid allowance ID"}) + return + } + + exists, err := db.UserExists(userId) + if err != nil { + log.Printf(ErrCheckingUserExist, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError}) + return + } + if !exists { + c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound}) + return + } + + var allowanceRequest UpdateAllowanceRequest + if err := c.ShouldBindJSON(&allowanceRequest); err != nil { + log.Printf("Error parsing request body: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) + return + } + + err = db.UpdateAllowance(userId, allowanceId, &allowanceRequest) + if err != nil { + log.Printf("Error updating allowance: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError}) + return + } + + c.IndentedJSON(http.StatusOK, gin.H{"message": "Allowance updated successfully"}) +} + func createTask(c *gin.Context) { var taskRequest CreateTaskRequest if err := c.ShouldBindJSON(&taskRequest); err != nil { @@ -439,6 +485,7 @@ func start(ctx context.Context, config *ServerConfig) { router.POST("/api/user/:userId/allowance", createUserAllowance) router.GET("/api/user/:userId/allowance/:allowanceId", getUserAllowanceById) router.DELETE("/api/user/:userId/allowance/:allowanceId", deleteUserAllowance) + router.PUT("/api/user/:userId/allowance/:allowanceId", putUserAllowance) router.POST("/api/tasks", createTask) router.GET("/api/tasks", getTasks) router.GET("/api/task/:taskId", getTask)