Initial work towards get allowance endpoint

This commit is contained in:
Sebastiaan de Schaetzen 2025-05-13 19:37:50 +02:00
parent 92d0514c04
commit eb3a4e3d34
6 changed files with 91 additions and 36 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/gavv/httpexpect/v2" "github.com/gavv/httpexpect/v2"
"strconv" "strconv"
"testing" "testing"
"time"
) )
const ( const (
@ -35,6 +36,7 @@ func TestGetUser(t *testing.T) {
result := e.GET("/user/1").Expect().Status(200).JSON().Object() result := e.GET("/user/1").Expect().Status(200).JSON().Object()
result.Value("name").IsEqual("Seeseemelk") result.Value("name").IsEqual("Seeseemelk")
result.Value("id").IsEqual(1) result.Value("id").IsEqual(1)
result.Value("allowance").IsEqual(0)
} }
func TestGetUserUnknown(t *testing.T) { func TestGetUserUnknown(t *testing.T) {
@ -403,3 +405,18 @@ func TestPostAllowanceInvalidUserId(t *testing.T) {
e.POST("/user/999/allowance").WithJSON(PostAllowance{Allowance: 100}).Expect(). e.POST("/user/999/allowance").WithJSON(PostAllowance{Allowance: 100}).Expect().
Status(404) Status(404)
} }
func TestGetAllowance(t *testing.T) {
e := startServer(t)
e.POST("/user/1/allowance").WithJSON(PostAllowance{Allowance: 100}).Expect().Status(200)
e.POST("/user/1/allowance").WithJSON(PostAllowance{Allowance: 20}).Expect().Status(200)
e.POST("/user/1/allowance").WithJSON(PostAllowance{Allowance: -10}).Expect().Status(200)
response := e.GET("/user/1/allowance").Expect().Status(200).JSON().Array()
response.Length().IsEqual(3)
response.Value(0).Object().Value("allowance").Number().IsEqual(100)
response.Value(0).Object().Value("timestamp").Number().NotEqual(0).InDelta(float64(time.Now().Unix()), 2.0)
response.Value(1).Object().Value("allowance").Number().IsEqual(20)
response.Value(2).Object().Value("allowance").Number().IsEqual(-10)
}

View File

@ -243,7 +243,7 @@ func (db *Db) AddAllowance(userId int, allowance *PostAllowance) error {
} }
defer tx.MustRollback() defer tx.MustRollback()
err = tx.Query("insert into history (user_id, date, amount) values (?, ?, ?)"). err = tx.Query("insert into history (user_id, timestamp, amount) values (?, ?, ?)").
Bind(userId, time.Now().Unix(), allowance.Allowance). Bind(userId, time.Now().Unix(), allowance.Allowance).
Exec() Exec()
if err != nil { if err != nil {
@ -251,3 +251,22 @@ func (db *Db) AddAllowance(userId int, allowance *PostAllowance) error {
} }
return tx.Commit() return tx.Commit()
} }
func (db *Db) GetHistory(userId int) ([]Allowance, error) {
history := make([]Allowance, 0)
var err error
for row := range db.db.Query("select amount from history where user_id = ? order by `timestamp` desc").
Bind(userId).Range(&err) {
allowance := Allowance{}
err = row.Scan(&allowance.Allowance)
if err != nil {
return nil, err
}
history = append(history, allowance)
}
if err != nil {
return nil, err
}
return history, nil
}

View File

