added rating books

This commit is contained in:
2025-10-29 22:38:38 +01:00
parent b4df375e4c
commit 26df6417b1
14 changed files with 353 additions and 153 deletions

View File

@@ -1,43 +0,0 @@
package apitest
import (
"fmt"
"log"
"net/http"
"net/http/httptest"
"strings"
"testing"
"git.artlef.fr/PersonalLibraryManager/internal/testutils"
"github.com/stretchr/testify/assert"
)
func TestPostBookReadHandler_Ok(t *testing.T) {
userBookJson :=
`{
"bookId": 6
}`
testPostBookReadHandler(t, userBookJson, http.StatusOK)
}
func TestPostBookReadHandler_IDDoesNotExist(t *testing.T) {
userBookJson :=
`{
"bookId": 46546
}`
testPostBookReadHandler(t, userBookJson, http.StatusNotFound)
}
func testPostBookReadHandler(t *testing.T, userBookJson string, expectedCode int) {
router := testutils.TestSetup()
w := httptest.NewRecorder()
token := testutils.ConnectDemo2User(router)
req, _ := http.NewRequest("POST", "/book/read",
strings.NewReader(string(userBookJson)))
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
router.ServeHTTP(w, req)
log.Printf("%s\n", w.Body.String())
assert.Equal(t, expectedCode, w.Code)
}

View File

@@ -0,0 +1,90 @@
package apitest
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"git.artlef.fr/PersonalLibraryManager/internal/testutils"
"github.com/stretchr/testify/assert"
)
func TestPutBookUpdate_NewReadOk(t *testing.T) {
payload :=
`{
"read": true
}`
testPutUserBooksHandler(t, payload, "21", http.StatusOK)
}
func TestPutUserBooksHandler_UpdateRating(t *testing.T) {
payload :=
`{
"rating": 5
}`
bookId := "17"
testPutUserBooksHandler(t, payload, bookId, http.StatusOK)
book := testGetBook(t, bookId, http.StatusOK)
assert.Equal(t, 5, book.Rating)
assert.Equal(t, true, book.Read)
}
func TestPutUserBooksHandler_RateNewBookMakeItRead(t *testing.T) {
payload :=
`{
"rating": 7
}`
bookId := "18"
testPutUserBooksHandler(t, payload, bookId, http.StatusOK)
book := testGetBook(t, bookId, http.StatusOK)
assert.Equal(t, 7, book.Rating)
assert.Equal(t, true, book.Read)
}
func TestPutUserBooksHandler_RatingTypeWrong(t *testing.T) {
payload :=
`{
"rating": "bad"
}`
bookId := "18"
testPutUserBooksHandler(t, payload, bookId, http.StatusInternalServerError)
}
func TestPutUserBooksHandler_RatingMin(t *testing.T) {
payload :=
`{
"rating": -3
}`
bookId := "18"
testPutUserBooksHandler(t, payload, bookId, http.StatusBadRequest)
}
func TestPutUserBooksHandler_RatingMax(t *testing.T) {
payload :=
`{
"rating": 15
}`
bookId := "18"
testPutUserBooksHandler(t, payload, bookId, http.StatusBadRequest)
}
func TestPutUserBooksHandler_BadBookId(t *testing.T) {
payload :=
`{
"rating": 15
}`
bookId := "18574"
testPutUserBooksHandler(t, payload, bookId, http.StatusNotFound)
}
func testPutUserBooksHandler(t *testing.T, payload string, bookId string, expectedCode int) {
router := testutils.TestSetup()
token := testutils.ConnectDemoUser(router)
req, _ := http.NewRequest("PUT", "/book/"+bookId, strings.NewReader(payload))
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, expectedCode, w.Code)
}

View File

