diff --git a/demodata.sql b/demodata.sql index 43bf7be..6c5cdee 100644 --- a/demodata.sql +++ b/demodata.sql @@ -132,18 +132,18 @@ INSERT INTO collections(name, user_id) VALUES ('Nouvelles',(SELECT id FROM users INSERT INTO collections(name, user_id) VALUES ('Non fiction',(SELECT id FROM users WHERE name = 'demo2')); INSERT INTO collections(name, user_id) VALUES ('Empty',(SELECT id FROM users WHERE name = 'demo')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Nord')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Gargantua')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Duo')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Un barrage contre le Pacifique')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Rigodon')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Les dieux ont soif')); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Nord'), 1); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Gargantua'), 2); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Duo'), 3); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Un barrage contre le Pacifique'), 4); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Rigodon'), 5); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Les dieux ont soif'), 6); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Dojoji et autres nouvelles')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le meurtre d''O-tsuya')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le coup de pistolet')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Duo')); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Dojoji et autres nouvelles'), 1); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le meurtre d''O-tsuya'), 2); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le coup de pistolet'), 3); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Duo'), 3); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'Recherches philosophiques')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'De sang-froid')); -INSERT INTO collection_books(collection_id, book_id) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'The Life of Jesus')); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'Recherches philosophiques'), 1); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'De sang-froid'), 2); +INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'The Life of Jesus'), 3); diff --git a/front/src/BookListElement.vue b/front/src/BookListElement.vue index 729ea20..bf04ea9 100644 --- a/front/src/BookListElement.vue +++ b/front/src/BookListElement.vue @@ -164,6 +164,7 @@ async function importInventaireEdition(inventaireid) { + diff --git a/front/src/CollectionForm.vue b/front/src/CollectionForm.vue index 163fce9..1c25056 100644 --- a/front/src/CollectionForm.vue +++ b/front/src/CollectionForm.vue @@ -1,7 +1,7 @@ + + + diff --git a/internal/adapter/adapter.go b/internal/adapter/adapter.go index 4ff2ad6..bef0656 100644 --- a/internal/adapter/adapter.go +++ b/internal/adapter/adapter.go @@ -10,12 +10,37 @@ import ( "gorm.io/gorm" ) -func CollectionQueryToCollectionItemDto(collectionsQueryResult []query.CollectionsQueryResult) []dto.CollectionItemGet { - var collections []dto.CollectionItemGet +func CollectionItemsQueryToDto(itemsQueryResult []query.CollectionItemQueryResult) []dto.CollectionItemGet { + var dtoItems []dto.CollectionItemGet + for _, queryResult := range itemsQueryResult { + bookItem := dto.BookItemGet{ + ID: queryResult.ID, + Title: queryResult.Title, + Author: queryResult.Author, + Description: queryResult.Description, + InventaireID: queryResult.InventaireID, + IsInventaireEdition: queryResult.IsInventaireEdition, + Rating: queryResult.Rating, + Read: queryResult.Read, + StartReadDate: queryResult.StartReadDate, + WantRead: queryResult.WantRead, + CoverPath: queryResult.CoverPath, + } + dtoItems = append(dtoItems, + dto.CollectionItemGet{ + Position: queryResult.Position, + Book: bookItem, + }) + } + return dtoItems +} + +func CollectionQueryToCollectionItemDto(collectionsQueryResult []query.CollectionsQueryResult) []dto.CollectionListItemGet { + var collections []dto.CollectionListItemGet for _, collectionDb := range collectionsQueryResult { i := findIdInCollection(collections, collectionDb.ID) if i == -1 { - collections = append(collections, dto.CollectionItemGet{ + collections = append(collections, dto.CollectionListItemGet{ ID: collectionDb.ID, Name: collectionDb.Name, }) @@ -30,9 +55,9 @@ func CollectionQueryToCollectionItemDto(collectionsQueryResult []query.Collectio return collections } -func collectionDbToCollectionBookItem(collectionDb *query.CollectionsQueryResult) *dto.CollectionBookItemGet { +func collectionDbToCollectionBookItem(collectionDb *query.CollectionsQueryResult) *dto.CollectionListBookItemGet { if collectionDb.BookId > 0 { - bookItem := dto.CollectionBookItemGet{ + bookItem := dto.CollectionListBookItemGet{ ID: collectionDb.BookId, Title: collectionDb.BookTitle, CoverPath: collectionDb.CoverPath, @@ -44,7 +69,7 @@ func collectionDbToCollectionBookItem(collectionDb *query.CollectionsQueryResult } // returns the position in collections, -1 if not found -func findIdInCollection(collections []dto.CollectionItemGet, collectionId uint) int { +func findIdInCollection(collections []dto.CollectionListItemGet, collectionId uint) int { for i, collection := range collections { if collection.ID == collectionId { return i diff --git a/internal/apitest/get_collection_test.go b/internal/apitest/get_collection_test.go index c49faa5..c8a80b0 100644 --- a/internal/apitest/get_collection_test.go +++ b/internal/apitest/get_collection_test.go @@ -13,14 +13,14 @@ func TestGetCollection_Ok(t *testing.T) { status, collection := testGetCollection(t, "1", "10", "0") assert.Equal(t, http.StatusOK, status) assert.Equal(t, "Littérature française", collection.Name) - assert.Equal(t, 6, len(collection.Books)) + assert.Equal(t, 6, len(collection.Items)) } func TestGetCollection_Limit(t *testing.T) { status, collection := testGetCollection(t, "2", "3", "0") assert.Equal(t, http.StatusOK, status) assert.Equal(t, "Nouvelles", collection.Name) - assert.Equal(t, 3, len(collection.Books)) + assert.Equal(t, 3, len(collection.Items)) assert.Equal(t, int64(4), collection.Count) } @@ -33,10 +33,17 @@ func TestGetCollection_Empty(t *testing.T) { status, collection := testGetCollection(t, "4", "10", "0") assert.Equal(t, http.StatusOK, status) assert.Equal(t, "Empty", collection.Name) - assert.Equal(t, 0, len(collection.Books)) + assert.Equal(t, 0, len(collection.Items)) assert.Equal(t, int64(0), collection.Count) } +func TestGetCollection_Position(t *testing.T) { + status, collection := testGetCollection(t, "2", "3", "0") + assert.Equal(t, http.StatusOK, status) + assert.Equal(t, "Nouvelles", collection.Name) + assert.Equal(t, uint(3), collection.Items[2].Position) +} + func testGetCollection(t *testing.T, id string, limit string, offset string) (int, dto.CollectionGet) { return testutils.TestFetchModel[dto.CollectionGet](t, "/ws/collection/"+id, limit, offset) } diff --git a/internal/apitest/post_collection_addbook_test.go b/internal/apitest/post_collection_addbook_test.go index 533abab..a63b48d 100644 --- a/internal/apitest/post_collection_addbook_test.go +++ b/internal/apitest/post_collection_addbook_test.go @@ -11,24 +11,36 @@ import ( "github.com/stretchr/testify/assert" ) -func TestPostCollectionBookHandler_Ok(t *testing.T) { +func TestPostCollectionBookHandler_AddOk(t *testing.T) { payload := `{ "bookId": 9 }` collectionId := "1" - status := testPostCollectionBookHandler(t, collectionId, payload) + status := testPostCollectionBookHandler(collectionId, payload) assert.Equal(t, http.StatusOK, status) _, collection := testGetCollection(t, collectionId, "10", "0") assert.Equal(t, int64(7), collection.Count) } +func TestPostCollectionBookHandler_BookOK(t *testing.T) { + payload := + `{ + "bookId": 7 + }` + collectionId := "2" + status := testPostCollectionBookHandler(collectionId, payload) + assert.Equal(t, http.StatusOK, status) + _, collection := testGetCollection(t, collectionId, "10", "0") + assert.Equal(t, uint(7), collection.Items[0].Book.ID) +} + func TestPostCollectionBookHandler_CollectionNotFound(t *testing.T) { payload := `{ "bookId": 9 }` - status := testPostCollectionBookHandler(t, "12", payload) + status := testPostCollectionBookHandler("12", payload) assert.Equal(t, http.StatusNotFound, status) } @@ -37,7 +49,7 @@ func TestPostCollectionBookHandler_BookNotFound(t *testing.T) { `{ "bookId": 14654 }` - status := testPostCollectionBookHandler(t, "1", payload) + status := testPostCollectionBookHandler("1", payload) assert.Equal(t, http.StatusNotFound, status) } @@ -46,11 +58,11 @@ func TestPostCollectionBookHandler_Unauthorized(t *testing.T) { `{ "bookId": 9 }` - status := testPostCollectionBookHandler(t, "3", payload) + status := testPostCollectionBookHandler("3", payload) assert.Equal(t, http.StatusUnauthorized, status) } -func testPostCollectionBookHandler(t *testing.T, collectionId string, payload string) int { +func testPostCollectionBookHandler(collectionId string, payload string) int { router := testutils.TestSetup() w := httptest.NewRecorder() diff --git a/internal/db/init.go b/internal/db/init.go index 05ae685..24ec742 100644 --- a/internal/db/init.go +++ b/internal/db/init.go @@ -23,6 +23,7 @@ func Initdb(databasePath string, demoDataPath string) *gorm.DB { db.AutoMigrate(&model.UserBook{}) db.AutoMigrate(&model.StaticFile{}) db.AutoMigrate(&model.Collection{}) + db.AutoMigrate(&model.CollectionItem{}) var book model.Book queryResult := db.Limit(1).Find(&book) if queryResult.RowsAffected == 0 && demoDataPath != "" { diff --git a/internal/dto/out.go b/internal/dto/out.go index bb48407..42cc1fc 100644 --- a/internal/dto/out.go +++ b/internal/dto/out.go @@ -45,23 +45,28 @@ type BookItemGet struct { } type CollectionGet struct { - Name string `json:"name"` - Count int64 `json:"count"` - Books []BookItemGet `json:"books"` -} - -type CollectionItemsGet struct { - Count int64 `json:"count"` - Collections []CollectionItemGet `json:"collections"` + Name string `json:"name"` + Count int64 `json:"count"` + Items []CollectionItemGet `json:"items"` } type CollectionItemGet struct { - ID uint `json:"id"` - Name string `json:"name"` - Books []CollectionBookItemGet `json:"books"` + Position uint `json:"position"` + Book BookItemGet `json:"book"` } -type CollectionBookItemGet struct { +type CollectionItemsGet struct { + Count int64 `json:"count"` + Collections []CollectionListItemGet `json:"collections"` +} + +type CollectionListItemGet struct { + ID uint `json:"id"` + Name string `json:"name"` + Books []CollectionListBookItemGet `json:"books"` +} + +type CollectionListBookItemGet struct { ID uint `json:"id"` Title string `json:"title"` CoverPath string `json:"coverPath"` diff --git a/internal/model/collection.go b/internal/model/collection.go index 57f819d..741351a 100644 --- a/internal/model/collection.go +++ b/internal/model/collection.go @@ -7,5 +7,5 @@ type Collection struct { Name string User User UserID uint - Books []Book `gorm:"many2many:collection_books;"` + Items []CollectionItem } diff --git a/internal/model/collectionitem.go b/internal/model/collectionitem.go new file mode 100644 index 0000000..091760d --- /dev/null +++ b/internal/model/collectionitem.go @@ -0,0 +1,11 @@ +package model + +import "gorm.io/gorm" + +type CollectionItem struct { + gorm.Model + CollectionID uint + Position uint + Book Book + BookID uint +} diff --git a/internal/query/querycollections.go b/internal/query/querycollections.go index 1f6f83f..7d6a51d 100644 --- a/internal/query/querycollections.go +++ b/internal/query/querycollections.go @@ -1,7 +1,6 @@ package query import ( - "git.artlef.fr/bibliomane/internal/dto" "git.artlef.fr/bibliomane/internal/model" "gorm.io/gorm" ) @@ -64,8 +63,8 @@ func fetchCollectionItemBook(db *gorm.DB, collectionId uint, limit int, offset i func fetchCollectionItemBooksQuery(db *gorm.DB, collectionId uint) *gorm.DB { query := db.Model(&model.Collection{}) query = query.Select("collections.id, collections.user_id, collections.name, books.id as book_id, books.title as book_title, " + selectStaticFilesPath()) - query = query.Joins("left join collection_books on (collection_books.collection_id = collections.id)") - query = query.Joins("left join books on (books.id = collection_books.book_id)") + query = query.Joins("left join collection_items on (collection_items.collection_id = collections.id)") + query = query.Joins("left join books on (books.id = collection_items.book_id)") query = joinStaticFiles(query) query = query.Where("collections.id = ?", collectionId) return query @@ -81,14 +80,29 @@ func fetchCollections(db *gorm.DB, userId uint) *gorm.DB { return db.Model(&model.Collection{}).Where("collections.user_id = ?", userId) } -func FetchCollectionBooks(db *gorm.DB, userId uint, collectionId uint, limit int, offset int) ([]dto.BookItemGet, error) { - var books []dto.BookItemGet +type CollectionItemQueryResult struct { + Position uint + ID uint + Title string + Author string + Description string + InventaireID string + IsInventaireEdition bool + Rating int + Read bool + StartReadDate string + WantRead bool + CoverPath string +} + +func FetchCollectionItems(db *gorm.DB, userId uint, collectionId uint, limit int, offset int) ([]CollectionItemQueryResult, error) { + var collectionitems []CollectionItemQueryResult query := fetchCollectionBooksQuery(db, userId, collectionId) query = query.Limit(limit) query = query.Offset(offset) - query = query.Order("books.id DESC") - res := query.Find(&books) - return books, res.Error + query = query.Order("collection_items.position") + res := query.Find(&collectionitems) + return collectionitems, res.Error } func FetchCollectionBooksCount(db *gorm.DB, userId uint, collectionId uint) (int64, error) { @@ -98,8 +112,14 @@ func FetchCollectionBooksCount(db *gorm.DB, userId uint, collectionId uint) (int } func fetchCollectionBooksQuery(db *gorm.DB, userId uint, collectionId uint) *gorm.DB { - query := fetchBookQueryBuilder(db, userId) - query = query.Joins("left join collection_books on (collection_books.book_id = books.id)") - query = query.Where("collection_books.collection_id = ?", collectionId) + query := db.Model(&model.CollectionItem{}) + query = query.Select("collection_items.position, " + selectBookItem()) + query = query.Joins("left join collections on (collection_items.collection_id = collections.id)") + query = query.Joins("left join books on (books.id = collection_items.book_id)") + query = joinAuthors(query) + query = query.Joins("left join user_books on (user_books.book_id = books.id and user_books.user_id = ?)", userId) + query = joinStaticFiles(query) + query = query.Order("collection_items.position") + query = query.Where("collections.id = ?", collectionId) return query } diff --git a/internal/routes/collectionaddbookpost.go b/internal/routes/collectionaddbookpost.go index f93d66d..a656b11 100644 --- a/internal/routes/collectionaddbookpost.go +++ b/internal/routes/collectionaddbookpost.go @@ -56,7 +56,7 @@ func PostCollectionBookHandler(ac appcontext.AppContext) { return } - collection.Books = append(collection.Books, book) - ac.Db.Save(&collection) + item := model.CollectionItem{Position: 0, BookID: book.ID, CollectionID: collection.ID} + ac.Db.Save(&item) ac.C.String(http.StatusOK, "Success") } diff --git a/internal/routes/collectionget.go b/internal/routes/collectionget.go index 2084681..ce86dc6 100644 --- a/internal/routes/collectionget.go +++ b/internal/routes/collectionget.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" + "git.artlef.fr/bibliomane/internal/adapter" "git.artlef.fr/bibliomane/internal/appcontext" "git.artlef.fr/bibliomane/internal/dto" "git.artlef.fr/bibliomane/internal/i18nresource" @@ -50,12 +51,13 @@ func GetCollectionHandler(ac appcontext.AppContext) { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return } - books, err := query.FetchCollectionBooks(ac.Db, user.ID, uint(collectionId), limit, offset) + itemsQueryResult, err := query.FetchCollectionItems(ac.Db, user.ID, uint(collectionId), limit, offset) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err) return } - collection.Books = books + items := adapter.CollectionItemsQueryToDto(itemsQueryResult) + collection.Items = items count, err := query.FetchCollectionBooksCount(ac.Db, user.ID, uint(collectionId)) if err != nil { myvalidator.ReturnErrorsAsJsonResponse(&ac, err)