fix(api): books could disappear for users if read by others

This commit is contained in:
Gauthier Roebroeck 2020-06-05 20:41:00 +08:00
parent 8517613915
commit 3d1f0e0d05
3 changed files with 65 additions and 15 deletions

View file

@ -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<Record>.fetchAndMap() =
fetch()

View file

@ -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<Record> =
private fun selectBase(userId: Long): SelectOnConditionStep<Record> =
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)

View file

@ -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)
)
}
}