Add /user/{userId} (#24)

Closes #9

Reviewed-on: #24
This commit is contained in:
Sebastiaan de Schaetzen 2025-05-08 11:31:02 +02:00
parent 1baa1afb07
commit 5a19fc041d
5 changed files with 68 additions and 10 deletions

View File

@ -5,7 +5,7 @@ import (
) )
import "github.com/gavv/httpexpect/v2" import "github.com/gavv/httpexpect/v2"
func startServer(t *testing.T) { func startServer(t *testing.T) *httpexpect.Expect {
config := ServerConfig{ config := ServerConfig{
Datasource: ":memory:", Datasource: ":memory:",
Port: "8181", Port: "8181",
@ -13,13 +13,30 @@ func startServer(t *testing.T) {
} }
go start(t.Context(), &config) go start(t.Context(), &config)
<-config.Started <-config.Started
return httpexpect.Default(t, "http://localhost:8181/api")
} }
func TestGetUsers(t *testing.T) { func TestGetUsers(t *testing.T) {
startServer(t) e := startServer(t)
e := httpexpect.Default(t, "http://localhost:8181/api")
result := e.GET("/users").Expect().Status(200).JSON() result := e.GET("/users").Expect().Status(200).JSON()
result.Array().Length().IsEqual(2) result.Array().Length().IsEqual(2)
result.Path("$[0].name").InList("Seeseemelk", "Huffle") result.Path("$[0].name").InList("Seeseemelk", "Huffle")
result.Path("$[1].name").InList("Seeseemelk", "Huffle") result.Path("$[1].name").InList("Seeseemelk", "Huffle")
} }
func TestGetUser(t *testing.T) {
e := startServer(t)
result := e.GET("/user/1").Expect().Status(200).JSON().Object()
result.Value("name").IsEqual("Seeseemelk")
result.Value("id").IsEqual(1)
}
func TestGetUserUnknown(t *testing.T) {
e := startServer(t)
e.GET("/user/999").Expect().Status(404)
}
func TestGetUserBadId(t *testing.T) {
e := startServer(t)
e.GET("/user/bad-id").Expect().Status(400)
}

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"gitea.seeseepuff.be/seeseemelk/mysqlite" "gitea.seeseepuff.be/seeseemelk/mysqlite"
"log" "log"
) )
@ -42,3 +43,17 @@ func (db *Db) GetUsers() ([]User, error) {
} }
return users, nil return users, nil
} }
func (db *Db) GetUser(id int) (*User, error) {
user := &User{}
err := db.db.Query("select id, name from users where id = ?").
Bind(id).ScanSingle(&user.ID, &user.Name)
if errors.Is(err, mysqlite.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, err
}
return user, nil
}

View File

@ -3,7 +3,7 @@ module allowance_planner
go 1.24.2 go 1.24.2
require ( require (
gitea.seeseepuff.be/seeseemelk/mysqlite v0.9.0 gitea.seeseepuff.be/seeseemelk/mysqlite v0.10.0
github.com/gavv/httpexpect/v2 v2.17.0 github.com/gavv/httpexpect/v2 v2.17.0
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
) )

View File

@ -1,5 +1,5 @@
gitea.seeseepuff.be/seeseemelk/mysqlite v0.9.0 h1:GaU2DSrgDfZEqST3HdnNgfKSI4sNXvMm8SSfeMvBxA4= gitea.seeseepuff.be/seeseemelk/mysqlite v0.10.0 h1:xvbSZvLcHWlt+P4SE7vcgw3kih0kLrCqkOo/eXlbcSg=
gitea.seeseepuff.be/seeseemelk/mysqlite v0.9.0/go.mod h1:cgswydOxJjMlNwfcBIXnKjr47LwXnMT9BInkiHb0tXE= gitea.seeseepuff.be/seeseemelk/mysqlite v0.10.0/go.mod h1:cgswydOxJjMlNwfcBIXnKjr47LwXnMT9BInkiHb0tXE=
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4=
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=

View File

@ -8,6 +8,7 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"strconv"
) )
//go:embed migrations/*.sql //go:embed migrations/*.sql
@ -30,18 +31,36 @@ func getUsers(c *gin.Context) {
c.IndentedJSON(http.StatusOK, users) c.IndentedJSON(http.StatusOK, users)
} }
func main() { func getUser(c *gin.Context) {
config := ServerConfig{ userIdStr := c.Param("userId")
Datasource: os.Getenv("DB_PATH"), 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
} }
start(context.Background(), &config)
user, err := db.GetUser(userId)
if err != nil {
log.Printf("Error getting user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
return
}
if user == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
c.IndentedJSON(http.StatusOK, user)
} }
func start(ctx context.Context, config *ServerConfig) { func start(ctx context.Context, config *ServerConfig) {
db = NewDb(config.Datasource) db = NewDb(config.Datasource)
defer db.db.MustClose() defer db.db.MustClose()
router := gin.Default() router := gin.Default()
router.GET("/api/users", getUsers) router.GET("/api/users", getUsers)
router.GET("/api/user/:userId", getUser)
srv := &http.Server{ srv := &http.Server{
Addr: ":" + config.Port, Addr: ":" + config.Port,
@ -61,3 +80,10 @@ func start(ctx context.Context, config *ServerConfig) {
log.Fatalf("Server forced to shutdown: %v", err) log.Fatalf("Server forced to shutdown: %v", err)
} }
} }
func main() {
config := ServerConfig{
Datasource: os.Getenv("DB_PATH"),
}
start(context.Background(), &config)
}