Initial work towards get allowance endpoint
This commit is contained in:
		@@ -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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user