diff --git a/model/album.go b/model/album.go index 6f53e4c66..ef48ac7be 100644 --- a/model/album.go +++ b/model/album.go @@ -145,6 +145,7 @@ type AlbumRepository interface { Exists(id string) (bool, error) Put(*Album) error UpdateExternalInfo(*Album) error + UpdateImage(id, filename string) error Get(id string) (*Album, error) GetAll(...QueryOptions) (Albums, error) GetCursor(...QueryOptions) (AlbumCursor, error) diff --git a/persistence/album_repository.go b/persistence/album_repository.go index 6ebbd9202..e8167cde4 100644 --- a/persistence/album_repository.go +++ b/persistence/album_repository.go @@ -219,6 +219,22 @@ func (r *albumRepository) UpdateExternalInfo(al *model.Album) error { return err } +// UpdateImage is the sole writer of uploaded_image: it uses raw SQL because Put's +// structs.Map marshaling drops the structs:"-" tagged UploadedImage field. +func (r *albumRepository) UpdateImage(id, filename string) error { + c, err := r.executeSQL(Update(r.tableName). + Set("uploaded_image", filename). + Set("updated_at", time.Now()). + Where(Eq{"id": id})) + if err != nil { + return err + } + if c == 0 { + return model.ErrNotFound + } + return nil +} + func (r *albumRepository) selectAlbum(options ...model.QueryOptions) SelectBuilder { sql := r.newSelect(options...).Columns("album.*", "library.path as library_path", "library.name as library_name"). LeftJoin("library on album.library_id = library.id") diff --git a/persistence/album_repository_test.go b/persistence/album_repository_test.go index 64ff0095e..3b2028a7e 100644 --- a/persistence/album_repository_test.go +++ b/persistence/album_repository_test.go @@ -41,6 +41,37 @@ var _ = Describe("AlbumRepository", func() { }) }) + Describe("UpdateImage", func() { + BeforeEach(func() { + Expect(albumRepo.Put(&model.Album{ID: "img-1", Name: "img", LibraryID: 1})).To(Succeed()) + DeferCleanup(func() { + _, _ = albumRepo.executeSQL(squirrel.Delete("album").Where(squirrel.Eq{"id": "img-1"})) + }) + }) + It("sets and clears the uploaded image filename", func() { + Expect(albumRepo.UpdateImage("img-1", "img-1_cover.jpg")).To(Succeed()) + got, err := albumRepo.Get("img-1") + Expect(err).ToNot(HaveOccurred()) + Expect(got.UploadedImage).To(Equal("img-1_cover.jpg")) + + Expect(albumRepo.UpdateImage("img-1", "")).To(Succeed()) + got, err = albumRepo.Get("img-1") + Expect(err).ToNot(HaveOccurred()) + Expect(got.UploadedImage).To(BeEmpty()) + }) + It("is preserved across a full-row Put (structs:\"-\" contract)", func() { + Expect(albumRepo.UpdateImage("img-1", "img-1_cover.jpg")).To(Succeed()) + // A scan-style refresh re-Puts the album with a zero-valued UploadedImage. + Expect(albumRepo.Put(&model.Album{ID: "img-1", Name: "img changed", LibraryID: 1})).To(Succeed()) + got, err := albumRepo.Get("img-1") + Expect(err).ToNot(HaveOccurred()) + Expect(got.UploadedImage).To(Equal("img-1_cover.jpg")) + }) + It("returns ErrNotFound for a missing album", func() { + Expect(albumRepo.UpdateImage("does-not-exist", "x.jpg")).To(MatchError(model.ErrNotFound)) + }) + }) + Describe("CopyAttributes", func() { var srcTime, dstTime time.Time BeforeEach(func() { diff --git a/tests/mock_album_repo.go b/tests/mock_album_repo.go index 03dfed879..ccd1eba15 100644 --- a/tests/mock_album_repo.go +++ b/tests/mock_album_repo.go @@ -134,6 +134,17 @@ func (m *MockAlbumRepo) UpdateExternalInfo(album *model.Album) error { return nil } +func (m *MockAlbumRepo) UpdateImage(id, filename string) error { + if m.Err { + return errors.New("unexpected error") + } + if al, ok := m.Data[id]; ok { + al.UploadedImage = filename + return nil + } + return model.ErrNotFound +} + func (m *MockAlbumRepo) Search(q string, options ...model.QueryOptions) (model.Albums, error) { m.SearchQuery = q if len(options) > 0 {