mirror of
https://github.com/gotson/komga.git
synced 2025-12-17 05:57:56 +01:00
feat: restrict content by labels
This commit is contained in:
parent
496ebb0aac
commit
8d4eb68f7d
21 changed files with 1570 additions and 141 deletions
|
|
@ -0,0 +1,22 @@
|
|||
package org.gotson.komga.domain.model
|
||||
|
||||
import org.gotson.komga.language.lowerNotBlank
|
||||
import org.gotson.komga.language.toSetOrNull
|
||||
|
||||
class ContentRestrictions(
|
||||
val ageRestriction: ContentRestriction.AgeRestriction? = null,
|
||||
labelsAllow: Set<String> = emptySet(),
|
||||
labelsExclude: Set<String> = emptySet(),
|
||||
) {
|
||||
val labelsAllowRestriction =
|
||||
labelsAllow.lowerNotBlank().toSet()
|
||||
.minus(labelsExclude.lowerNotBlank().toSet())
|
||||
.toSetOrNull()?.let { ContentRestriction.LabelsRestriction.AllowOnly(it) }
|
||||
|
||||
val labelsExcludeRestriction =
|
||||
labelsExclude.lowerNotBlank().toSetOrNull()?.let { ContentRestriction.LabelsRestriction.Exclude(it) }
|
||||
|
||||
fun isRestricted() = ageRestriction != null || labelsAllowRestriction != null || labelsExcludeRestriction != null
|
||||
|
||||
override fun toString(): String = "ContentRestriction(ageRestriction=$ageRestriction, labelsAllowRestriction=$labelsAllowRestriction, labelsExcludeRestriction=$labelsExcludeRestriction)"
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package org.gotson.komga.domain.model
|
||||
|
||||
import com.github.f4b6a3.tsid.TsidCreator
|
||||
import org.gotson.komga.language.lowerNotBlank
|
||||
import java.io.Serializable
|
||||
import java.time.LocalDateTime
|
||||
import javax.validation.constraints.Email
|
||||
|
|
@ -22,7 +23,7 @@ data class KomgaUser(
|
|||
val rolePageStreaming: Boolean = true,
|
||||
val sharedLibrariesIds: Set<String> = emptySet(),
|
||||
val sharedAllLibraries: Boolean = true,
|
||||
val restrictions: Set<ContentRestriction> = emptySet(),
|
||||
val restrictions: ContentRestrictions = ContentRestrictions(),
|
||||
val id: String = TsidCreator.getTsid256().toString(),
|
||||
override val createdDate: LocalDateTime = LocalDateTime.now(),
|
||||
override val lastModifiedDate: LocalDateTime = createdDate,
|
||||
|
|
@ -67,16 +68,37 @@ data class KomgaUser(
|
|||
return sharedAllLibraries || sharedLibrariesIds.any { it == library.id }
|
||||
}
|
||||
|
||||
fun isContentRestricted(ageRating: Int? = null, sharingLabels: Set<String> = emptySet()): Boolean {
|
||||
restrictions.forEach { restriction ->
|
||||
when (restriction) {
|
||||
is ContentRestriction.AgeRestriction.AllowOnlyUnder -> if (ageRating == null || ageRating > restriction.age) return true
|
||||
is ContentRestriction.AgeRestriction.ExcludeOver -> ageRating?.let { if (it >= restriction.age) return true }
|
||||
is ContentRestriction.LabelsRestriction.AllowOnly -> if (restriction.labels.intersect(sharingLabels).isEmpty()) return true
|
||||
is ContentRestriction.LabelsRestriction.Exclude -> if (restriction.labels.intersect(sharingLabels).isNotEmpty()) return true
|
||||
}
|
||||
fun isContentAllowed(ageRating: Int? = null, sharingLabels: Set<String> = emptySet()): Boolean {
|
||||
val labels = sharingLabels.lowerNotBlank().toSet()
|
||||
|
||||
val ageAllowed =
|
||||
if (restrictions.ageRestriction is ContentRestriction.AgeRestriction.AllowOnlyUnder)
|
||||
ageRating != null && ageRating <= restrictions.ageRestriction.age
|
||||
else null
|
||||
|
||||
val labelAllowed =
|
||||
if (restrictions.labelsAllowRestriction != null)
|
||||
restrictions.labelsAllowRestriction.labels.intersect(labels).isNotEmpty()
|
||||
else null
|
||||
|
||||
val allowed = when {
|
||||
ageAllowed == null -> labelAllowed != false
|
||||
labelAllowed == null -> ageAllowed != false
|
||||
else -> ageAllowed != false || labelAllowed != false
|
||||
}
|
||||
return false
|
||||
if (!allowed) return false
|
||||
|
||||
val ageDenied =
|
||||
if (restrictions.ageRestriction is ContentRestriction.AgeRestriction.ExcludeOver)
|
||||
ageRating != null && ageRating >= restrictions.ageRestriction.age
|
||||
else false
|
||||
|
||||
val labelDenied =
|
||||
if (restrictions.labelsExcludeRestriction != null)
|
||||
restrictions.labelsExcludeRestriction.labels.intersect(labels).isNotEmpty()
|
||||
else false
|
||||
|
||||
return !ageDenied && !labelDenied
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package org.gotson.komga.domain.persistence
|
||||
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.domain.model.ReadList
|
||||
import org.springframework.data.domain.Page
|
||||
import org.springframework.data.domain.Pageable
|
||||
|
|
@ -10,20 +10,20 @@ interface ReadListRepository {
|
|||
* Find one ReadList by [readListId],
|
||||
* optionally with only bookIds filtered by the provided [filterOnLibraryIds] it not null.
|
||||
*/
|
||||
fun findByIdOrNull(readListId: String, filterOnLibraryIds: Collection<String>? = null, restrictions: Set<ContentRestriction> = emptySet()): ReadList?
|
||||
fun findByIdOrNull(readListId: String, filterOnLibraryIds: Collection<String>? = null, restrictions: ContentRestrictions = ContentRestrictions()): ReadList?
|
||||
|
||||
/**
|
||||
* Find all ReadList
|
||||
* optionally with at least one Book belonging to the provided [belongsToLibraryIds] if not null,
|
||||
* optionally with only bookIds filtered by the provided [filterOnLibraryIds] if not null.
|
||||
*/
|
||||
fun findAll(belongsToLibraryIds: Collection<String>? = null, filterOnLibraryIds: Collection<String>? = null, search: String? = null, pageable: Pageable, restrictions: Set<ContentRestriction> = emptySet()): Page<ReadList>
|
||||
fun findAll(belongsToLibraryIds: Collection<String>? = null, filterOnLibraryIds: Collection<String>? = null, search: String? = null, pageable: Pageable, restrictions: ContentRestrictions = ContentRestrictions()): Page<ReadList>
|
||||
|
||||
/**
|
||||
* Find all ReadList that contains the provided [containsBookId],
|
||||
* optionally with only bookIds filtered by the provided [filterOnLibraryIds] if not null.
|
||||
*/
|
||||
fun findAllContainingBookId(containsBookId: String, filterOnLibraryIds: Collection<String>?, restrictions: Set<ContentRestriction> = emptySet()): Collection<ReadList>
|
||||
fun findAllContainingBookId(containsBookId: String, filterOnLibraryIds: Collection<String>?, restrictions: ContentRestrictions = ContentRestrictions()): Collection<ReadList>
|
||||
|
||||
fun findAllEmpty(): Collection<ReadList>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package org.gotson.komga.domain.persistence
|
||||
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.domain.model.SeriesCollection
|
||||
import org.springframework.data.domain.Page
|
||||
import org.springframework.data.domain.Pageable
|
||||
|
|
@ -10,20 +10,20 @@ interface SeriesCollectionRepository {
|
|||
* Find one SeriesCollection by [collectionId],
|
||||
* optionally with only seriesId filtered by the provided [filterOnLibraryIds] if not null.
|
||||
*/
|
||||
fun findByIdOrNull(collectionId: String, filterOnLibraryIds: Collection<String>? = null, restrictions: Set<ContentRestriction> = emptySet()): SeriesCollection?
|
||||
fun findByIdOrNull(collectionId: String, filterOnLibraryIds: Collection<String>? = null, restrictions: ContentRestrictions = ContentRestrictions()): SeriesCollection?
|
||||
|
||||
/**
|
||||
* Find all SeriesCollection
|
||||
* optionally with at least one Series belonging to the provided [belongsToLibraryIds] if not null,
|
||||
* optionally with only seriesId filtered by the provided [filterOnLibraryIds] if not null.
|
||||
*/
|
||||
fun findAll(belongsToLibraryIds: Collection<String>? = null, filterOnLibraryIds: Collection<String>? = null, search: String? = null, pageable: Pageable, restrictions: Set<ContentRestriction> = emptySet()): Page<SeriesCollection>
|
||||
fun findAll(belongsToLibraryIds: Collection<String>? = null, filterOnLibraryIds: Collection<String>? = null, search: String? = null, pageable: Pageable, restrictions: ContentRestrictions = ContentRestrictions()): Page<SeriesCollection>
|
||||
|
||||
/**
|
||||
* Find all SeriesCollection that contains the provided [containsSeriesId],
|
||||
* optionally with only seriesId filtered by the provided [filterOnLibraryIds] if not null.
|
||||
*/
|
||||
fun findAllContainingSeriesId(containsSeriesId: String, filterOnLibraryIds: Collection<String>?, restrictions: Set<ContentRestriction> = emptySet()): Collection<SeriesCollection>
|
||||
fun findAllContainingSeriesId(containsSeriesId: String, filterOnLibraryIds: Collection<String>?, restrictions: ContentRestrictions = ContentRestrictions()): Collection<SeriesCollection>
|
||||
|
||||
fun findAllEmpty(): Collection<SeriesCollection>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package org.gotson.komga.infrastructure.jooq
|
||||
|
||||
import org.gotson.komga.domain.model.BookSearchWithReadProgress
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.domain.model.ReadStatus
|
||||
import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource
|
||||
import org.gotson.komga.infrastructure.search.LuceneEntity
|
||||
|
|
@ -78,8 +78,8 @@ class BookDtoDao(
|
|||
"readList.number" to rlb.NUMBER,
|
||||
)
|
||||
|
||||
override fun findAll(search: BookSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: Set<ContentRestriction>): Page<BookDto> {
|
||||
val conditions = search.toCondition().and(restrictions.toCondition())
|
||||
override fun findAll(search: BookSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: ContentRestrictions): Page<BookDto> {
|
||||
val conditions = search.toCondition().and(restrictions.toCondition(dsl))
|
||||
|
||||
return findAll(conditions, userId, pageable, search.toJoinConditions(), null, search.searchTerm)
|
||||
}
|
||||
|
|
@ -174,13 +174,13 @@ class BookDtoDao(
|
|||
): BookDto? =
|
||||
findSiblingReadList(readListId, bookId, userId, filterOnLibraryIds, next = true)
|
||||
|
||||
override fun findAllOnDeck(userId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable, restrictions: Set<ContentRestriction>): Page<BookDto> {
|
||||
override fun findAllOnDeck(userId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable, restrictions: ContentRestrictions): Page<BookDto> {
|
||||
val seriesIds = dsl.select(s.ID)
|
||||
.from(s)
|
||||
.leftJoin(b).on(s.ID.eq(b.SERIES_ID))
|
||||
.leftJoin(r).on(b.ID.eq(r.BOOK_ID)).and(readProgressCondition(userId))
|
||||
.leftJoin(sd).on(b.SERIES_ID.eq(sd.SERIES_ID))
|
||||
.where(restrictions.toCondition())
|
||||
.where(restrictions.toCondition(dsl))
|
||||
.apply { filterOnLibraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } }
|
||||
.groupBy(s.ID)
|
||||
.having(countUnread.ge(inline(1.toBigDecimal())))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package org.gotson.komga.infrastructure.jooq
|
||||
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.domain.model.ReadList
|
||||
import org.gotson.komga.domain.persistence.ReadListRepository
|
||||
import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource
|
||||
|
|
@ -39,31 +39,31 @@ class ReadListDao(
|
|||
"name" to rl.NAME.collate(SqliteUdfDataSource.collationUnicode3),
|
||||
)
|
||||
|
||||
override fun findByIdOrNull(readListId: String, filterOnLibraryIds: Collection<String>?, restrictions: Set<ContentRestriction>): ReadList? =
|
||||
selectBase(restrictions.isNotEmpty())
|
||||
override fun findByIdOrNull(readListId: String, filterOnLibraryIds: Collection<String>?, restrictions: ContentRestrictions): ReadList? =
|
||||
selectBase(restrictions.isRestricted())
|
||||
.where(rl.ID.eq(readListId))
|
||||
.apply { filterOnLibraryIds?.let { and(b.LIBRARY_ID.`in`(it)) } }
|
||||
.apply { if (restrictions.isNotEmpty()) and(restrictions.toCondition()) }
|
||||
.apply { if (restrictions.isRestricted()) and(restrictions.toCondition(dsl)) }
|
||||
.fetchAndMap(filterOnLibraryIds, restrictions)
|
||||
.firstOrNull()
|
||||
|
||||
override fun findAll(belongsToLibraryIds: Collection<String>?, filterOnLibraryIds: Collection<String>?, search: String?, pageable: Pageable, restrictions: Set<ContentRestriction>): Page<ReadList> {
|
||||
override fun findAll(belongsToLibraryIds: Collection<String>?, filterOnLibraryIds: Collection<String>?, search: String?, pageable: Pageable, restrictions: ContentRestrictions): Page<ReadList> {
|
||||
val readListIds = luceneHelper.searchEntitiesIds(search, LuceneEntity.ReadList)
|
||||
val searchCondition = rl.ID.inOrNoCondition(readListIds)
|
||||
|
||||
val conditions = searchCondition
|
||||
.and(b.LIBRARY_ID.inOrNoCondition(belongsToLibraryIds))
|
||||
.and(b.LIBRARY_ID.inOrNoCondition(filterOnLibraryIds))
|
||||
.and(restrictions.toCondition())
|
||||
.and(restrictions.toCondition(dsl))
|
||||
|
||||
val queryIds =
|
||||
if (belongsToLibraryIds == null && filterOnLibraryIds == null && restrictions.isEmpty()) null
|
||||
if (belongsToLibraryIds == null && filterOnLibraryIds == null && !restrictions.isRestricted()) null
|
||||
else
|
||||
dsl.selectDistinct(rl.ID)
|
||||
.from(rl)
|
||||
.leftJoin(rlb).on(rl.ID.eq(rlb.READLIST_ID))
|
||||
.leftJoin(b).on(rlb.BOOK_ID.eq(b.ID))
|
||||
.apply { if (restrictions.isNotEmpty()) leftJoin(sd).on(sd.SERIES_ID.eq(b.SERIES_ID)) }
|
||||
.apply { if (restrictions.isRestricted()) leftJoin(sd).on(sd.SERIES_ID.eq(b.SERIES_ID)) }
|
||||
.where(conditions)
|
||||
|
||||
val count =
|
||||
|
|
@ -76,7 +76,7 @@ class ReadListDao(
|
|||
else it.toSortField(sorts)
|
||||
}
|
||||
|
||||
val items = selectBase(restrictions.isNotEmpty())
|
||||
val items = selectBase(restrictions.isRestricted())
|
||||
.where(conditions)
|
||||
.apply { if (queryIds != null) and(rl.ID.`in`(queryIds)) }
|
||||
.orderBy(orderBy)
|
||||
|
|
@ -92,18 +92,18 @@ class ReadListDao(
|
|||
)
|
||||
}
|
||||
|
||||
override fun findAllContainingBookId(containsBookId: String, filterOnLibraryIds: Collection<String>?, restrictions: Set<ContentRestriction>): Collection<ReadList> {
|
||||
override fun findAllContainingBookId(containsBookId: String, filterOnLibraryIds: Collection<String>?, restrictions: ContentRestrictions): Collection<ReadList> {
|
||||
val queryIds = dsl.select(rl.ID)
|
||||
.from(rl)
|
||||
.leftJoin(rlb).on(rl.ID.eq(rlb.READLIST_ID))
|
||||
.apply { if (restrictions.isNotEmpty()) leftJoin(b).on(rlb.BOOK_ID.eq(b.ID)).leftJoin(sd).on(sd.SERIES_ID.eq(b.SERIES_ID)) }
|
||||
.apply { if (restrictions.isRestricted()) leftJoin(b).on(rlb.BOOK_ID.eq(b.ID)).leftJoin(sd).on(sd.SERIES_ID.eq(b.SERIES_ID)) }
|
||||
.where(rlb.BOOK_ID.eq(containsBookId))
|
||||
.apply { if (restrictions.isNotEmpty()) and(restrictions.toCondition()) }
|
||||
.apply { if (restrictions.isRestricted()) and(restrictions.toCondition(dsl)) }
|
||||
|
||||
return selectBase(restrictions.isNotEmpty())
|
||||
return selectBase(restrictions.isRestricted())
|
||||
.where(rl.ID.`in`(queryIds))
|
||||
.apply { filterOnLibraryIds?.let { and(b.LIBRARY_ID.`in`(it)) } }
|
||||
.apply { if (restrictions.isNotEmpty()) and(restrictions.toCondition()) }
|
||||
.apply { if (restrictions.isRestricted()) and(restrictions.toCondition(dsl)) }
|
||||
.fetchAndMap(filterOnLibraryIds, restrictions)
|
||||
}
|
||||
|
||||
|
|
@ -132,16 +132,16 @@ class ReadListDao(
|
|||
.leftJoin(b).on(rlb.BOOK_ID.eq(b.ID))
|
||||
.apply { if (joinOnSeriesMetadata) leftJoin(sd).on(sd.SERIES_ID.eq(b.SERIES_ID)) }
|
||||
|
||||
private fun ResultQuery<Record>.fetchAndMap(filterOnLibraryIds: Collection<String>?, restrictions: Set<ContentRestriction> = emptySet()): List<ReadList> =
|
||||
private fun ResultQuery<Record>.fetchAndMap(filterOnLibraryIds: Collection<String>?, restrictions: ContentRestrictions = ContentRestrictions()): List<ReadList> =
|
||||
fetchInto(rl)
|
||||
.map { rr ->
|
||||
val bookIds = dsl.select(*rlb.fields())
|
||||
.from(rlb)
|
||||
.leftJoin(b).on(rlb.BOOK_ID.eq(b.ID))
|
||||
.apply { if (restrictions.isNotEmpty()) leftJoin(sd).on(sd.SERIES_ID.eq(b.SERIES_ID)) }
|
||||
.apply { if (restrictions.isRestricted()) leftJoin(sd).on(sd.SERIES_ID.eq(b.SERIES_ID)) }
|
||||
.where(rlb.READLIST_ID.eq(rr.id))
|
||||
.apply { filterOnLibraryIds?.let { and(b.LIBRARY_ID.`in`(it)) } }
|
||||
.apply { if (restrictions.isNotEmpty()) and(restrictions.toCondition()) }
|
||||
.apply { if (restrictions.isRestricted()) and(restrictions.toCondition(dsl)) }
|
||||
.orderBy(rlb.NUMBER.asc())
|
||||
.fetchInto(rlb)
|
||||
.mapNotNull { it.number to it.bookId }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package org.gotson.komga.infrastructure.jooq
|
||||
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.domain.model.SeriesCollection
|
||||
import org.gotson.komga.domain.persistence.SeriesCollectionRepository
|
||||
import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource
|
||||
|
|
@ -38,25 +38,25 @@ class SeriesCollectionDao(
|
|||
"name" to c.NAME.collate(SqliteUdfDataSource.collationUnicode3),
|
||||
)
|
||||
|
||||
override fun findByIdOrNull(collectionId: String, filterOnLibraryIds: Collection<String>?, restrictions: Set<ContentRestriction>): SeriesCollection? =
|
||||
selectBase(restrictions.isNotEmpty())
|
||||
override fun findByIdOrNull(collectionId: String, filterOnLibraryIds: Collection<String>?, restrictions: ContentRestrictions): SeriesCollection? =
|
||||
selectBase(restrictions.isRestricted())
|
||||
.where(c.ID.eq(collectionId))
|
||||
.apply { filterOnLibraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } }
|
||||
.apply { if (restrictions.isNotEmpty()) and(restrictions.toCondition()) }
|
||||
.apply { if (restrictions.isRestricted()) and(restrictions.toCondition(dsl)) }
|
||||
.fetchAndMap(filterOnLibraryIds, restrictions)
|
||||
.firstOrNull()
|
||||
|
||||
override fun findAll(belongsToLibraryIds: Collection<String>?, filterOnLibraryIds: Collection<String>?, search: String?, pageable: Pageable, restrictions: Set<ContentRestriction>): Page<SeriesCollection> {
|
||||
override fun findAll(belongsToLibraryIds: Collection<String>?, filterOnLibraryIds: Collection<String>?, search: String?, pageable: Pageable, restrictions: ContentRestrictions): Page<SeriesCollection> {
|
||||
val collectionIds = luceneHelper.searchEntitiesIds(search, LuceneEntity.Collection)
|
||||
val searchCondition = c.ID.inOrNoCondition(collectionIds)
|
||||
|
||||
val conditions = searchCondition
|
||||
.and(s.LIBRARY_ID.inOrNoCondition(belongsToLibraryIds))
|
||||
.and(s.LIBRARY_ID.inOrNoCondition(filterOnLibraryIds))
|
||||
.and(restrictions.toCondition())
|
||||
.and(restrictions.toCondition(dsl))
|
||||
|
||||
val queryIds =
|
||||
if (belongsToLibraryIds == null && filterOnLibraryIds == null && restrictions.isEmpty()) null
|
||||
if (belongsToLibraryIds == null && filterOnLibraryIds == null && !restrictions.isRestricted()) null
|
||||
else
|
||||
dsl.selectDistinct(c.ID)
|
||||
.from(c)
|
||||
|
|
@ -75,7 +75,7 @@ class SeriesCollectionDao(
|
|||
else it.toSortField(sorts)
|
||||
}
|
||||
|
||||
val items = selectBase(restrictions.isNotEmpty())
|
||||
val items = selectBase(restrictions.isRestricted())
|
||||
.where(conditions)
|
||||
.apply { if (queryIds != null) and(c.ID.`in`(queryIds)) }
|
||||
.orderBy(orderBy)
|
||||
|
|
@ -91,18 +91,18 @@ class SeriesCollectionDao(
|
|||
)
|
||||
}
|
||||
|
||||
override fun findAllContainingSeriesId(containsSeriesId: String, filterOnLibraryIds: Collection<String>?, restrictions: Set<ContentRestriction>): Collection<SeriesCollection> {
|
||||
override fun findAllContainingSeriesId(containsSeriesId: String, filterOnLibraryIds: Collection<String>?, restrictions: ContentRestrictions): Collection<SeriesCollection> {
|
||||
val queryIds = dsl.select(c.ID)
|
||||
.from(c)
|
||||
.leftJoin(cs).on(c.ID.eq(cs.COLLECTION_ID))
|
||||
.apply { if (restrictions.isNotEmpty()) leftJoin(sd).on(cs.SERIES_ID.eq(sd.SERIES_ID)) }
|
||||
.apply { if (restrictions.isRestricted()) leftJoin(sd).on(cs.SERIES_ID.eq(sd.SERIES_ID)) }
|
||||
.where(cs.SERIES_ID.eq(containsSeriesId))
|
||||
.apply { if (restrictions.isNotEmpty()) and(restrictions.toCondition()) }
|
||||
.apply { if (restrictions.isRestricted()) and(restrictions.toCondition(dsl)) }
|
||||
|
||||
return selectBase(restrictions.isNotEmpty())
|
||||
return selectBase(restrictions.isRestricted())
|
||||
.where(c.ID.`in`(queryIds))
|
||||
.apply { filterOnLibraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } }
|
||||
.apply { if (restrictions.isNotEmpty()) and(restrictions.toCondition()) }
|
||||
.apply { if (restrictions.isRestricted()) and(restrictions.toCondition(dsl)) }
|
||||
.fetchAndMap(filterOnLibraryIds, restrictions)
|
||||
}
|
||||
|
||||
|
|
@ -131,16 +131,16 @@ class SeriesCollectionDao(
|
|||
.leftJoin(s).on(cs.SERIES_ID.eq(s.ID))
|
||||
.apply { if (joinOnSeriesMetadata) leftJoin(sd).on(cs.SERIES_ID.eq(sd.SERIES_ID)) }
|
||||
|
||||
private fun ResultQuery<Record>.fetchAndMap(filterOnLibraryIds: Collection<String>?, restrictions: Set<ContentRestriction> = emptySet()): List<SeriesCollection> =
|
||||
private fun ResultQuery<Record>.fetchAndMap(filterOnLibraryIds: Collection<String>?, restrictions: ContentRestrictions = ContentRestrictions()): List<SeriesCollection> =
|
||||
fetchInto(c)
|
||||
.map { cr ->
|
||||
val seriesIds = dsl.select(*cs.fields())
|
||||
.from(cs)
|
||||
.leftJoin(s).on(cs.SERIES_ID.eq(s.ID))
|
||||
.apply { if (restrictions.isNotEmpty()) leftJoin(sd).on(cs.SERIES_ID.eq(sd.SERIES_ID)) }
|
||||
.apply { if (restrictions.isRestricted()) leftJoin(sd).on(cs.SERIES_ID.eq(sd.SERIES_ID)) }
|
||||
.where(cs.COLLECTION_ID.eq(cr.id))
|
||||
.apply { filterOnLibraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } }
|
||||
.apply { if (restrictions.isNotEmpty()) and(restrictions.toCondition()) }
|
||||
.apply { if (restrictions.isRestricted()) and(restrictions.toCondition(dsl)) }
|
||||
.orderBy(cs.NUMBER.asc())
|
||||
.fetchInto(cs)
|
||||
.mapNotNull { it.seriesId }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package org.gotson.komga.infrastructure.jooq
|
||||
|
||||
import mu.KotlinLogging
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.domain.model.ReadStatus
|
||||
import org.gotson.komga.domain.model.SeriesSearch
|
||||
import org.gotson.komga.domain.model.SeriesSearchWithReadProgress
|
||||
|
|
@ -26,6 +26,7 @@ import org.jooq.ResultQuery
|
|||
import org.jooq.SelectOnConditionStep
|
||||
import org.jooq.impl.DSL
|
||||
import org.jooq.impl.DSL.count
|
||||
import org.jooq.impl.DSL.countDistinct
|
||||
import org.jooq.impl.DSL.lower
|
||||
import org.jooq.impl.DSL.substring
|
||||
import org.springframework.data.domain.Page
|
||||
|
|
@ -78,8 +79,8 @@ class SeriesDtoDao(
|
|||
"booksCount" to s.BOOK_COUNT,
|
||||
)
|
||||
|
||||
override fun findAll(search: SeriesSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: Set<ContentRestriction>): Page<SeriesDto> {
|
||||
val conditions = search.toCondition().and(restrictions.toCondition())
|
||||
override fun findAll(search: SeriesSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: ContentRestrictions): Page<SeriesDto> {
|
||||
val conditions = search.toCondition().and(restrictions.toCondition(dsl))
|
||||
|
||||
return findAll(conditions, userId, pageable, search.toJoinConditions(), search.searchTerm)
|
||||
}
|
||||
|
|
@ -89,9 +90,9 @@ class SeriesDtoDao(
|
|||
search: SeriesSearchWithReadProgress,
|
||||
userId: String,
|
||||
pageable: Pageable,
|
||||
restrictions: Set<ContentRestriction>,
|
||||
restrictions: ContentRestrictions,
|
||||
): Page<SeriesDto> {
|
||||
val conditions = search.toCondition().and(restrictions.toCondition()).and(cs.COLLECTION_ID.eq(collectionId))
|
||||
val conditions = search.toCondition().and(restrictions.toCondition(dsl)).and(cs.COLLECTION_ID.eq(collectionId))
|
||||
val joinConditions = search.toJoinConditions().copy(selectCollectionNumber = true, collection = true)
|
||||
|
||||
return findAll(conditions, userId, pageable, joinConditions, search.searchTerm)
|
||||
|
|
@ -100,18 +101,18 @@ class SeriesDtoDao(
|
|||
override fun findAllRecentlyUpdated(
|
||||
search: SeriesSearchWithReadProgress,
|
||||
userId: String,
|
||||
restrictions: Set<ContentRestriction>,
|
||||
restrictions: ContentRestrictions,
|
||||
pageable: Pageable,
|
||||
): Page<SeriesDto> {
|
||||
val conditions = search.toCondition()
|
||||
.and(restrictions.toCondition())
|
||||
.and(restrictions.toCondition(dsl))
|
||||
.and(s.CREATED_DATE.notEqual(s.LAST_MODIFIED_DATE))
|
||||
|
||||
return findAll(conditions, userId, pageable, search.toJoinConditions(), search.searchTerm)
|
||||
}
|
||||
|
||||
override fun countByFirstCharacter(search: SeriesSearchWithReadProgress, userId: String, restrictions: Set<ContentRestriction>): List<GroupCountDto> {
|
||||
val conditions = search.toCondition().and(restrictions.toCondition())
|
||||
override fun countByFirstCharacter(search: SeriesSearchWithReadProgress, userId: String, restrictions: ContentRestrictions): List<GroupCountDto> {
|
||||
val conditions = search.toCondition().and(restrictions.toCondition(dsl))
|
||||
val joinConditions = search.toJoinConditions()
|
||||
val seriesIds = luceneHelper.searchEntitiesIds(search.searchTerm, LuceneEntity.Series)
|
||||
val searchCondition = s.ID.inOrNoCondition(seriesIds)
|
||||
|
|
@ -174,7 +175,7 @@ class SeriesDtoDao(
|
|||
val seriesIds = luceneHelper.searchEntitiesIds(searchTerm, LuceneEntity.Series)
|
||||
val searchCondition = s.ID.inOrNoCondition(seriesIds)
|
||||
|
||||
val count = dsl.select(count(s.ID))
|
||||
val count = dsl.select(countDistinct(s.ID))
|
||||
.from(s)
|
||||
.leftJoin(d).on(s.ID.eq(d.SERIES_ID))
|
||||
.leftJoin(bma).on(s.ID.eq(bma.SERIES_ID))
|
||||
|
|
@ -189,7 +190,7 @@ class SeriesDtoDao(
|
|||
.apply { if (joinConditions.aggregationAuthor) leftJoin(bmaa).on(s.ID.eq(bmaa.SERIES_ID)) }
|
||||
.where(conditions)
|
||||
.and(searchCondition)
|
||||
.fetchOne(count(s.ID)) ?: 0
|
||||
.fetchOne(countDistinct(s.ID)) ?: 0
|
||||
|
||||
val orderBy =
|
||||
pageable.sort.mapNotNull {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package org.gotson.komga.infrastructure.jooq
|
||||
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource
|
||||
import org.gotson.komga.jooq.Tables
|
||||
import org.jooq.Condition
|
||||
|
|
@ -65,15 +66,34 @@ fun DSLContext.insertTempStrings(batchSize: Int, collection: Collection<String>)
|
|||
|
||||
fun DSLContext.selectTempStrings() = this.select(Tables.TEMP_STRING_LIST.STRING).from(Tables.TEMP_STRING_LIST)
|
||||
|
||||
fun Set<ContentRestriction>.toCondition(): Condition =
|
||||
this.fold(DSL.noCondition()) { accumulator, restriction ->
|
||||
accumulator.and(
|
||||
when (restriction) {
|
||||
is ContentRestriction.AgeRestriction.AllowOnlyUnder -> Tables.SERIES_METADATA.AGE_RATING.lessOrEqual(restriction.age)
|
||||
is ContentRestriction.AgeRestriction.ExcludeOver -> Tables.SERIES_METADATA.AGE_RATING.isNull.or(Tables.SERIES_METADATA.AGE_RATING.lessThan(restriction.age))
|
||||
// TODO: add conditions for sharing labels
|
||||
is ContentRestriction.LabelsRestriction.AllowOnly -> DSL.noCondition()
|
||||
is ContentRestriction.LabelsRestriction.Exclude -> DSL.noCondition()
|
||||
},
|
||||
)
|
||||
}
|
||||
fun ContentRestrictions.toCondition(dsl: DSLContext): Condition {
|
||||
val ageAllowed = if (ageRestriction is ContentRestriction.AgeRestriction.AllowOnlyUnder) {
|
||||
Tables.SERIES_METADATA.AGE_RATING.isNotNull.and(Tables.SERIES_METADATA.AGE_RATING.lessOrEqual(ageRestriction.age))
|
||||
} else DSL.noCondition()
|
||||
|
||||
val labelAllowed =
|
||||
if (labelsAllowRestriction != null)
|
||||
Tables.SERIES_METADATA.SERIES_ID.`in`(
|
||||
dsl.select(Tables.SERIES_METADATA_SHARING.SERIES_ID)
|
||||
.from(Tables.SERIES_METADATA_SHARING)
|
||||
.where(Tables.SERIES_METADATA_SHARING.LABEL.`in`(labelsAllowRestriction.labels)),
|
||||
)
|
||||
else DSL.noCondition()
|
||||
|
||||
val ageDenied =
|
||||
if (ageRestriction is ContentRestriction.AgeRestriction.ExcludeOver)
|
||||
Tables.SERIES_METADATA.AGE_RATING.isNull.or(Tables.SERIES_METADATA.AGE_RATING.lessThan(ageRestriction.age))
|
||||
else DSL.noCondition()
|
||||
|
||||
val labelDenied =
|
||||
if (labelsExcludeRestriction != null)
|
||||
Tables.SERIES_METADATA.SERIES_ID.notIn(
|
||||
dsl.select(Tables.SERIES_METADATA_SHARING.SERIES_ID)
|
||||
.from(Tables.SERIES_METADATA_SHARING)
|
||||
.where(Tables.SERIES_METADATA_SHARING.LABEL.`in`(labelsExcludeRestriction.labels)),
|
||||
)
|
||||
else DSL.noCondition()
|
||||
|
||||
return ageAllowed.or(labelAllowed)
|
||||
.and(ageDenied.and(labelDenied))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@ import org.springframework.web.server.ResponseStatusException
|
|||
*/
|
||||
fun KomgaUser.checkContentRestriction(series: SeriesDto) {
|
||||
if (!canAccessLibrary(series.libraryId)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
if (isContentRestricted(ageRating = series.metadata.ageRating)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
if (!isContentAllowed(series.metadata.ageRating, series.metadata.sharingLabels)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
package org.gotson.komga.interfaces.api.persistence
|
||||
|
||||
import org.gotson.komga.domain.model.BookSearchWithReadProgress
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.interfaces.api.rest.dto.BookDto
|
||||
import org.springframework.data.domain.Page
|
||||
import org.springframework.data.domain.Pageable
|
||||
|
||||
interface BookDtoRepository {
|
||||
fun findAll(search: BookSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: Set<ContentRestriction> = emptySet()): Page<BookDto>
|
||||
fun findAll(search: BookSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: ContentRestrictions = ContentRestrictions()): Page<BookDto>
|
||||
|
||||
/**
|
||||
* Find books that are part of a readlist, optionally filtered by library
|
||||
|
|
@ -39,7 +39,7 @@ interface BookDtoRepository {
|
|||
filterOnLibraryIds: Collection<String>?,
|
||||
): BookDto?
|
||||
|
||||
fun findAllOnDeck(userId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable, restrictions: Set<ContentRestriction> = emptySet()): Page<BookDto>
|
||||
fun findAllOnDeck(userId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable, restrictions: ContentRestrictions = ContentRestrictions()): Page<BookDto>
|
||||
|
||||
fun findAllDuplicates(userId: String, pageable: Pageable): Page<BookDto>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package org.gotson.komga.interfaces.api.persistence
|
||||
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.domain.model.SeriesSearchWithReadProgress
|
||||
import org.gotson.komga.interfaces.api.rest.dto.GroupCountDto
|
||||
import org.gotson.komga.interfaces.api.rest.dto.SeriesDto
|
||||
|
|
@ -10,9 +10,9 @@ import org.springframework.data.domain.Pageable
|
|||
interface SeriesDtoRepository {
|
||||
fun findByIdOrNull(seriesId: String, userId: String): SeriesDto?
|
||||
|
||||
fun findAll(search: SeriesSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: Set<ContentRestriction> = emptySet()): Page<SeriesDto>
|
||||
fun findAllByCollectionId(collectionId: String, search: SeriesSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: Set<ContentRestriction> = emptySet()): Page<SeriesDto>
|
||||
fun findAllRecentlyUpdated(search: SeriesSearchWithReadProgress, userId: String, restrictions: Set<ContentRestriction>, pageable: Pageable): Page<SeriesDto>
|
||||
fun findAll(search: SeriesSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: ContentRestrictions = ContentRestrictions()): Page<SeriesDto>
|
||||
fun findAllByCollectionId(collectionId: String, search: SeriesSearchWithReadProgress, userId: String, pageable: Pageable, restrictions: ContentRestrictions = ContentRestrictions()): Page<SeriesDto>
|
||||
fun findAllRecentlyUpdated(search: SeriesSearchWithReadProgress, userId: String, restrictions: ContentRestrictions, pageable: Pageable): Page<SeriesDto>
|
||||
|
||||
fun countByFirstCharacter(search: SeriesSearchWithReadProgress, userId: String, restrictions: Set<ContentRestriction>): List<GroupCountDto>
|
||||
fun countByFirstCharacter(search: SeriesSearchWithReadProgress, userId: String, restrictions: ContentRestrictions): List<GroupCountDto>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -691,8 +691,8 @@ class BookController(
|
|||
*/
|
||||
private fun KomgaUser.checkContentRestriction(book: BookDto) {
|
||||
if (!canAccessLibrary(book.libraryId)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
if (restrictions.isNotEmpty()) seriesMetadataRepository.findById(book.seriesId).let {
|
||||
if (isContentRestricted(ageRating = it.ageRating)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
if (restrictions.isRestricted()) seriesMetadataRepository.findById(book.seriesId).let {
|
||||
if (!isContentAllowed(it.ageRating, it.sharingLabels)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -704,8 +704,8 @@ class BookController(
|
|||
*/
|
||||
private fun KomgaUser.checkContentRestriction(book: Book) {
|
||||
if (!canAccessLibrary(book.libraryId)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
if (restrictions.isNotEmpty()) seriesMetadataRepository.findById(book.seriesId).let {
|
||||
if (isContentRestricted(ageRating = it.ageRating)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
if (restrictions.isRestricted()) seriesMetadataRepository.findById(book.seriesId).let {
|
||||
if (!isContentAllowed(it.ageRating, it.sharingLabels)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -721,9 +721,9 @@ class BookController(
|
|||
if (!canAccessLibrary(it)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
}
|
||||
if (restrictions.isNotEmpty()) bookRepository.getSeriesIdOrNull(bookId)?.let { seriesId ->
|
||||
if (restrictions.isRestricted()) bookRepository.getSeriesIdOrNull(bookId)?.let { seriesId ->
|
||||
seriesMetadataRepository.findById(seriesId).let {
|
||||
if (isContentRestricted(ageRating = it.ageRating)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
if (!isContentAllowed(it.ageRating, it.sharingLabels)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
}
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -699,8 +699,8 @@ class SeriesController(
|
|||
if (!canAccessLibrary(it)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
}
|
||||
if (restrictions.isNotEmpty()) seriesMetadataRepository.findById(seriesId).let {
|
||||
if (isContentRestricted(ageRating = it.ageRating)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
if (restrictions.isRestricted()) seriesMetadataRepository.findById(seriesId).let {
|
||||
if (!isContentAllowed(it.ageRating, it.sharingLabels)) throw ResponseStatusException(HttpStatus.FORBIDDEN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
package org.gotson.komga.domain.model
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ContentRestrictionsTest {
|
||||
|
||||
@Test
|
||||
fun `given no arguments when creating restriction then all restrictions are null`() {
|
||||
val restriction = ContentRestrictions()
|
||||
|
||||
assertThat(restriction.ageRestriction).isNull()
|
||||
assertThat(restriction.labelsAllowRestriction).isNull()
|
||||
assertThat(restriction.labelsExcludeRestriction).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given AllowOnlyUnder restriction only when creating restriction then label restrictions are null`() {
|
||||
val restriction = ContentRestrictions(ContentRestriction.AgeRestriction.AllowOnlyUnder(10))
|
||||
|
||||
assertThat(restriction.ageRestriction)
|
||||
.isNotNull
|
||||
.isInstanceOf(ContentRestriction.AgeRestriction.AllowOnlyUnder::class.java)
|
||||
assertThat(restriction.ageRestriction!!.age).isEqualTo(10)
|
||||
|
||||
assertThat(restriction.labelsAllowRestriction).isNull()
|
||||
assertThat(restriction.labelsExcludeRestriction).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given ExcludeOver only when creating restriction then label restrictions are null`() {
|
||||
val restriction = ContentRestrictions(ContentRestriction.AgeRestriction.ExcludeOver(10))
|
||||
|
||||
assertThat(restriction.ageRestriction)
|
||||
.isNotNull
|
||||
.isInstanceOf(ContentRestriction.AgeRestriction.ExcludeOver::class.java)
|
||||
assertThat(restriction.ageRestriction!!.age).isEqualTo(10)
|
||||
|
||||
assertThat(restriction.labelsAllowRestriction).isNull()
|
||||
assertThat(restriction.labelsExcludeRestriction).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given empty labels when creating restriction then label restrictions are normalized`() {
|
||||
val restriction = ContentRestrictions(
|
||||
labelsAllow = setOf("", " "),
|
||||
labelsExclude = setOf("", " "),
|
||||
)
|
||||
|
||||
assertThat(restriction.labelsAllowRestriction).isNull()
|
||||
assertThat(restriction.labelsExcludeRestriction).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given labels with duplicate values when creating restriction then label restrictions are normalized`() {
|
||||
val restriction = ContentRestrictions(
|
||||
labelsAllow = setOf("a", "b", "B", "b ", "b", " B "),
|
||||
labelsExclude = setOf("c", "d", "D", "d ", "d", " D ")
|
||||
)
|
||||
|
||||
assertThat(restriction.labelsAllowRestriction).isNotNull
|
||||
assertThat(restriction.labelsAllowRestriction!!.labels).containsExactlyInAnyOrder("a", "b")
|
||||
assertThat(restriction.labelsExcludeRestriction).isNotNull
|
||||
assertThat(restriction.labelsExcludeRestriction!!.labels).containsExactlyInAnyOrder("c", "d")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given labels with same value in both allow and exclude when creating restriction then exclude labels are removed from allow labels`() {
|
||||
val restriction = ContentRestrictions(
|
||||
labelsAllow = setOf("a", "b", "B", "b ", "b", " B "),
|
||||
labelsExclude = setOf(" A ", "d", "D", "d ", "d", " D ")
|
||||
)
|
||||
|
||||
assertThat(restriction.labelsAllowRestriction).isNotNull
|
||||
assertThat(restriction.labelsAllowRestriction!!.labels).containsExactlyInAnyOrder("b")
|
||||
assertThat(restriction.labelsExcludeRestriction).isNotNull
|
||||
assertThat(restriction.labelsExcludeRestriction!!.labels).containsExactlyInAnyOrder("a", "d")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given allow labels with all values in exclude labels when creating restriction then allow labels is null`() {
|
||||
val restriction = ContentRestrictions(
|
||||
labelsAllow = setOf("a", "b", "B", "b ", "b", " B "),
|
||||
labelsExclude = setOf(" A ", "b", "B", "B ", "b", " B ")
|
||||
)
|
||||
|
||||
assertThat(restriction.labelsAllowRestriction).isNull()
|
||||
assertThat(restriction.labelsExcludeRestriction).isNotNull
|
||||
assertThat(restriction.labelsExcludeRestriction!!.labels).containsExactlyInAnyOrder("a", "b")
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package org.gotson.komga.domain.model
|
|||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.gotson.komga.domain.model.ContentRestriction.AgeRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestriction.LabelsRestriction
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
|
|
@ -15,59 +14,101 @@ class KomgaUserTest {
|
|||
|
||||
@Test
|
||||
fun `given user with age AllowOnlyUnder restriction when checking for content restriction then it is accurate`() {
|
||||
val user = defaultUser.copy(restrictions = setOf(AgeRestriction.AllowOnlyUnder(5)))
|
||||
val user = defaultUser.copy(restrictions = ContentRestrictions(AgeRestriction.AllowOnlyUnder(5)))
|
||||
|
||||
assertThat(user.isContentRestricted(ageRating = 3)).isFalse
|
||||
assertThat(user.isContentRestricted(ageRating = 5)).isFalse
|
||||
assertThat(user.isContentRestricted(ageRating = 8)).isTrue
|
||||
assertThat(user.isContentRestricted(ageRating = null)).isTrue
|
||||
assertThat(user.isContentAllowed(ageRating = 3)).isTrue
|
||||
assertThat(user.isContentAllowed(ageRating = 5)).isTrue
|
||||
assertThat(user.isContentAllowed(ageRating = 8)).isFalse
|
||||
assertThat(user.isContentAllowed(ageRating = null)).isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user with age ExcludeOver restriction when checking for content restriction then it is accurate`() {
|
||||
val user = defaultUser.copy(restrictions = setOf(AgeRestriction.ExcludeOver(16)))
|
||||
val user = defaultUser.copy(restrictions = ContentRestrictions(AgeRestriction.ExcludeOver(16)))
|
||||
|
||||
assertThat(user.isContentRestricted(ageRating = 10)).isFalse
|
||||
assertThat(user.isContentRestricted(ageRating = null)).isFalse
|
||||
assertThat(user.isContentRestricted(ageRating = 16)).isTrue
|
||||
assertThat(user.isContentRestricted(ageRating = 18)).isTrue
|
||||
assertThat(user.isContentAllowed(ageRating = 10)).`as`("age 10 is allowed").isTrue
|
||||
assertThat(user.isContentAllowed(ageRating = null)).`as`("age null is allowed").isTrue
|
||||
assertThat(user.isContentAllowed(ageRating = 16)).`as`("age 16 is not allowed").isFalse
|
||||
assertThat(user.isContentAllowed(ageRating = 18)).`as`("age 18 is not allowed").isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user with sharing label AllowOnly restriction when checking for content restriction then it is accurate`() {
|
||||
val user = defaultUser.copy(restrictions = setOf(LabelsRestriction.AllowOnly(setOf("allow", "this"))))
|
||||
val user = defaultUser.copy(restrictions = ContentRestrictions(labelsAllow = setOf("allow", "this")))
|
||||
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("allow"))).isFalse
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("this"))).isFalse
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("allow", "this"))).isFalse
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("other"))).isTrue
|
||||
assertThat(user.isContentRestricted(sharingLabels = emptySet())).isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("allow"))).`as`("any tag is fine: allow").isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("this"))).`as`("any tag is fine: this").isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("allow", "this"))).`as`("both tags are fine").isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("other"))).`as`("no allowed tags: other").isFalse
|
||||
assertThat(user.isContentAllowed(sharingLabels = emptySet())).`as`("no allowed tags: emptySet").isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user with sharing label Exclude restriction when checking for content restriction then it is accurate`() {
|
||||
val user = defaultUser.copy(restrictions = setOf(LabelsRestriction.Exclude(setOf("exclude", "this"))))
|
||||
val user = defaultUser.copy(restrictions = ContentRestrictions(labelsExclude = setOf("exclude", "this")))
|
||||
|
||||
assertThat(user.isContentRestricted(sharingLabels = emptySet())).isFalse
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("allow"))).isFalse
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("other", "this"))).isTrue
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("this"))).isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = emptySet())).`as`("no label so no exclusion").isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("allow"))).`as`("label allow is not in exclusion list").isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("other", "this"))).`as`("label this is in exclusion list, other label is ignored").isFalse
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("this"))).`as`("label this is in exclusion list").isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user with both sharing label AllowOnly and Exclude restriction when checking for content restriction then it is accurate`() {
|
||||
val user = defaultUser.copy(
|
||||
restrictions = setOf(
|
||||
LabelsRestriction.AllowOnly(setOf("allow", "both")),
|
||||
LabelsRestriction.Exclude(setOf("exclude", "both")),
|
||||
restrictions = ContentRestrictions(
|
||||
labelsAllow = setOf("allow", "both"),
|
||||
labelsExclude = setOf("exclude", "both"),
|
||||
),
|
||||
)
|
||||
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("allow"))).isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("allow", "other"))).isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("allow", "both"))).isFalse
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("exclude"))).isFalse
|
||||
assertThat(user.isContentAllowed(sharingLabels = emptySet())).isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user with both age AllowOnlyUnder restriction and sharing label AllowOnly restriction when checking for content restriction then it is accurate`() {
|
||||
val user = defaultUser.copy(
|
||||
restrictions = ContentRestrictions(
|
||||
ageRestriction = AgeRestriction.AllowOnlyUnder(10),
|
||||
labelsAllow = setOf("allow"),
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("allow"))).isFalse
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("allow", "other"))).isFalse
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("allow", "both"))).isTrue
|
||||
assertThat(user.isContentRestricted(sharingLabels = setOf("exclude"))).isTrue
|
||||
assertThat(user.isContentRestricted(sharingLabels = emptySet())).isTrue
|
||||
assertThat(user.isContentAllowed(ageRating = 5)).`as`("age 5 only is sufficient").isTrue
|
||||
assertThat(user.isContentAllowed(ageRating = 15)).`as`("age 15 is not allowed").isFalse
|
||||
assertThat(user.isContentAllowed(ageRating = null)).`as`("missing age and no allowed label: age null").isFalse
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("allow"))).`as`("allowed tag is sufficient").isTrue
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("other"))).`as`("missing age and no allowed label: other").isFalse
|
||||
assertThat(user.isContentAllowed(sharingLabels = emptySet())).`as`("missing age and empty set label").isFalse
|
||||
assertThat(user.isContentAllowed(5, setOf("allow"))).`as`("age and tag are good").isTrue
|
||||
assertThat(user.isContentAllowed(5, setOf("other"))).`as`("age is good, other tag is ignored").isTrue
|
||||
assertThat(user.isContentAllowed(15, setOf("allow"))).`as`("age is ignored, tag is allowed").isTrue
|
||||
assertThat(user.isContentAllowed(15, setOf("other"))).`as`("age is too high, and no tag is allowed").isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user with both age AllowOnlyUnder restriction and sharing label Exclude restriction when checking for content restriction then it is accurate`() {
|
||||
val user = defaultUser.copy(
|
||||
restrictions = ContentRestrictions(
|
||||
ageRestriction = AgeRestriction.AllowOnlyUnder(10),
|
||||
labelsExclude = setOf("exclude"),
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(user.isContentAllowed(ageRating = 5)).isTrue
|
||||
assertThat(user.isContentAllowed(ageRating = 15)).isFalse
|
||||
assertThat(user.isContentAllowed(ageRating = null)).isFalse
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("exclude"))).isFalse
|
||||
assertThat(user.isContentAllowed(sharingLabels = setOf("other"))).isFalse
|
||||
assertThat(user.isContentAllowed(sharingLabels = emptySet())).isFalse
|
||||
assertThat(user.isContentAllowed(5, setOf("exclude"))).isFalse
|
||||
assertThat(user.isContentAllowed(5, setOf("other"))).isTrue
|
||||
assertThat(user.isContentAllowed(15, setOf("exclude"))).isFalse
|
||||
assertThat(user.isContentAllowed(15, setOf("other"))).isFalse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class BookControllerTest(
|
|||
inner class RestrictedContent {
|
||||
@Test
|
||||
@WithMockCustomUser(allowAgeUnder = 10)
|
||||
fun `given user only allowed content with specific age rating when getting series then only gets books that satisfies this criteria`() {
|
||||
fun `given user only allowed content with specific age rating when getting books then only gets books that satisfies this criteria`() {
|
||||
val book10 = makeBook("book_10", libraryId = library.id)
|
||||
makeSeries(name = "series_10", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
|
|
@ -188,7 +188,7 @@ class BookControllerTest(
|
|||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeAgeOver = 10)
|
||||
fun `given user disallowed content with specific age rating when getting series then only gets books that satisfies this criteria`() {
|
||||
fun `given user disallowed content with specific age rating when getting books then only gets books that satisfies this criteria`() {
|
||||
val book10 = makeBook("book_10", libraryId = library.id)
|
||||
makeSeries(name = "series_10", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
|
|
@ -243,6 +243,203 @@ class BookControllerTest(
|
|||
jsonPath("$.content[1].name") { value(book5.name) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(allowLabels = ["kids", "cute"])
|
||||
fun `given user allowed content with specific labels when getting series then only gets books that satisfies this criteria`() {
|
||||
val bookKids = makeBook("book_kids", libraryId = library.id)
|
||||
makeSeries(name = "series_kids", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookKids)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookCute = makeBook("book_cute", libraryId = library.id)
|
||||
makeSeries(name = "series_cute", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookCute)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookAdult = makeBook("book_adult", libraryId = library.id)
|
||||
makeSeries(name = "series_adult", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookAdult)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val book = makeBook("book", libraryId = library.id)
|
||||
makeSeries(name = "series_no", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/books/${bookKids.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/books/${bookCute.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/books/${bookAdult.id}").andExpect { status { isForbidden() } }
|
||||
mockMvc.get("/api/v1/books/${book.id}").andExpect { status { isForbidden() } }
|
||||
|
||||
mockMvc.get("/api/v1/books")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.content.length()") { value(2) }
|
||||
jsonPath("$.content[0].name") { value(bookCute.name) }
|
||||
jsonPath("$.content[1].name") { value(bookKids.name) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeLabels = ["kids", "cute"])
|
||||
fun `given user disallowed content with specific labels when getting books then only gets books that satisfies this criteria`() {
|
||||
val bookKids = makeBook("book_kids", libraryId = library.id)
|
||||
makeSeries(name = "series_kids", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookKids)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookCute = makeBook("book_cute", libraryId = library.id)
|
||||
makeSeries(name = "series_cute", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookCute)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookAdult = makeBook("book_adult", libraryId = library.id)
|
||||
makeSeries(name = "series_adult", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookAdult)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val book = makeBook("book", libraryId = library.id)
|
||||
makeSeries(name = "series_no", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/books/${bookKids.id}").andExpect { status { isForbidden() } }
|
||||
mockMvc.get("/api/v1/books/${bookCute.id}").andExpect { status { isForbidden() } }
|
||||
mockMvc.get("/api/v1/books/${bookAdult.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/books/${book.id}").andExpect { status { isOk() } }
|
||||
|
||||
mockMvc.get("/api/v1/books")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.content.length()") { value(2) }
|
||||
jsonPath("$.content[0].name") { value(book.name) }
|
||||
jsonPath("$.content[1].name") { value(bookAdult.name) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(allowAgeUnder = 10, allowLabels = ["kids"], excludeLabels = ["adult", "teen"])
|
||||
fun `given user allowed and disallowed content labels when getting books then only gets books that satisfies this criteria`() {
|
||||
val bookKids = makeBook("book_kids", libraryId = library.id)
|
||||
makeSeries(name = "series_kids", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookKids)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookCute = makeBook("book_cute", libraryId = library.id)
|
||||
makeSeries(name = "series_cute", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookCute)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 5, sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookAdult = makeBook("book_adult", libraryId = library.id)
|
||||
makeSeries(name = "series_adult", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookAdult)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val book = makeBook("book", libraryId = library.id)
|
||||
makeSeries(name = "series_no", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/books/${bookKids.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/books/${bookCute.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/books/${bookAdult.id}").andExpect { status { isForbidden() } }
|
||||
mockMvc.get("/api/v1/books/${book.id}").andExpect { status { isForbidden() } }
|
||||
|
||||
mockMvc.get("/api/v1/books")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.content.length()") { value(2) }
|
||||
jsonPath("$.content[0].name") { value(bookCute.name) }
|
||||
jsonPath("$.content[1].name") { value(bookKids.name) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeAgeOver = 16, allowLabels = ["teen"])
|
||||
fun `given user allowed and disallowed content labels when getting books then only gets books that satisfies this criteria (2)`() {
|
||||
val bookTeen16 = makeBook("book_teen_16", libraryId = library.id)
|
||||
makeSeries(name = "series_teen_16", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookTeen16)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("teen"), ageRating = 16))
|
||||
}
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/books/${bookTeen16.id}").andExpect { status { isForbidden() } }
|
||||
|
||||
mockMvc.get("/api/v1/books")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.content.length()") { value(0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package org.gotson.komga.interfaces.api.rest
|
||||
|
||||
import org.gotson.komga.domain.model.ContentRestriction
|
||||
import org.gotson.komga.domain.model.ContentRestrictions
|
||||
import org.gotson.komga.domain.model.KomgaUser
|
||||
import org.gotson.komga.domain.model.ROLE_ADMIN
|
||||
import org.gotson.komga.domain.model.ROLE_FILE_DOWNLOAD
|
||||
|
|
@ -40,16 +41,13 @@ class WithMockCustomUserSecurityContextFactory : WithSecurityContextFactory<With
|
|||
rolePageStreaming = customUser.roles.contains(ROLE_PAGE_STREAMING),
|
||||
sharedAllLibraries = customUser.sharedAllLibraries,
|
||||
sharedLibrariesIds = customUser.sharedLibraries.toSet(),
|
||||
restrictions = buildSet {
|
||||
if (customUser.allowAgeUnder >= 0) add(ContentRestriction.AgeRestriction.AllowOnlyUnder(customUser.allowAgeUnder))
|
||||
if (customUser.excludeAgeOver >= 0) add(ContentRestriction.AgeRestriction.ExcludeOver(customUser.excludeAgeOver))
|
||||
if (customUser.allowLabels.isNotEmpty()) {
|
||||
add(ContentRestriction.LabelsRestriction.AllowOnly(customUser.allowLabels.toSet()))
|
||||
}
|
||||
if (customUser.excludeLabels.isNotEmpty()) {
|
||||
add(ContentRestriction.LabelsRestriction.Exclude(customUser.excludeLabels.toSet()))
|
||||
}
|
||||
},
|
||||
restrictions = ContentRestrictions(
|
||||
ageRestriction = if (customUser.allowAgeUnder >= 0) ContentRestriction.AgeRestriction.AllowOnlyUnder(customUser.allowAgeUnder)
|
||||
else if (customUser.excludeAgeOver >= 0) ContentRestriction.AgeRestriction.ExcludeOver(customUser.excludeAgeOver)
|
||||
else null,
|
||||
labelsAllow = customUser.allowLabels.toSet(),
|
||||
labelsExclude = customUser.excludeLabels.toSet(),
|
||||
),
|
||||
id = customUser.id,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -176,9 +176,9 @@ class ReadListControllerTest(
|
|||
inner class ContentRestriction {
|
||||
@Test
|
||||
@WithMockCustomUser(allowAgeUnder = 10)
|
||||
fun `given user only allowed content with specific age rating when getting collections then only get collections that satisfies this criteria`() {
|
||||
fun `given user only allowed content with specific age rating when getting read lists then only get read lists that satisfies this criteria`() {
|
||||
val book10 = makeBook("book_10", libraryId = library1.id)
|
||||
val series10 = makeSeries(name = "series_10", libraryId = library1.id).also { series ->
|
||||
makeSeries(name = "series_10", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book10)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
|
|
@ -189,7 +189,7 @@ class ReadListControllerTest(
|
|||
}
|
||||
|
||||
val book = makeBook("book", libraryId = library1.id)
|
||||
val series = makeSeries(name = "series_no", libraryId = library1.id).also { series ->
|
||||
makeSeries(name = "series_no", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
|
|
@ -252,6 +252,391 @@ class ReadListControllerTest(
|
|||
jsonPath("$[?(@.name == '${rlFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeAgeOver = 16)
|
||||
fun `given user disallowed content with specific age rating when getting read lists then only get read lists that satisfies this criteria`() {
|
||||
val book10 = makeBook("book_10", libraryId = library1.id)
|
||||
makeSeries(name = "series_10", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book10)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 10))
|
||||
}
|
||||
}
|
||||
|
||||
val book18 = makeBook("1", libraryId = library1.id)
|
||||
makeSeries(name = "series_18", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book18)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 18))
|
||||
}
|
||||
}
|
||||
|
||||
val book16 = makeBook("1", libraryId = library1.id)
|
||||
makeSeries(name = "series_16", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book16)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 16))
|
||||
}
|
||||
}
|
||||
|
||||
val book = makeBook("book", libraryId = library1.id)
|
||||
makeSeries(name = "series_no", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
val rlAllowed = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Allowed",
|
||||
bookIds = listOf(book10.id, book.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
val rlFiltered = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Filtered",
|
||||
bookIds = listOf(book10.id, book.id, book16.id, book18.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
val rlDenied = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Denied",
|
||||
bookIds = listOf(book16.id, book18.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
mockMvc.get("/api/v1/readlists")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.totalElements") { value(2) }
|
||||
jsonPath("$.content[?(@.name == '${rlAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$.content[?(@.name == '${rlFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlAllowed.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.bookIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(false) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlFiltered.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.bookIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlDenied.id}")
|
||||
.andExpect {
|
||||
status { isNotFound() }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/books/${book10.id}/readlists")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.length()") { value(2) }
|
||||
jsonPath("$[?(@.name == '${rlAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$[?(@.name == '${rlFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeLabels = ["kids", "cute"])
|
||||
fun `given user disallowed content with specific labels when getting read lists then only get read lists that satisfies this criteria`() {
|
||||
val bookKids = makeBook("book_kids", libraryId = library1.id)
|
||||
makeSeries(name = "series_kids", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookKids)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookCute = makeBook("1", libraryId = library1.id)
|
||||
makeSeries(name = "series_cute", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookCute)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookAdult = makeBook("1", libraryId = library1.id)
|
||||
makeSeries(name = "series_adult", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookAdult)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val book = makeBook("book", libraryId = library1.id)
|
||||
makeSeries(name = "series_no", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
val rlAllowed = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Allowed",
|
||||
bookIds = listOf(bookAdult.id, book.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
val rlFiltered = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Filtered",
|
||||
bookIds = listOf(bookKids.id, book.id, bookAdult.id, bookCute.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
val rlDenied = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Denied",
|
||||
bookIds = listOf(bookKids.id, bookCute.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
mockMvc.get("/api/v1/readlists")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.totalElements") { value(2) }
|
||||
jsonPath("$.content[?(@.name == '${rlAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$.content[?(@.name == '${rlFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlAllowed.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.bookIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(false) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlFiltered.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.bookIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlDenied.id}")
|
||||
.andExpect {
|
||||
status { isNotFound() }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/books/${bookAdult.id}/readlists")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.length()") { value(2) }
|
||||
jsonPath("$[?(@.name == '${rlAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$[?(@.name == '${rlFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(allowAgeUnder = 10, allowLabels = ["kids"], excludeLabels = ["adult", "teen"])
|
||||
fun `given user allowed and disallowed content when getting read lists then only get read lists that satisfies this criteria`() {
|
||||
val bookKids = makeBook("book_kids", libraryId = library1.id)
|
||||
makeSeries(name = "series_kids", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookKids)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookCute = makeBook("book_cute", libraryId = library1.id)
|
||||
makeSeries(name = "series_cute", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookCute)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 5, sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val bookAdult = makeBook("book_adult", libraryId = library1.id)
|
||||
makeSeries(name = "series_adult", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookAdult)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val book = makeBook("book", libraryId = library1.id)
|
||||
makeSeries(name = "series_no", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(book)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
val rlAllowed = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Allowed",
|
||||
bookIds = listOf(bookKids.id, bookCute.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
val rlFiltered = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Filtered",
|
||||
bookIds = listOf(bookKids.id, book.id, bookAdult.id, bookCute.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
val rlDenied = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Denied",
|
||||
bookIds = listOf(bookAdult.id, book.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
mockMvc.get("/api/v1/readlists")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.totalElements") { value(2) }
|
||||
jsonPath("$.content[?(@.name == '${rlAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$.content[?(@.name == '${rlFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlAllowed.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.bookIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(false) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlFiltered.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.bookIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlDenied.id}")
|
||||
.andExpect {
|
||||
status { isNotFound() }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/books/${bookKids.id}/readlists")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.length()") { value(2) }
|
||||
jsonPath("$[?(@.name == '${rlAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$[?(@.name == '${rlFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeAgeOver = 16, allowLabels = ["teen"])
|
||||
fun `given user allowed and disallowed content when getting read lists then only get read lists that satisfies this criteria (2)`() {
|
||||
val bookTeen16 = makeBook("book_teen_16", libraryId = library1.id)
|
||||
makeSeries(name = "series_teen_16", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookTeen16)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("teen"), ageRating = 16))
|
||||
}
|
||||
}
|
||||
|
||||
val bookTeen = makeBook("1", libraryId = library1.id)
|
||||
makeSeries(name = "series_teen", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(bookTeen)
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("teen")))
|
||||
}
|
||||
}
|
||||
|
||||
val rlAllowed = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Allowed",
|
||||
bookIds = listOf(bookTeen.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
val rlFiltered = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Filtered",
|
||||
bookIds = listOf(bookTeen16.id, bookTeen.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
val rlDenied = readListLifecycle.addReadList(
|
||||
ReadList(
|
||||
name = "Denied",
|
||||
bookIds = listOf(bookTeen16.id).toIndexedMap(),
|
||||
),
|
||||
)
|
||||
|
||||
mockMvc.get("/api/v1/readlists")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.totalElements") { value(2) }
|
||||
jsonPath("$.content[?(@.name == '${rlAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$.content[?(@.name == '${rlFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlAllowed.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.bookIds.length()") { value(1) }
|
||||
jsonPath("$.filtered") { value(false) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlFiltered.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.bookIds.length()") { value(1) }
|
||||
jsonPath("$.filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/readlists/${rlDenied.id}")
|
||||
.andExpect {
|
||||
status { isNotFound() }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/books/${bookTeen.id}/readlists")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.length()") { value(2) }
|
||||
jsonPath("$[?(@.name == '${rlAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$[?(@.name == '${rlFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
|
|
|||
|
|
@ -246,6 +246,474 @@ class SeriesCollectionControllerTest(
|
|||
jsonPath("$[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeAgeOver = 16)
|
||||
fun `given user disallowed content with specific age rating when getting collections then only gets collections that satisfies this criteria`() {
|
||||
val series10 = makeSeries(name = "series_10", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 10))
|
||||
}
|
||||
}
|
||||
|
||||
val series18 = makeSeries(name = "series_18", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 18))
|
||||
}
|
||||
}
|
||||
|
||||
val series16 = makeSeries(name = "series_16", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 16))
|
||||
}
|
||||
}
|
||||
|
||||
val series = makeSeries(name = "series_no", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
val colAllowed = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Allowed",
|
||||
seriesIds = listOf(series10.id, series.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colFiltered = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Filtered",
|
||||
seriesIds = listOf(series10.id, series16.id, series18.id, series.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colDenied = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Denied",
|
||||
seriesIds = listOf(series16.id, series18.id),
|
||||
),
|
||||
)
|
||||
|
||||
mockMvc.get("/api/v1/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.totalElements") { value(2) }
|
||||
jsonPath("$.content[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$.content[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colAllowed.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(false) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colFiltered.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colDenied.id}")
|
||||
.andExpect {
|
||||
status { isNotFound() }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/series/${series10.id}/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.length()") { value(2) }
|
||||
jsonPath("$[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(allowLabels = ["kids", "cute"])
|
||||
fun `given user allowed only content with specific labels when getting series then only gets series that satisfies this criteria`() {
|
||||
val seriesKids = makeSeries(name = "series_kids", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesCute = makeSeries(name = "series_cute", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesAdult = makeSeries(name = "series_adult", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val series = makeSeries(name = "series_no", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
val colAllowed = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Allowed",
|
||||
seriesIds = listOf(seriesKids.id, seriesCute.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colFiltered = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Filtered",
|
||||
seriesIds = listOf(series.id, seriesKids.id, seriesCute.id, seriesAdult.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colDenied = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Denied",
|
||||
seriesIds = listOf(seriesAdult.id, series.id),
|
||||
),
|
||||
)
|
||||
|
||||
mockMvc.get("/api/v1/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.totalElements") { value(2) }
|
||||
jsonPath("$.content[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$.content[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colAllowed.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(false) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colFiltered.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colDenied.id}")
|
||||
.andExpect {
|
||||
status { isNotFound() }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/series/${seriesKids.id}/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.length()") { value(2) }
|
||||
jsonPath("$[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeLabels = ["kids", "cute"])
|
||||
fun `given user disallowed content with specific labels when getting series then only gets series that satisfies this criteria`() {
|
||||
val seriesKids = makeSeries(name = "series_kids", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesCute = makeSeries(name = "series_cute", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesAdult = makeSeries(name = "series_adult", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val series = makeSeries(name = "series_no", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
val colAllowed = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Allowed",
|
||||
seriesIds = listOf(seriesAdult.id, series.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colFiltered = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Filtered",
|
||||
seriesIds = listOf(seriesAdult.id, seriesCute.id, seriesKids.id, series.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colDenied = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Denied",
|
||||
seriesIds = listOf(seriesKids.id, seriesCute.id),
|
||||
),
|
||||
)
|
||||
|
||||
mockMvc.get("/api/v1/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.totalElements") { value(2) }
|
||||
jsonPath("$.content[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$.content[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colAllowed.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(false) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colFiltered.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colDenied.id}")
|
||||
.andExpect {
|
||||
status { isNotFound() }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/series/${series.id}/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.length()") { value(2) }
|
||||
jsonPath("$[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(allowAgeUnder = 10, allowLabels = ["kids"], excludeLabels = ["adult", "teen"])
|
||||
fun `given user allowed and disallowed content when getting series then only gets series that satisfies this criteria`() {
|
||||
val seriesKids = makeSeries(name = "series_kids", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesCute = makeSeries(name = "series_cute", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 5, sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesAdult = makeSeries(name = "series_adult", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val series = makeSeries(name = "series_no", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
val colAllowed = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Allowed",
|
||||
seriesIds = listOf(seriesKids.id, seriesCute.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colFiltered = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Filtered",
|
||||
seriesIds = listOf(series.id, seriesKids.id, seriesCute.id, seriesAdult.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colDenied = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Denied",
|
||||
seriesIds = listOf(seriesAdult.id, series.id),
|
||||
),
|
||||
)
|
||||
|
||||
mockMvc.get("/api/v1/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.totalElements") { value(2) }
|
||||
jsonPath("$.content[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$.content[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colAllowed.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(false) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colFiltered.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(2) }
|
||||
jsonPath("$.filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colDenied.id}")
|
||||
.andExpect {
|
||||
status { isNotFound() }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/series/${seriesKids.id}/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.length()") { value(2) }
|
||||
jsonPath("$[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeAgeOver = 16, allowLabels = ["teen"])
|
||||
fun `given user allowed and disallowed content when getting series then only gets series that satisfies this criteria (2)`() {
|
||||
val seriesTeen16 = makeSeries(name = "series_teen_16", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("teen"), ageRating = 16))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesTeen = makeSeries(name = "series_teen", libraryId = library1.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library1.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("teen")))
|
||||
}
|
||||
}
|
||||
|
||||
val colAllowed = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Allowed",
|
||||
seriesIds = listOf(seriesTeen.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colFiltered = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Filtered",
|
||||
seriesIds = listOf(seriesTeen16.id, seriesTeen.id),
|
||||
),
|
||||
)
|
||||
|
||||
val colDenied = collectionLifecycle.addCollection(
|
||||
SeriesCollection(
|
||||
name = "Denied",
|
||||
seriesIds = listOf(seriesTeen16.id),
|
||||
),
|
||||
)
|
||||
|
||||
mockMvc.get("/api/v1/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.totalElements") { value(2) }
|
||||
jsonPath("$.content[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$.content[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colAllowed.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(1) }
|
||||
jsonPath("$.filtered") { value(false) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colFiltered.id}")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.seriesIds.length()") { value(1) }
|
||||
jsonPath("$.filtered") { value(true) }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/collections/${colDenied.id}")
|
||||
.andExpect {
|
||||
status { isNotFound() }
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/series/${seriesTeen.id}/collections")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.length()") { value(2) }
|
||||
jsonPath("$[?(@.name == '${colAllowed.name}')].filtered") { value(false) }
|
||||
jsonPath("$[?(@.name == '${colFiltered.name}')].filtered") { value(true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
|
|
|||
|
|
@ -352,6 +352,190 @@ class SeriesControllerTest(
|
|||
jsonPath("$.content[1].name") { value("series_no") }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(allowLabels = ["kids", "cute"])
|
||||
fun `given user allowed only content with specific labels when getting series then only gets series that satisfies this criteria`() {
|
||||
val seriesKids = makeSeries(name = "series_kids", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesCute = makeSeries(name = "series_cute", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesAdult = makeSeries(name = "series_adult", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val series = makeSeries(name = "series_no", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/series/${seriesKids.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/series/${seriesCute.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/series/${seriesAdult.id}").andExpect { status { isForbidden() } }
|
||||
mockMvc.get("/api/v1/series/${series.id}").andExpect { status { isForbidden() } }
|
||||
|
||||
mockMvc.get("/api/v1/series")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.content.length()") { value(2) }
|
||||
jsonPath("$.content[0].name") { value(seriesCute.name) }
|
||||
jsonPath("$.content[1].name") { value(seriesKids.name) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeLabels = ["kids", "cute"])
|
||||
fun `given user disallowed content with specific labels when getting series then only gets series that satisfies this criteria`() {
|
||||
val seriesKids = makeSeries(name = "series_kids", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesCute = makeSeries(name = "series_cute", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesAdult = makeSeries(name = "series_adult", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val series = makeSeries(name = "series_no", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/series/${seriesKids.id}").andExpect { status { isForbidden() } }
|
||||
mockMvc.get("/api/v1/series/${seriesCute.id}").andExpect { status { isForbidden() } }
|
||||
mockMvc.get("/api/v1/series/${seriesAdult.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/series/${series.id}").andExpect { status { isOk() } }
|
||||
|
||||
mockMvc.get("/api/v1/series")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.content.length()") { value(2) }
|
||||
jsonPath("$.content[0].name") { value(seriesAdult.name) }
|
||||
jsonPath("$.content[1].name") { value(series.name) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(allowAgeUnder = 10, allowLabels = ["kids"], excludeLabels = ["adult", "teen"])
|
||||
fun `given user allowed and disallowed content when getting series then only gets series that satisfies this criteria`() {
|
||||
val seriesKids = makeSeries(name = "series_kids", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("kids")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesCute = makeSeries(name = "series_cute", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(ageRating = 5, sharingLabels = setOf("cute", "other")))
|
||||
}
|
||||
}
|
||||
|
||||
val seriesAdult = makeSeries(name = "series_adult", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("adult")))
|
||||
}
|
||||
}
|
||||
|
||||
val series = makeSeries(name = "series_no", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/series/${seriesKids.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/series/${seriesCute.id}").andExpect { status { isOk() } }
|
||||
mockMvc.get("/api/v1/series/${seriesAdult.id}").andExpect { status { isForbidden() } }
|
||||
mockMvc.get("/api/v1/series/${series.id}").andExpect { status { isForbidden() } }
|
||||
|
||||
mockMvc.get("/api/v1/series")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.content.length()") { value(2) }
|
||||
jsonPath("$.content[0].name") { value(seriesCute.name) }
|
||||
jsonPath("$.content[1].name") { value(seriesKids.name) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockCustomUser(excludeAgeOver = 16, allowLabels = ["teen"])
|
||||
fun `given user allowed and disallowed content when getting series then only gets series that satisfies this criteria (2)`() {
|
||||
val seriesTeen16 = makeSeries(name = "series_teen_16", libraryId = library.id).also { series ->
|
||||
seriesLifecycle.createSeries(series).also { created ->
|
||||
val books = listOf(makeBook("1", libraryId = library.id))
|
||||
seriesLifecycle.addBooks(created, books)
|
||||
}
|
||||
seriesMetadataRepository.findById(series.id).let {
|
||||
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("teen"), ageRating = 16))
|
||||
}
|
||||
}
|
||||
|
||||
mockMvc.get("/api/v1/series/${seriesTeen16.id}").andExpect { status { isForbidden() } }
|
||||
|
||||
mockMvc.get("/api/v1/series")
|
||||
.andExpect {
|
||||
status { isOk() }
|
||||
jsonPath("$.content.length()") { value(0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
|
|
|||
Loading…
Reference in a new issue