diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDtoDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDtoDao.kt index 5301aac2d..25b15cbd4 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDtoDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDtoDao.kt @@ -56,15 +56,14 @@ class BookDtoDao( .leftJoin(m).on(b.ID.eq(m.BOOK_ID)) .leftJoin(d).on(b.ID.eq(d.BOOK_ID)) .leftJoin(r).on(b.ID.eq(r.BOOK_ID)) - .where(conditions) .and(readProgressCondition(userId)) + .where(conditions) .fetchOne(0, Long::class.java) val orderBy = pageable.sort.toOrderBy(sorts) - val dtos = selectBase() + val dtos = selectBase(userId) .where(conditions) - .and(readProgressCondition(userId)) .orderBy(orderBy) .limit(pageable.pageSize) .offset(pageable.offset) @@ -78,9 +77,8 @@ class BookDtoDao( } override fun findByIdOrNull(bookId: Long, userId: Long): BookDto? = - selectBase() + selectBase(userId) .where(b.ID.eq(bookId)) - .and(readProgressCondition(userId)) .fetchAndMap() .firstOrNull() @@ -99,9 +97,8 @@ class BookDtoDao( val seriesId = record.get(0, Long::class.java) val numberSort = record.get(1, Float::class.java) - return selectBase() + return selectBase(userId) .where(b.SERIES_ID.eq(seriesId)) - .and(readProgressCondition(userId)) .orderBy(d.NUMBER_SORT.let { if (next) it.asc() else it.desc() }) .seek(numberSort) .limit(1) @@ -109,7 +106,7 @@ class BookDtoDao( .firstOrNull() } - private fun selectBase() = + private fun selectBase(userId: Long) = dsl.select( *b.fields(), *mediaFields, @@ -119,6 +116,7 @@ class BookDtoDao( .leftJoin(m).on(b.ID.eq(m.BOOK_ID)) .leftJoin(d).on(b.ID.eq(d.BOOK_ID)) .leftJoin(r).on(b.ID.eq(r.BOOK_ID)) + .and(readProgressCondition(userId)) private fun ResultQuery.fetchAndMap() = fetch() diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt index 9933902ab..ae4c3b63d 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt @@ -74,9 +74,8 @@ class SeriesDtoDao( } override fun findByIdOrNull(seriesId: Long, userId: Long): SeriesDto? = - selectBase() + selectBase(userId) .where(s.ID.eq(seriesId)) - .and(readProgressCondition(userId)) .groupBy(*groupFields) .fetchAndMap() .firstOrNull() @@ -89,7 +88,6 @@ class SeriesDtoDao( .leftJoin(d).on(s.ID.eq(d.SERIES_ID)) .leftJoin(r).on(b.ID.eq(r.BOOK_ID)) .where(conditions) - .and(readProgressCondition(userId)) .groupBy(s.ID) .having(having) .fetch() @@ -97,9 +95,8 @@ class SeriesDtoDao( val orderBy = pageable.sort.toOrderBy(sorts) - val dtos = selectBase() + val dtos = selectBase(userId) .where(conditions) - .and(readProgressCondition(userId)) .groupBy(*groupFields) .having(having) .orderBy(orderBy) @@ -114,7 +111,7 @@ class SeriesDtoDao( ) } - private fun selectBase(): SelectOnConditionStep = + private fun selectBase(userId: Long): SelectOnConditionStep = dsl.select(*groupFields) .select(DSL.count(b.ID).`as`(BOOKS_COUNT)) .select(countUnread.`as`(BOOKS_UNREAD_COUNT)) @@ -124,6 +121,7 @@ class SeriesDtoDao( .leftJoin(b).on(s.ID.eq(b.SERIES_ID)) .leftJoin(d).on(s.ID.eq(d.SERIES_ID)) .leftJoin(r).on(b.ID.eq(r.BOOK_ID)) + .and(readProgressCondition(userId)) private fun readProgressCondition(userId: Long): Condition = r.USER_ID.eq(userId).or(r.USER_ID.isNull) diff --git a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/BookControllerTest.kt b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/BookControllerTest.kt index e84f3a78f..053d69007 100644 --- a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/BookControllerTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/BookControllerTest.kt @@ -20,6 +20,7 @@ import org.gotson.komga.domain.persistence.SeriesRepository import org.gotson.komga.domain.service.KomgaUserLifecycle import org.gotson.komga.domain.service.LibraryLifecycle import org.gotson.komga.domain.service.SeriesLifecycle +import org.gotson.komga.infrastructure.security.KomgaPrincipal import org.hamcrest.core.IsNull import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterEach @@ -37,12 +38,15 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.HttpHeaders import org.springframework.http.MediaType import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user import org.springframework.test.context.junit.jupiter.SpringExtension import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.MockMvcResultMatchersDsl import org.springframework.test.web.servlet.delete import org.springframework.test.web.servlet.get import org.springframework.test.web.servlet.patch +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import java.time.LocalDate import javax.sql.DataSource import kotlin.random.Random @@ -72,13 +76,16 @@ class BookControllerTest( } private var library = makeLibrary() + private lateinit var user2: KomgaUser + private lateinit var user3: KomgaUser @BeforeAll fun `setup library`() { jdbcTemplate.execute("ALTER SEQUENCE hibernate_sequence RESTART WITH 1") library = libraryRepository.insert(library) // id = 1 - userRepository.save(KomgaUser("user@example.org", "", false)) // id = 2 + user2 = userRepository.save(KomgaUser("user@example.org", "", false)) // id = 2 + user3 = userRepository.save(KomgaUser("user2@example.org", "", false)) // id = 3 } @AfterAll @@ -892,4 +899,51 @@ class BookControllerTest( } } } + + @Test + fun `given a user with read progress when getting books for the other user then books are returned correctly`() { + makeSeries(name = "series", libraryId = library.id).let { series -> + seriesLifecycle.createSeries(series).also { created -> + val books = listOf(makeBook("1.cbr", libraryId = library.id), makeBook("2.cbr", libraryId = library.id)) + seriesLifecycle.addBooks(created, books) + } + } + + val book = bookRepository.findAll().first() + mediaRepository.findById(book.id).let { + mediaRepository.update(it.copy( + status = Media.Status.READY, + pages = (1..10).map { BookPage("$it", "image/jpeg") } + )) + } + + val jsonString = """ + { + "completed": true + } + """.trimIndent() + + mockMvc.perform(MockMvcRequestBuilders + .patch("/api/v1/books/${book.id}/read-progress") + .with(user(KomgaPrincipal(user2))) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonString) + ) + + mockMvc.perform(MockMvcRequestBuilders + .get("/api/v1/books") + .with(user(KomgaPrincipal(user2))) + .contentType(MediaType.APPLICATION_JSON) + ).andExpect( + jsonPath("$.totalElements").value(2) + ) + + mockMvc.perform(MockMvcRequestBuilders + .get("/api/v1/books") + .with(user(KomgaPrincipal(user3))) + .contentType(MediaType.APPLICATION_JSON) + ).andExpect( + jsonPath("$.totalElements").value(2) + ) + } }