Add delete goal endpoint #27
@ -1,11 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gavv/httpexpect/v2"
|
"github.com/gavv/httpexpect/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TestGoalName = "Test Goal"
|
||||||
|
)
|
||||||
|
|
||||||
func startServer(t *testing.T) *httpexpect.Expect {
|
func startServer(t *testing.T) *httpexpect.Expect {
|
||||||
config := ServerConfig{
|
config := ServerConfig{
|
||||||
Datasource: ":memory:",
|
Datasource: ":memory:",
|
||||||
@ -63,7 +68,7 @@ func TestCreateUserGoal(t *testing.T) {
|
|||||||
|
|
||||||
// Create a new goal
|
// Create a new goal
|
||||||
requestBody := map[string]interface{}{
|
requestBody := map[string]interface{}{
|
||||||
"name": "Test Goal",
|
"name": TestGoalName,
|
||||||
"target": 5000,
|
"target": 5000,
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
}
|
}
|
||||||
@ -88,7 +93,7 @@ func TestCreateUserGoal(t *testing.T) {
|
|||||||
|
|
||||||
goal := goals.Value(0).Object()
|
goal := goals.Value(0).Object()
|
||||||
goal.Value("id").IsEqual(goalId)
|
goal.Value("id").IsEqual(goalId)
|
||||||
goal.Value("name").IsEqual("Test Goal")
|
goal.Value("name").IsEqual(TestGoalName)
|
||||||
goal.Value("target").IsEqual(5000)
|
goal.Value("target").IsEqual(5000)
|
||||||
goal.Value("weight").IsEqual(10)
|
goal.Value("weight").IsEqual(10)
|
||||||
goal.Value("progress").IsEqual(0)
|
goal.Value("progress").IsEqual(0)
|
||||||
@ -98,7 +103,7 @@ func TestCreateUserGoalNoUser(t *testing.T) {
|
|||||||
e := startServer(t)
|
e := startServer(t)
|
||||||
|
|
||||||
requestBody := map[string]interface{}{
|
requestBody := map[string]interface{}{
|
||||||
"name": "Test Goal",
|
"name": TestGoalName,
|
||||||
"target": 5000,
|
"target": 5000,
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
}
|
}
|
||||||
@ -139,7 +144,7 @@ func TestCreateUserGoalBadId(t *testing.T) {
|
|||||||
e := startServer(t)
|
e := startServer(t)
|
||||||
|
|
||||||
requestBody := map[string]interface{}{
|
requestBody := map[string]interface{}{
|
||||||
"name": "Test Goal",
|
"name": TestGoalName,
|
||||||
"target": 5000,
|
"target": 5000,
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
}
|
}
|
||||||
@ -149,3 +154,54 @@ func TestCreateUserGoalBadId(t *testing.T) {
|
|||||||
Expect().
|
Expect().
|
||||||
Status(400)
|
Status(400)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteUserGoal(t *testing.T) {
|
||||||
|
e := startServer(t)
|
||||||
|
|
||||||
|
// Create a new goal to delete
|
||||||
|
createRequest := map[string]interface{}{
|
||||||
|
"name": TestGoalName,
|
||||||
|
"target": 1000,
|
||||||
|
"weight": 5,
|
||||||
|
}
|
||||||
|
response := e.POST("/user/1/goals").
|
||||||
|
WithJSON(createRequest).
|
||||||
|
Expect().
|
||||||
|
Status(201).
|
||||||
|
JSON().Object()
|
||||||
|
|
||||||
|
goalId := response.Value("id").Number().Raw()
|
||||||
|
|
||||||
|
// Delete the goal
|
||||||
|
e.DELETE("/user/1/goal/" + strconv.Itoa(int(goalId))).
|
||||||
|
Expect().
|
||||||
|
Status(200).
|
||||||
|
JSON().Object().Value("message").IsEqual("Goal deleted successfully")
|
||||||
|
|
||||||
|
// Verify the goal no longer exists
|
||||||
|
goals := e.GET("/user/1/goals").
|
||||||
|
Expect().
|
||||||
|
Status(200).
|
||||||
|
JSON().Array()
|
||||||
|
goals.Length().IsEqual(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteUserGoalNotFound(t *testing.T) {
|
||||||
|
e := startServer(t)
|
||||||
|
|
||||||
|
// Attempt to delete a non-existent goal
|
||||||
|
e.DELETE("/user/1/goal/999").
|
||||||
|
Expect().
|
||||||
|
Status(404).
|
||||||
|
JSON().Object().Value("error").IsEqual("Goal not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteUserGoalInvalidId(t *testing.T) {
|
||||||
|
e := startServer(t)
|
||||||
|
|
||||||
|
// Attempt to delete a goal with an invalid ID
|
||||||
|
e.DELETE("/user/1/goal/invalid-id").
|
||||||
|
Expect().
|
||||||
|
Status(400).
|
||||||
|
JSON().Object().Value("error").IsEqual("Invalid goal ID")
|
||||||
|
}
|
||||||
|
@ -128,3 +128,25 @@ func (db *Db) CreateGoal(userId int, goal *CreateGoalRequest) (int, error) {
|
|||||||
|
|
||||||
return lastId, nil
|
return lastId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Db) DeleteGoal(userId int, goalId int) error {
|
||||||
|
// Check if the goal exists for the user
|
||||||
|
count := 0
|
||||||
|
err := db.db.Query("select count(*) from goals where id = ? and user_id = ?").
|
||||||
|
Bind(goalId, userId).ScanSingle(&count)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
return errors.New("goal not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the goal
|
||||||
|
err = db.db.Query("delete from goals where id = ? and user_id = ?").
|
||||||
|
Bind(goalId, userId).Exec()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -16,6 +16,13 @@ import (
|
|||||||
var migrations embed.FS
|
var migrations embed.FS
|
||||||
var db *Db
|
var db *Db
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrInternalServerError = "Internal Server Error"
|
||||||
|
ErrInvalidUserID = "Invalid user ID"
|
||||||
|
ErrUserNotFound = "User not found"
|
||||||
|
ErrCheckingUserExist = "Error checking user existence: %v"
|
||||||
|
)
|
||||||
|
|
||||||
// ServerConfig holds configuration for the server.
|
// ServerConfig holds configuration for the server.
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
// The datasource to the SQLite database.
|
// The datasource to the SQLite database.
|
||||||
@ -34,7 +41,7 @@ func getUsers(c *gin.Context) {
|
|||||||
users, err := db.GetUsers()
|
users, err := db.GetUsers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting users: %v", err)
|
log.Printf("Error getting users: %v", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.IndentedJSON(http.StatusOK, users)
|
c.IndentedJSON(http.StatusOK, users)
|
||||||
@ -44,19 +51,19 @@ func getUser(c *gin.Context) {
|
|||||||
userIdStr := c.Param("userId")
|
userIdStr := c.Param("userId")
|
||||||
userId, err := strconv.Atoi(userIdStr)
|
userId, err := strconv.Atoi(userIdStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Invalid user ID: %v", err)
|
log.Printf(ErrInvalidUserID+": %v", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.GetUser(userId)
|
user, err := db.GetUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting user: %v", err)
|
log.Printf("Error getting user: %v", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if user == nil {
|
if user == nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
|
c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,27 +74,27 @@ func getUserGoals(c *gin.Context) {
|
|||||||
userIdStr := c.Param("userId")
|
userIdStr := c.Param("userId")
|
||||||
userId, err := strconv.Atoi(userIdStr)
|
userId, err := strconv.Atoi(userIdStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Invalid user ID: %v", err)
|
log.Printf(ErrInvalidUserID+": %v", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, err := db.UserExists(userId)
|
exists, err := db.UserExists(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error checking user existence: %v", err)
|
log.Printf(ErrCheckingUserExist, err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
log.Printf("Error checking user existence: %v", err)
|
log.Printf(ErrCheckingUserExist, err)
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
|
c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
goals, err := db.GetUserGoals(userId)
|
goals, err := db.GetUserGoals(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting user goals: %v", err)
|
log.Printf("Error getting user goals: %v", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.IndentedJSON(http.StatusOK, goals)
|
c.IndentedJSON(http.StatusOK, goals)
|
||||||
@ -97,8 +104,8 @@ func createUserGoal(c *gin.Context) {
|
|||||||
userIdStr := c.Param("userId")
|
userIdStr := c.Param("userId")
|
||||||
userId, err := strconv.Atoi(userIdStr)
|
userId, err := strconv.Atoi(userIdStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Invalid user ID: %v", err)
|
log.Printf(ErrInvalidUserID+": %v", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +128,7 @@ func createUserGoal(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error creating goal: %v", err)
|
log.Printf("Error creating goal: %v", err)
|
||||||
if err.Error() == "user does not exist" {
|
if err.Error() == "user does not exist" {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
|
c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound})
|
||||||
} else {
|
} else {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Could not create goal"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Could not create goal"})
|
||||||
}
|
}
|
||||||
@ -133,6 +140,49 @@ func createUserGoal(c *gin.Context) {
|
|||||||
c.IndentedJSON(http.StatusCreated, response)
|
c.IndentedJSON(http.StatusCreated, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deleteUserGoal(c *gin.Context) {
|
||||||
|
userIdStr := c.Param("userId")
|
||||||
|
goalIdStr := c.Param("goalId")
|
||||||
|
|
||||||
|
userId, err := strconv.Atoi(userIdStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf(ErrInvalidUserID+": %v", err)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
goalId, err := strconv.Atoi(goalIdStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Invalid goal ID: %v", err)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.DeleteGoal(userId, goalId)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "goal not found" {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"})
|
||||||
|
} else {
|
||||||
|
log.Printf("Error deleting goal: %v", err)
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "Goal deleted successfully"})
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
Initialises the database, and then starts the server.
|
Initialises the database, and then starts the server.
|
||||||
@ -147,6 +197,7 @@ func start(ctx context.Context, config *ServerConfig) {
|
|||||||
router.GET("/api/user/:userId", getUser)
|
router.GET("/api/user/:userId", getUser)
|
||||||
router.GET("/api/user/:userId/goals", getUserGoals)
|
router.GET("/api/user/:userId/goals", getUserGoals)
|
||||||
router.POST("/api/user/:userId/goals", createUserGoal)
|
router.POST("/api/user/:userId/goals", createUserGoal)
|
||||||
|
router.DELETE("/api/user/:userId/goal/:goalId", deleteUserGoal)
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: ":" + config.Port,
|
Addr: ":" + config.Port,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user