diff --git a/api_test.go b/api_test.go
index a69878d..6d99c0f 100644
--- a/api_test.go
+++ b/api_test.go
@@ -63,6 +63,26 @@ func TestPostBookHandler_WrongRating(t *testing.T) {
testPostBookHandler(t, bookJson, 400)
}
+func TestPostBookHandler_TitleTooLong(t *testing.T) {
+ bookJson :=
+ `{
+ "title": "Noisy outlaws, unfriendly blobs, and some other things that aren't as scary, maybe, depending on how you feel about lost lands, stray cellphones, creatures from the sky, parents who disappear in Peru, a man named Lars Farf, and one other story we couldn't quite finish, so maybe you could help us out.",
+ "author": "Eli Horowitz",
+ "rating": 2
+ }`
+ testPostBookHandler(t, bookJson, 400)
+}
+
+func TestPostBookHandler_AuthorTooLong(t *testing.T) {
+ bookJson :=
+ `{
+ "title": "something",
+ "author": "Wolfeschlegelsteinhausenbergerdorffwelchevoralternwarengewissenhaftschaferswessenschafewarenwohlgepflegeundsorgfaltigkeitbeschutzenvonangreifendurchihrraubgierigfeindewelchevoralternzwolftausendjahresvorandieerscheinenvanderersteerdemenschderraumschiffgebrauchlichtalsseinursprungvonkraftgestartseinlangefahrthinzwischensternartigraumaufdersuchenachdiesternwelchegehabtbewohnbarplanetenkreisedrehensichundwohinderneurassevonverstandigmenschlichkeitkonntefortpflanzenundsicherfreuenanlebenslanglichfreudeundruhemitnichteinfurchtvorangreifenvonandererintelligentgeschopfsvonhinzwischensternartigraum",
+ "rating": 2
+ }`
+ testPostBookHandler(t, bookJson, 400)
+}
+
func testPostBookHandler(t *testing.T, bookJson string, expectedCode int) {
router := testSetup()
w := httptest.NewRecorder()
diff --git a/demodata.sql b/demodata.sql
index 50afdb5..4884646 100644
--- a/demodata.sql
+++ b/demodata.sql
@@ -25,3 +25,4 @@ INSERT INTO books(created_at, title, author, rating) VALUES ('NOW', 'Sa majesté
INSERT INTO books(created_at, title, author, rating) VALUES ('NOW', 'Le Pavillon d''or','Yukio Mishima', 8);
INSERT INTO books(created_at, title, author, rating) VALUES ('NOW', 'Le meurtre d''O-tsuya','Junichiro Tanizaki', 7);
INSERT INTO books(created_at, title, author, rating) VALUES ('NOW', 'Dojoji et autres nouvelles','Yukio Mishima', 8);
+INSERT INTO books(created_at, title, author, rating) VALUES ('NOW', 'Noisy outlaws, unfriendly blobs, and some other things that aren''t as scary, maybe, depending on how you feel about lost lands, stray cellphones, creatures from the sky, parents who disappear in Peru, a man named Lars Farf, and one other story we couldn''t quite finish, so maybe you could help us out','Wolfeschlegelsteinhausenbergerdorffwelchevoralternwarengewissenhaftschaferswessenschafewarenwohlgepf', 2);
diff --git a/front/src/AddBook.vue b/front/src/AddBook.vue
index 9609e92..a78c4f1 100644
--- a/front/src/AddBook.vue
+++ b/front/src/AddBook.vue
@@ -1,5 +1,5 @@
@@ -21,14 +47,17 @@
diff --git a/front/src/api.js b/front/src/api.js
index ecb5bb8..79ffd7b 100644
--- a/front/src/api.js
+++ b/front/src/api.js
@@ -3,13 +3,13 @@ import { ref } from 'vue'
const baseUrl = "http://localhost:8080"
function useFetch(url) {
- const data = ref(null)
- const error = ref(null)
+ const data = ref(null);
+ const error = ref(null);
fetch(url)
.then((res) => res.json())
.then((json) => (data.value = json))
- .catch((err) => (error.value = err))
+ .catch((err) => (error.value = err));
return { data, error }
}
@@ -19,11 +19,11 @@ export function getBooks() {
}
export function postBook(book) {
- fetch(baseUrl + '/book', {
+ return fetch(baseUrl + '/book', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(book.value)
- });
+ })
}
diff --git a/internal/api/dto.go b/internal/api/dto.go
index dbb0a56..7bd917a 100644
--- a/internal/api/dto.go
+++ b/internal/api/dto.go
@@ -1,7 +1,7 @@
package api
type bookPostCreate struct {
- Title string `json:"title" binding:"required"`
- Author string `json:"author"`
+ Title string `json:"title" binding:"required,max=300"`
+ Author string `json:"author" binding:"max=100"`
Rating int `json:"rating" binding:"min=0,max=10"`
}
diff --git a/internal/api/routes.go b/internal/api/routes.go
index ed17d2a..ad49b6d 100644
--- a/internal/api/routes.go
+++ b/internal/api/routes.go
@@ -1,10 +1,12 @@
package api
import (
+ "errors"
"net/http"
"git.artlef.fr/PersonalLibraryManager/internal/model"
"github.com/gin-gonic/gin"
+ "github.com/go-playground/validator/v10"
"gorm.io/gorm"
)
@@ -18,7 +20,12 @@ func PostBookHandler(c *gin.Context, db *gorm.DB) {
var book bookPostCreate
err := c.ShouldBindJSON(&book)
if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ var ve validator.ValidationErrors
+ if errors.As(err, &ve) {
+ c.JSON(http.StatusBadRequest, getValidationErrors(&ve))
+ } else {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ }
return
}
bookDb := book.toBook()
diff --git a/internal/api/validator.go b/internal/api/validator.go
new file mode 100644
index 0000000..a63cf3c
--- /dev/null
+++ b/internal/api/validator.go
@@ -0,0 +1,35 @@
+package api
+
+import (
+ "fmt"
+
+ "github.com/go-playground/validator/v10"
+)
+
+type apiValidationError struct {
+ Field string `json:"field"`
+ Err string `json:"error"`
+}
+
+func getValidationErrors(ve *validator.ValidationErrors) []apiValidationError {
+ errors := make([]apiValidationError, len(*ve))
+ for i, fe := range *ve {
+ errors[i] = apiValidationError{
+ Field: fe.Field(),
+ Err: computeValidationMessage(&fe),
+ }
+ }
+ return errors
+}
+
+func computeValidationMessage(fe *validator.FieldError) string {
+ tag := (*fe).Tag()
+ switch tag {
+ case "required":
+ return fmt.Sprintf("%s is required.", (*fe).Field())
+ case "max":
+ return fmt.Sprintf("%s is too long. It should be under %s characters.", (*fe).Field(), (*fe).Param())
+ default:
+ return fmt.Sprintf("Validation failed for '%s' property.", tag)
+ }
+}