feat(api): add read/unread books count in SeriesDto

related to #25
This commit is contained in:
Gauthier Roebroeck 2020-06-03 17:38:12 +08:00
parent 2f7d2a447f
commit 3ca50d7b34
5 changed files with 49 additions and 19 deletions

View file

@ -28,6 +28,7 @@ class SeriesDtoDao(
private val s = Tables.SERIES
private val b = Tables.BOOK
private val d = Tables.SERIES_METADATA
private val r = Tables.READ_PROGRESS
private val groupFields = arrayOf(
*s.fields(),
@ -40,28 +41,29 @@ class SeriesDtoDao(
"lastModifiedDate" to s.LAST_MODIFIED_DATE
)
override fun findAll(search: SeriesSearch, pageable: Pageable): Page<SeriesDto> {
override fun findAll(search: SeriesSearch, userId: Long, pageable: Pageable): Page<SeriesDto> {
val conditions = search.toCondition()
return findAll(conditions, pageable)
return findAll(conditions, userId, pageable)
}
override fun findRecentlyUpdated(search: SeriesSearch, pageable: Pageable): Page<SeriesDto> {
override fun findRecentlyUpdated(search: SeriesSearch, userId: Long, pageable: Pageable): Page<SeriesDto> {
val conditions = search.toCondition()
.and(s.CREATED_DATE.ne(s.LAST_MODIFIED_DATE))
return findAll(conditions, pageable)
return findAll(conditions, userId, pageable)
}
override fun findByIdOrNull(seriesId: Long): SeriesDto? =
override fun findByIdOrNull(seriesId: Long, userId: Long): SeriesDto? =
selectBase()
.where(s.ID.eq(seriesId))
.and(readProgressCondition(userId))
.groupBy(*groupFields)
.fetchAndMap()
.firstOrNull()
private fun findAll(conditions: Condition, pageable: Pageable): Page<SeriesDto> {
private fun findAll(conditions: Condition, userId: Long, pageable: Pageable): Page<SeriesDto> {
val count = dsl.selectCount()
.from(s)
.leftJoin(d).on(s.ID.eq(d.SERIES_ID))
@ -72,6 +74,7 @@ class SeriesDtoDao(
val dtos = selectBase()
.where(conditions)
.and(readProgressCondition(userId))
.groupBy(*groupFields)
.orderBy(orderBy)
.limit(pageable.pageSize)
@ -87,18 +90,23 @@ class SeriesDtoDao(
private fun selectBase() =
dsl.select(*groupFields)
.select(DSL.count(b.ID).`as`("bookCount"))
.select(DSL.count(b.ID).`as`("booksCount"))
.select(DSL.count(r.COMPLETED).`as`("booksReadCount"))
.from(s)
.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))
private fun readProgressCondition(userId: Long): Condition = r.USER_ID.eq(userId).or(r.USER_ID.isNull)
private fun ResultQuery<Record>.fetchAndMap() =
fetch()
.map { r ->
val sr = r.into(s)
val dr = r.into(d)
val bookCount = r["bookCount"] as Int
sr.toDto(bookCount, dr.toDto())
val booksCount = r["booksCount"] as Int
val booksReadCount = r["booksReadCount"] as Int
sr.toDto(booksCount, booksReadCount, dr.toDto())
}
private fun SeriesSearch.toCondition(): Condition {
@ -111,7 +119,7 @@ class SeriesDtoDao(
return c
}
private fun SeriesRecord.toDto(bookCount: Int, metadata: SeriesMetadataDto) =
private fun SeriesRecord.toDto(booksCount: Int, booksReadCount: Int, metadata: SeriesMetadataDto) =
SeriesDto(
id = id,
libraryId = libraryId,
@ -120,7 +128,8 @@ class SeriesDtoDao(
created = createdDate.toUTC(),
lastModified = lastModifiedDate.toUTC(),
fileLastModified = fileLastModified.toUTC(),
booksCount = bookCount,
booksCount = booksCount,
booksReadCount = booksReadCount,
metadata = metadata
)

View file

@ -83,7 +83,7 @@ class SeriesController(
metadataStatus = metadataStatus ?: emptyList()
)
return seriesDtoRepository.findAll(seriesSearch, pageRequest)
return seriesDtoRepository.findAll(seriesSearch, principal.user.id, pageRequest)
.map { it.restrictUrl(!principal.user.roleAdmin) }
}
@ -104,6 +104,7 @@ class SeriesController(
return seriesDtoRepository.findAll(
SeriesSearch(libraryIds = libraryIds),
principal.user.id,
pageRequest
).map { it.restrictUrl(!principal.user.roleAdmin) }
}
@ -125,6 +126,7 @@ class SeriesController(
return seriesDtoRepository.findAll(
SeriesSearch(libraryIds = libraryIds),
principal.user.id,
pageRequest
).map { it.restrictUrl(!principal.user.roleAdmin) }
}
@ -146,6 +148,7 @@ class SeriesController(
return seriesDtoRepository.findRecentlyUpdated(
SeriesSearch(libraryIds = libraryIds),
principal.user.id,
pageRequest
).map { it.restrictUrl(!principal.user.roleAdmin) }
}
@ -155,7 +158,7 @@ class SeriesController(
@AuthenticationPrincipal principal: KomgaPrincipal,
@PathVariable(name = "seriesId") id: Long
): SeriesDto =
seriesDtoRepository.findByIdOrNull(id)?.let {
seriesDtoRepository.findByIdOrNull(id, principal.user.id)?.let {
if (!principal.user.canAccessLibrary(it.libraryId)) throw ResponseStatusException(HttpStatus.UNAUTHORIZED)
it.restrictUrl(!principal.user.roleAdmin)
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
@ -227,7 +230,8 @@ class SeriesController(
fun updateMetadata(
@PathVariable seriesId: Long,
@Parameter(description = "Metadata fields to update. Set a field to null to unset the metadata. You can omit fields you don't want to update.")
@Valid @RequestBody newMetadata: SeriesMetadataUpdateDto
@Valid @RequestBody newMetadata: SeriesMetadataUpdateDto,
@AuthenticationPrincipal principal: KomgaPrincipal
): SeriesDto =
seriesMetadataRepository.findByIdOrNull(seriesId)?.let { existing ->
val updated = with(newMetadata) {
@ -241,7 +245,7 @@ class SeriesController(
)
}
seriesMetadataRepository.update(updated)
seriesDtoRepository.findByIdOrNull(seriesId)!!
seriesDtoRepository.findByIdOrNull(seriesId, principal.user.id)!!
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
@PostMapping("{seriesId}/read-progress")

View file

@ -15,8 +15,11 @@ data class SeriesDto(
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
val fileLastModified: LocalDateTime,
val booksCount: Int,
val booksReadCount: Int,
val metadata: SeriesMetadataDto
)
) {
val booksUnreadCount: Int = booksCount - booksReadCount
}
fun SeriesDto.restrictUrl(restrict: Boolean) =
if (restrict) copy(url = "") else this

View file

@ -6,7 +6,7 @@ import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
interface SeriesDtoRepository {
fun findAll(search: SeriesSearch, pageable: Pageable): Page<SeriesDto>
fun findRecentlyUpdated(search: SeriesSearch, pageable: Pageable): Page<SeriesDto>
fun findByIdOrNull(seriesId: Long): SeriesDto?
fun findAll(search: SeriesSearch, userId: Long, pageable: Pageable): Page<SeriesDto>
fun findRecentlyUpdated(search: SeriesSearch, userId: Long, pageable: Pageable): Page<SeriesDto>
fun findByIdOrNull(seriesId: Long, userId: Long): SeriesDto?
}

View file

@ -510,6 +510,13 @@ class SeriesControllerTest(
status { isNoContent }
}
mockMvc.get("/api/v1/series/${series.id}")
.andExpect {
status { isOk }
jsonPath("$.booksUnreadCount") { value(0) }
jsonPath("$.booksReadCount") { value(2) }
}
mockMvc.get("/api/v1/series/${series.id}/books")
.andExpect {
status { isOk }
@ -548,6 +555,13 @@ class SeriesControllerTest(
status { isNoContent }
}
mockMvc.get("/api/v1/series/${series.id}")
.andExpect {
status { isOk }
jsonPath("$.booksUnreadCount") { value(2) }
jsonPath("$.booksReadCount") { value(0) }
}
mockMvc.get("/api/v1/series/${series.id}/books")
.andExpect {
status { isOk }