package routes import ( "errors" "net/http" "strconv" "time" "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" ) func PutReadUserBookHandler(ac appcontext.AppContext) { data, err := retrieveDataFromContext(ac) if err != nil { return } bookId := data.BookId user := data.User var read userbookPutRead err = ac.C.ShouldBindJSON(&read) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return } userbook, err := fetchOrCreateUserBook(ac, bookId, &user) if err != nil { return } userbook.Read = read.Read if read.EndDate != "" { d, err := parseDate(read.EndDate) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return } userbook.EndReadDate = d } //remove the book from "wanted" list when it is marked as read. if userbook.Read { userbook.WantRead = false } //clear the date when unread if !userbook.Read { userbook.EndReadDate = nil } ac.Db.Save(&userbook) ac.C.String(http.StatusOK, "Success") } func PutWantReadUserBookHandler(ac appcontext.AppContext) { data, err := retrieveDataFromContext(ac) if err != nil { return } bookId := data.BookId user := data.User var wantread userbookPutWantRead err = ac.C.ShouldBindJSON(&wantread) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return } userbook, err := fetchOrCreateUserBook(ac, bookId, &user) if err != nil { return } userbook.WantRead = wantread.WantRead ac.Db.Save(&userbook) ac.C.String(http.StatusOK, "Success") } func PutStartReadUserBookHandler(ac appcontext.AppContext) { data, err := retrieveDataFromContext(ac) if err != nil { return } bookId := data.BookId user := data.User var startDateToParse userbookPutStartRead err = ac.C.ShouldBindJSON(&startDateToParse) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return } userbook, err := fetchOrCreateUserBook(ac, bookId, &user) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return } d, err := parseDate(startDateToParse.StartDate) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return } userbook.StartReadDate = d ac.Db.Save(&userbook) ac.C.String(http.StatusOK, "Success") } func PutRateUserBookHandler(ac appcontext.AppContext) { data, err := retrieveDataFromContext(ac) if err != nil { return } bookId := data.BookId user := data.User var rating userbookPutRating err = ac.C.ShouldBindJSON(&rating) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return } userbook, err := fetchOrCreateUserBook(ac, bookId, &user) if err != nil { return } userbook.Rating = rating.Rating //if rated, set to "read" (a rating = 0 means unrated) if userbook.Rating > 0 { userbook.Read = true //if set to read, remove want read userbook.WantRead = false } ac.Db.Save(&userbook) ac.C.String(http.StatusOK, "Success") } type userbookPutRead struct { Read bool `json:"read"` EndDate string `json:"endDate"` } type userbookPutWantRead struct { WantRead bool `json:"wantread"` } type userbookPutRating struct { Rating int `json:"rating" binding:"min=0,max=10"` } type userbookPutStartRead struct { StartDate string `json:"startDate" binding:"required"` } type apiCallData struct { BookId uint User model.User } func parseDate(dateToParse string) (*time.Time, error) { //string equal to "null" to unset value if dateToParse == "null" { return nil, nil } else { startDate, err := time.Parse(time.DateOnly, dateToParse) return &startDate, err } } func retrieveDataFromContext(ac appcontext.AppContext) (apiCallData, error) { 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 apiCallData{}, err } err = myvalidator.ValidateId(ac.Db, bookId, &model.Book{}) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return apiCallData{}, err } user, fetchUserErr := ac.GetAuthenticatedUser() if fetchUserErr != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return apiCallData{}, fetchUserErr } return apiCallData{BookId: bookId, User: user}, nil } func fetchOrCreateUserBook(ac appcontext.AppContext, bookId uint, user *model.User) (model.UserBook, error) { var userbook model.UserBook res := ac.Db.Where("user_id = ? AND book_id = ?", user.ID, bookId).First(&userbook) err := res.Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { userbook = createUserBook(bookId, user) err = ac.Db.Save(&userbook).Error if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return userbook, err } return userbook, nil } else { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return userbook, err } } else { return userbook, nil } } func createUserBook(bookId uint, user *model.User) model.UserBook { return model.UserBook{ UserID: user.ID, BookID: bookId, Read: false, WantRead: false, Rating: 0, } }