@@ -40,3 +40,19 @@ func TestGetUserBadId(t *testing.T) {
 | 
			
		||||
	e := startServer(t)
 | 
			
		||||
	e.GET("/user/bad-id").Expect().Status(400)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetUserGoalsWhenNoGoalsPresent(t *testing.T) {
 | 
			
		||||
	e := startServer(t)
 | 
			
		||||
	result := e.GET("/user/1/goals").Expect().Status(200).JSON().Array()
 | 
			
		||||
	result.Length().IsEqual(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetUserGoalsNoUser(t *testing.T) {
 | 
			
		||||
	e := startServer(t)
 | 
			
		||||
	e.GET("/user/999/goals").Expect().Status(404)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetUserGoalsBadId(t *testing.T) {
 | 
			
		||||
	e := startServer(t)
 | 
			
		||||
	e.GET("/user/bad-id/goals").Expect().Status(400)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -57,3 +57,32 @@ func (db *Db) GetUser(id int) (*User, error) {
 | 
			
		||||
	}
 | 
			
		||||
	return user, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *Db) UserExists(userId int) (bool, error) {
 | 
			
		||||
	count := 0
 | 
			
		||||
	err := db.db.Query("select count(*) from users where id = ?").
 | 
			
		||||
		Bind(userId).ScanSingle(&count)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	return count > 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *Db) GetUserGoals(userId int) ([]Goal, error) {
 | 
			
		||||
	goals := make([]Goal, 0)
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	for row := range db.db.Query("select id, name, target, progress, weight from goals where user_id = ?").
 | 
			
		||||
		Bind(userId).Range(&err) {
 | 
			
		||||
		goal := Goal{}
 | 
			
		||||
		err = row.Scan(&goal.ID, &goal.Name, &goal.Target, &goal.Progress, &goal.Weight)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		goals = append(goals, goal)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return goals, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,18 @@ import (
 | 
			
		||||
var migrations embed.FS
 | 
			
		||||
var db *Db
 | 
			
		||||
 | 
			
		||||
// ServerConfig holds configuration for the server.
 | 
			
		||||
type ServerConfig struct {
 | 
			
		||||
	// The datasource to the SQLite database.
 | 
			
		||||
	// Use ":memory:" for an in-memory database.
 | 
			
		||||
	Datasource string
 | 
			
		||||
	Port       string
 | 
			
		||||
	Started    chan bool
 | 
			
		||||
 | 
			
		||||
	// The port to listen on.
 | 
			
		||||
	// Use an empty string to listen on a random port.
 | 
			
		||||
	Port string
 | 
			
		||||
 | 
			
		||||
	// The channel that gets signaled when the server has started.
 | 
			
		||||
	Started chan bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUsers(c *gin.Context) {
 | 
			
		||||
@@ -54,6 +62,41 @@ func getUser(c *gin.Context) {
 | 
			
		||||
	c.IndentedJSON(http.StatusOK, user)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUserGoals(c *gin.Context) {
 | 
			
		||||
	userIdStr := c.Param("userId")
 | 
			
		||||
	userId, err := strconv.Atoi(userIdStr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("Invalid user ID: %v", err)
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	exists, err := db.UserExists(userId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("Error checking user existence: %v", err)
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !exists {
 | 
			
		||||
		log.Printf("Error checking user existence: %v", err)
 | 
			
		||||
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	goals, err := db.GetUserGoals(userId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("Error getting user goals: %v", err)
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.IndentedJSON(http.StatusOK, goals)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
*
 | 
			
		||||
Initialises the database, and then starts the server.
 | 
			
		||||
If the context gets cancelled, the server is shutdown and the database is closed.
 | 
			
		||||
*/
 | 
			
		||||
func start(ctx context.Context, config *ServerConfig) {
 | 
			
		||||
	db = NewDb(config.Datasource)
 | 
			
		||||
	defer db.db.MustClose()
 | 
			
		||||
@@ -61,6 +104,7 @@ func start(ctx context.Context, config *ServerConfig) {
 | 
			
		||||
	router := gin.Default()
 | 
			
		||||
	router.GET("/api/users", getUsers)
 | 
			
		||||
	router.GET("/api/user/:userId", getUser)
 | 
			
		||||
	router.GET("/api/user/:userId/goals", getUserGoals)
 | 
			
		||||
 | 
			
		||||
	srv := &http.Server{
 | 
			
		||||
		Addr:    ":" + config.Port,
 | 
			
		||||
@@ -73,7 +117,9 @@ func start(ctx context.Context, config *ServerConfig) {
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	log.Printf("Running server on port %s\n", config.Port)
 | 
			
		||||
	config.Started <- true
 | 
			
		||||
	if config.Started != nil {
 | 
			
		||||
		config.Started <- true
 | 
			
		||||
	}
 | 
			
		||||
	<-ctx.Done()
 | 
			
		||||
	log.Println("Shutting down")
 | 
			
		||||
	if err := srv.Shutdown(context.Background()); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ create table history
 | 
			
		||||
create table goals
 | 
			
		||||
(
 | 
			
		||||
    id integer primary key,
 | 
			
		||||
	user_id integer not null,
 | 
			
		||||
    name text not null,
 | 
			
		||||
    target integer not null,
 | 
			
		||||
    progress integer not null,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user