@ -1,5 +1,7 @@
package main package main
import "time"
type User struct { type User struct {
ID int `json:"id"` ID int `json:"id"`
Name string `json:"name"` Name string `json:"name"`
@ -13,7 +15,7 @@ type UserWithAllowance struct {
type Allowance struct { type Allowance struct {
Allowance int `json:"allowance"` Allowance int `json:"allowance"`
Goals []Goal `json:"goals"` Timestamp time.Time
} }
type PostAllowance struct { type PostAllowance struct {

View File

@ -185,7 +185,7 @@ func deleteUserGoal(c *gin.Context) {
return return
} }
c.JSON(http.StatusOK, gin.H{"message": "Goal deleted successfully"}) c.IndentedJSON(http.StatusOK, gin.H{"message": "Goal deleted successfully"})
} }
func createTask(c *gin.Context) { func createTask(c *gin.Context) {
@ -233,7 +233,7 @@ func getTasks(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError}) c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
return return
} }
c.JSON(http.StatusOK, &response) c.IndentedJSON(http.StatusOK, &response)
} }
func getTask(c *gin.Context) { func getTask(c *gin.Context) {
@ -325,8 +325,26 @@ func postAllowance(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "Allowance updated successfully"}) c.JSON(http.StatusOK, gin.H{"message": "Allowance updated successfully"})
} }
func getAllowance(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
}
history, err := db.GetHistory(userId)
if err != nil {
log.Printf("Error getting history: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
return
}
c.IndentedJSON(http.StatusOK, history)
}
/* /*
*
Initialises the database, and then starts the server. Initialises the database, and then starts the server.
If the context gets cancelled, the server is shutdown and the database is closed. If the context gets cancelled, the server is shutdown and the database is closed.
*/ */
@ -337,6 +355,8 @@ func start(ctx context.Context, config *ServerConfig) {
router := gin.Default() router := gin.Default()
router.GET("/api/users", getUsers) router.GET("/api/users", getUsers)
router.GET("/api/user/:userId", getUser) router.GET("/api/user/:userId", getUser)
router.POST("/api/user/:userId/allowance", postAllowance)
//router.GET("/api/user/:userId/allowance", getAllowance)
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) router.DELETE("/api/user/:userId/goal/:goalId", deleteUserGoal)
@ -344,7 +364,6 @@ func start(ctx context.Context, config *ServerConfig) {
router.GET("/api/tasks", getTasks) router.GET("/api/tasks", getTasks)
router.GET("/api/task/:taskId", getTask) router.GET("/api/task/:taskId", getTask)
router.PUT("/api/task/:taskId", putTask) router.PUT("/api/task/:taskId", putTask)
router.POST("/api/user/:userId/allowance", postAllowance)
srv := &http.Server{ srv := &http.Server{
Addr: config.Addr, Addr: config.Addr,

View File

@ -8,7 +8,7 @@ create table history
( (
id integer primary key, id integer primary key,
user_id integer not null, user_id integer not null,
date date not null, timestamp date not null,
amount integer not null amount integer not null
); );

View File

@ -60,6 +60,32 @@ paths:
description: The users could not be found. description: The users could not be found.
/user/{userId}/allowance: /user/{userId}/allowance:
get:
summary: Gets the allowance history of a user
parameters:
- in: path
name: userId
description: The user ID
required: true
schema:
type: integer
responses:
200:
description: Information about the allowance history of the user
content:
application/json:
schema:
type: array
items:
type: object
properties:
date:
type: string
format: date-time
description: The date of the allowance or expense.
amount:
type: integer
description: The amount of the allowance to be added, in cents. A negative value
post: post:
summary: Updates the allowance of a user summary: Updates the allowance of a user
parameters: parameters:
@ -88,34 +114,6 @@ paths:
400: 400:
description: The allowance could not be updated. description: The allowance could not be updated.
/user/{userId}/history:
get:
summary: Gets the allowance history of a user
parameters:
- in: path
name: userId
description: The user ID
required: true
schema:
type: integer
responses:
200:
description: Information about the allowance history of the user
content:
application/json:
schema:
type: array
items:
type: object
properties:
date:
type: string
format: date-time
description: The date of the allowance or expense.
amount:
type: integer
description: The amount of the allowance to be added, in cents. A negative value
/user/{userId}/goals: /user/{userId}/goals:
get: get:
summary: Gets all goals summary: Gets all goals