diff --git a/front/src/AddCollection.vue b/front/src/AddCollection.vue new file mode 100644 index 0000000..3c6657a --- /dev/null +++ b/front/src/AddCollection.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/front/src/CollectionsBrowser.vue b/front/src/CollectionsBrowser.vue index b032ac0..7bf9d86 100644 --- a/front/src/CollectionsBrowser.vue +++ b/front/src/CollectionsBrowser.vue @@ -3,6 +3,7 @@ import { ref, computed } from 'vue' import { getCollections } from './api.js' import CollectionListElement from './CollectionListElement.vue' import Pagination from './Pagination.vue' +import AddCollection from './AddCollection.vue' const limit = 5 const pageNumber = ref(1) @@ -34,6 +35,7 @@ function pageChange(newPageNumber) {
{{ $t('bookbrowser.error', { error: error.message }) }}
+
diff --git a/front/src/api.js b/front/src/api.js index 2eea003..2419831 100644 --- a/front/src/api.js +++ b/front/src/api.js @@ -121,6 +121,10 @@ export async function postImportBook(id, language) { return genericPayloadCall('/ws/importbook', { inventaireid: id, lang: language }, 'POST') } +export function postCollection(collection) { + return genericPayloadCall('/ws/collection', collection, 'POST') +} + export function putBook(id, book) { return genericPayloadCall('/ws/book/edit/' + id, book.value, 'PUT') } diff --git a/front/src/locales/en.json b/front/src/locales/en.json index c792a11..1d9fbc9 100644 --- a/front/src/locales/en.json +++ b/front/src/locales/en.json @@ -89,5 +89,9 @@ "review": { "title": "My review", "textplaceholder": "Write my review..." + }, + "collections": { + "add": "Add a collection", + "name": "Nom" } } diff --git a/front/src/locales/fr.json b/front/src/locales/fr.json index e3b1b2a..ce45bbf 100644 --- a/front/src/locales/fr.json +++ b/front/src/locales/fr.json @@ -89,5 +89,9 @@ "review": { "title": "Ma critique", "textplaceholder": "Écrire ma critique..." + }, + "collections": { + "add": "Ajouter une liste", + "name": "Nom" } } diff --git a/internal/apitest/post_book_test.go b/internal/apitest/post_book_test.go index 3902717..2fdd1e5 100644 --- a/internal/apitest/post_book_test.go +++ b/internal/apitest/post_book_test.go @@ -1,12 +1,8 @@ package apitest import ( - "encoding/json" - "fmt" "net/http" - "net/http/httptest" "strconv" - "strings" "testing" "git.artlef.fr/bibliomane/internal/testutils" @@ -88,27 +84,7 @@ func TestPostBookHandler_AuthorTooLong(t *testing.T) { } func testPostBookHandler(t *testing.T, bookJson string, expectedCode int) uint { - router := testutils.TestSetup() - w := httptest.NewRecorder() - - token := testutils.ConnectDemoUser(router) - req, _ := http.NewRequest("POST", "/ws/book", - strings.NewReader(string(bookJson))) - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token)) - router.ServeHTTP(w, req) - - assert.Equal(t, expectedCode, w.Code) - - if w.Code != http.StatusOK { - return 0 - } - var parsed struct { - ID uint - } - - err := json.Unmarshal(w.Body.Bytes(), &parsed) - if err != nil { - t.Error(err) - } - return parsed.ID + status, id := testutils.TestPostCall(t, "/ws/book", bookJson) + assert.Equal(t, expectedCode, status) + return id } diff --git a/internal/apitest/post_collection_test.go b/internal/apitest/post_collection_test.go new file mode 100644 index 0000000..64bd0e0 --- /dev/null +++ b/internal/apitest/post_collection_test.go @@ -0,0 +1,31 @@ +package apitest + +import ( + "net/http" + "testing" + + "git.artlef.fr/bibliomane/internal/testutils" + "github.com/stretchr/testify/assert" +) + +func TestPostCollectionHandler_Ok(t *testing.T) { + collectionJson := + `{ + "name": "My collection" + }` + status, _ := testPostCollectionHandler(t, collectionJson) + assert.Equal(t, http.StatusOK, status) +} + +func TestPostCollectionHandler_NameTooLong(t *testing.T) { + collectionJson := + `{ + "name": "rsteerdemenschderraumschiffgebrauchlichtalsseinursprungvonkraftgestartseinlangefahrthinzwischensternartigraumaufdersuchenrsteerdemenschderraumschiffgebrauchlichtalsseinursprungvonkraftgestartseinlangefahrthinzwischensternartigraumaufdersuchenrsteerdemenschderraumschiffgebrauchlichtalsseinursprungvonkraftgestartseinlangefahrthinzwischensternartigraumaufdersuchen" + }` + status, _ := testPostCollectionHandler(t, collectionJson) + assert.Equal(t, http.StatusBadRequest, status) +} + +func testPostCollectionHandler(t *testing.T, collectionJson string) (int, uint) { + return testutils.TestPostCall(t, "/ws/collection", collectionJson) +} diff --git a/internal/dto/in.go b/internal/dto/in.go index 2d2188f..fecd451 100644 --- a/internal/dto/in.go +++ b/internal/dto/in.go @@ -35,6 +35,10 @@ type UserBookPutUpdate struct { Review *string `json:"review"` } +type CollectionFields struct { + Name string `json:"name" binding:"required,max=300"` +} + type FileInfoPost struct { FileID uint `json:"fileId"` FilePath string `json:"filepath"` diff --git a/internal/routes/collectionspost.go b/internal/routes/collectionspost.go new file mode 100644 index 0000000..dd616b7 --- /dev/null +++ b/internal/routes/collectionspost.go @@ -0,0 +1,41 @@ +package routes + +import ( + "net/http" + + "git.artlef.fr/bibliomane/internal/appcontext" + "git.artlef.fr/bibliomane/internal/dto" + "git.artlef.fr/bibliomane/internal/model" + "git.artlef.fr/bibliomane/internal/myvalidator" + "github.com/gin-gonic/gin" +) + +func PostCollectionHandler(ac appcontext.AppContext) { + var collection dto.CollectionFields + err := ac.C.ShouldBindJSON(&collection) + if err != nil { + myvalidator.ReturnErrorsAsJsonResponse(&ac, err) + return + } + user, fetchUserErr := ac.GetAuthenticatedUser() + if fetchUserErr != nil { + myvalidator.ReturnErrorsAsJsonResponse(&ac, err) + return + } + + id, err := saveCollectionToDb(ac, &collection, &user) + if err != nil { + myvalidator.ReturnErrorsAsJsonResponse(&ac, err) + return + } + ac.C.JSON(http.StatusOK, gin.H{"id": id}) +} + +func saveCollectionToDb(ac appcontext.AppContext, c *dto.CollectionFields, user *model.User) (uint, error) { + collection := model.Collection{ + Name: c.Name, + User: *user, + } + err := ac.Db.Save(&collection).Error + return collection.ID, err +} diff --git a/internal/setup/setup.go b/internal/setup/setup.go index a2f803e..d1413cc 100644 --- a/internal/setup/setup.go +++ b/internal/setup/setup.go @@ -81,6 +81,9 @@ func Setup(config *config.Config) *gin.Engine { ws.GET("/collections", func(c *gin.Context) { routes.GetCollectionsHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config}) }) + ws.POST("/collection", func(c *gin.Context) { + routes.PostCollectionHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config}) + }) ws.POST("/auth/signup", func(c *gin.Context) { routes.PostSignupHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config}) }) diff --git a/internal/testutils/testutils.go b/internal/testutils/testutils.go index a34d220..1f0ad4a 100644 --- a/internal/testutils/testutils.go +++ b/internal/testutils/testutils.go @@ -104,3 +104,27 @@ func TestFetchModel[T any](t *testing.T, urlpath string, limit string, offset st } return w.Code, result } + +func TestPostCall(t *testing.T, urlpath string, payload string) (int, uint) { + router := TestSetup() + w := httptest.NewRecorder() + + token := ConnectDemoUser(router) + req, _ := http.NewRequest("POST", urlpath, + strings.NewReader(payload)) + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token)) + router.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + return w.Code, 0 + } + var parsed struct { + ID uint + } + + err := json.Unmarshal(w.Body.Bytes(), &parsed) + if err != nil { + t.Error(err) + } + return w.Code, parsed.ID +}