@@ -1,63 +0,0 @@
package routes
import (
"errors"
"net/http"
"git.artlef.fr/PersonalLibraryManager/internal/appcontext"
"git.artlef.fr/PersonalLibraryManager/internal/model"
"git.artlef.fr/PersonalLibraryManager/internal/myvalidator"
"gorm.io/gorm"
)
type bookPostMarkAsRead struct {
BookID uint `json:"bookId" binding:"required"`
}
func PostBookReadHandler(ac appcontext.AppContext) {
var userbook bookPostMarkAsRead
err := ac.C.ShouldBindJSON(&userbook)
if err != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
err = myvalidator.ValidateId(ac.Db, userbook.BookID, &model.Book{})
if err != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
user, fetchUserErr := ac.GetAuthenticatedUser()
if fetchUserErr != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
var userbookDb model.UserBook
res := ac.Db.Where("user_id = ? AND book_id = ?", user.ID, userbook.BookID).First(&userbookDb)
err = res.Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
userbookDb = userBookWsToDb(userbook, &user)
err = ac.Db.Save(&userbookDb).Error
if err != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
} else {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
} else {
userbookDb.Read = true
ac.Db.Save(&userbookDb)
}
ac.C.String(http.StatusOK, "Success")
}
func userBookWsToDb(ub bookPostMarkAsRead, user *model.User) model.UserBook {
return model.UserBook{
UserID: user.ID,
BookID: ub.BookID,
Read: true,
}
}

View File

@@ -0,0 +1,13 @@
package routes
import "git.artlef.fr/PersonalLibraryManager/internal/appcontext"
type bookPutUpdate struct {
Title string `json:"title" binding:"required,max=300"`
Author string `json:"author" binding:"max=100"`
CoverID uint `json:"coverId"`
}
func PutBookHandler(ac appcontext.AppContext) {
}

View File

@@ -0,0 +1,80 @@
package routes
import (
"errors"
"net/http"
"strconv"
"git.artlef.fr/PersonalLibraryManager/internal/appcontext"
"git.artlef.fr/PersonalLibraryManager/internal/model"
"git.artlef.fr/PersonalLibraryManager/internal/myvalidator"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type userbookPutUpdate struct {
Read bool `json:"read"`
Rating int `json:"rating" binding:"min=0,max=10"`
}
func PutUserBookHandler(ac appcontext.AppContext) {
bookId64, err := strconv.ParseUint(ac.C.Param("id"), 10, 64)
bookId := uint(bookId64)
if err != nil {
ac.C.JSON(http.StatusBadRequest, gin.H{"error": err})
return
}
err = myvalidator.ValidateId(ac.Db, bookId, &model.Book{})
if err != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
var userbook userbookPutUpdate
err = ac.C.ShouldBindJSON(&userbook)
if err != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
user, fetchUserErr := ac.GetAuthenticatedUser()
if fetchUserErr != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
//a rating of 0 means no rating
// if there is a rating, read is forced to true
userbook.Read = userbook.Read || userbook.Rating > 0
var userbookDb model.UserBook
res := ac.Db.Where("user_id = ? AND book_id = ?", user.ID, bookId).First(&userbookDb)
err = res.Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
userbookDb = userBookWsToDb(userbook, bookId, &user)
err = ac.Db.Save(&userbookDb).Error
if err != nil {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
} else {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return
}
} else {
userbookDb.Read = userbook.Read
if userbook.Rating > 0 {
userbookDb.Rating = userbook.Rating
}
ac.Db.Save(&userbookDb)
}
ac.C.String(http.StatusOK, "Success")
}
func userBookWsToDb(ub userbookPutUpdate, bookId uint, user *model.User) model.UserBook {
return model.UserBook{
UserID: user.ID,
BookID: bookId,
Read: ub.Read,
Rating: ub.Rating,
}
}

View File

@@ -33,12 +33,12 @@ func Setup(config *config.Config) *gin.Engine {
r.GET("/book/:id", func(c *gin.Context) {
routes.GetBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.PUT("/book/:id", func(c *gin.Context) {
routes.PutUserBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.POST("/book", func(c *gin.Context) {
routes.PostBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.POST("/book/read", func(c *gin.Context) {
routes.PostBookReadHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.POST("/auth/signup", func(c *gin.Context) {
routes.PostSignupHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})