feat(api): add v2 version of referential API

supports pagination
some endpoints support filtering and search
This commit is contained in:
Gauthier Roebroeck 2026-02-25 17:11:02 +08:00
parent 8364321034
commit b3c83a9190
12 changed files with 2483 additions and 98 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
CREATE VIEW SERIES_AND_BOOK_TAG AS
SELECT BMAT.TAG, BMAT.SERIES_ID
FROM BOOK_METADATA_AGGREGATION_TAG BMAT
UNION ALL
SELECT SMT.TAG, SMT.SERIES_ID
FROM SERIES_METADATA_TAG SMT;

View file

@ -11,3 +11,9 @@ data class FilterBy(
val type: FilterByEntity,
val ids: Set<String>,
)
enum class FilterTags {
SERIES,
BOOK,
BOTH,
}

View file

@ -2,40 +2,47 @@ package org.gotson.komga.domain.persistence
import org.gotson.komga.domain.model.Author
import org.gotson.komga.domain.model.FilterBy
import org.gotson.komga.domain.model.FilterTags
import org.gotson.komga.domain.model.SearchContext
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import java.time.LocalDate
interface ReferentialRepository {
@Deprecated("Use findAuthors instead")
fun findAllAuthorsByName(
search: String,
filterOnLibraryIds: Collection<String>?,
): List<Author>
@Deprecated("Use findAuthors instead")
fun findAllAuthorsByNameAndLibrary(
search: String,
libraryId: String,
filterOnLibraryIds: Collection<String>?,
): List<Author>
@Deprecated("Use findAuthors instead")
fun findAllAuthorsByNameAndCollection(
search: String,
collectionId: String,
filterOnLibraryIds: Collection<String>?,
): List<Author>
@Deprecated("Use findAuthors instead")
fun findAllAuthorsByNameAndSeries(
search: String,
seriesId: String,
filterOnLibraryIds: Collection<String>?,
): List<Author>
@Deprecated("Use findAuthorsNames instead")
fun findAllAuthorsNamesByName(
search: String,
filterOnLibraryIds: Collection<String>?,
): List<String>
@Deprecated("Use findAuthorsRoles instead")
fun findAllAuthorsRoles(filterOnLibraryIds: Collection<String>?): List<String>
fun findAuthors(
@ -46,116 +53,206 @@ interface ReferentialRepository {
pageable: Pageable,
): Page<Author>
fun findAuthorsRoles(
context: SearchContext,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String>
fun findAuthorsNames(
context: SearchContext,
search: String?,
role: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String>
@Deprecated("Use findGenres instead")
fun findAllGenres(filterOnLibraryIds: Collection<String>?): Set<String>
@Deprecated("Use findGenres instead")
fun findAllGenresByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
): Set<String>
@Deprecated("Use findGenres instead")
fun findAllGenresByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
): Set<String>
fun findGenres(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String>
@Deprecated("Use findTags instead")
fun findAllSeriesAndBookTags(filterOnLibraryIds: Collection<String>?): Set<String>
@Deprecated("Use findTags instead")
fun findAllSeriesAndBookTagsByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
): Set<String>
@Deprecated("Use findTags instead")
fun findAllSeriesAndBookTagsByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
): Set<String>
@Deprecated("Use findTags instead")
fun findAllSeriesTags(filterOnLibraryIds: Collection<String>?): Set<String>
@Deprecated("Use findTags instead")
fun findAllSeriesTagsByLibrary(
libraryId: String,
filterOnLibraryIds: Collection<String>?,
): Set<String>
@Deprecated("Use findTags instead")
fun findAllSeriesTagsByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
): Set<String>
@Deprecated("Use findTags instead")
fun findAllBookTags(filterOnLibraryIds: Collection<String>?): Set<String>
@Deprecated("Use findTags instead")
fun findAllBookTagsBySeries(
seriesId: String,
filterOnLibraryIds: Collection<String>?,
): Set<String>
@Deprecated("Use findTags instead")
fun findAllBookTagsByReadList(
readListId: String,
filterOnLibraryIds: Collection<String>?,
): Set<String>
fun findTags(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
filterTags: FilterTags,
pageable: Pageable,
): Page<String>
@Deprecated("Use findLanguages instead")
fun findAllLanguages(filterOnLibraryIds: Collection<String>?): Set<String>
@Deprecated("Use findLanguages instead")
fun findAllLanguagesByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
): Set<String>
@Deprecated("Use findLanguages instead")
fun findAllLanguagesByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
): Set<String>
fun findLanguages(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String>
@Deprecated("Use findPublishers instead")
fun findAllPublishers(filterOnLibraryIds: Collection<String>?): Set<String>
@Deprecated("Use findPublishers instead")
fun findAllPublishers(
filterOnLibraryIds: Collection<String>?,
pageable: Pageable,
): Page<String>
@Deprecated("Use findPublishers instead")
fun findAllPublishersByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
): Set<String>
@Deprecated("Use findPublishers instead")
fun findAllPublishersByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
): Set<String>
fun findPublishers(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String>
@Deprecated("Use findAgeRatings instead")
fun findAllAgeRatings(filterOnLibraryIds: Collection<String>?): Set<Int?>
@Deprecated("Use findAgeRatings instead")
fun findAllAgeRatingsByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
): Set<Int?>
@Deprecated("Use findAgeRatings instead")
fun findAllAgeRatingsByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
): Set<Int?>
fun findAgeRatings(
context: SearchContext,
filterBy: FilterBy?,
pageable: Pageable,
): Page<Int>
@Deprecated("Use findSeriesReleaseDates instead")
fun findAllSeriesReleaseDates(filterOnLibraryIds: Collection<String>?): Set<LocalDate>
@Deprecated("Use findSeriesReleaseDates instead")
fun findAllSeriesReleaseDatesByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
): Set<LocalDate>
@Deprecated("Use findSeriesReleaseDates instead")
fun findAllSeriesReleaseDatesByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
): Set<LocalDate>
fun findSeriesReleaseDates(
context: SearchContext,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String>
@Deprecated("Use findSharingLabels instead")
fun findAllSharingLabels(filterOnLibraryIds: Collection<String>?): Set<String>
@Deprecated("Use findSharingLabels instead")
fun findAllSharingLabelsByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
): Set<String>
@Deprecated("Use findSharingLabels instead")
fun findAllSharingLabelsByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
): Set<String>
fun findSharingLabels(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String>
}

View file

@ -19,7 +19,7 @@ private val logger = KotlinLogging.logger {}
*/
class BookSearchHelper(
val context: SearchContext,
) : ContentRestrictionsSearchHelper() {
) {
fun toCondition(searchCondition: SearchCondition.Book?): Pair<Condition, Set<RequiredJoin>> {
val base = toCondition()
val search = toConditionInternal(searchCondition)
@ -27,7 +27,7 @@ class BookSearchHelper(
}
fun toCondition(): Pair<Condition, Set<RequiredJoin>> {
val restrictions = toConditionInternal(context.restrictions)
val restrictions = ContentRestrictionsSearchHelper(context.restrictions).toCondition()
val authorizedLibraries = toConditionInternal(context.libraryIds)
return restrictions.first.and(authorizedLibraries.first) to (restrictions.second + authorizedLibraries.second)
}

View file

@ -6,8 +6,10 @@ import org.gotson.komga.jooq.main.Tables
import org.jooq.Condition
import org.jooq.impl.DSL
abstract class ContentRestrictionsSearchHelper {
protected fun toConditionInternal(restrictions: ContentRestrictions): Pair<Condition, Set<RequiredJoin>> {
class ContentRestrictionsSearchHelper(
val restrictions: ContentRestrictions,
) {
fun toCondition(): Pair<Condition, Set<RequiredJoin>> {
val ageAllowed =
if (restrictions.ageRestriction?.restriction == AllowExclude.ALLOW_ONLY) {
Tables.SERIES_METADATA.AGE_RATING.isNotNull

View file

@ -17,7 +17,7 @@ private val logger = KotlinLogging.logger {}
*/
class SeriesSearchHelper(
val context: SearchContext,
) : ContentRestrictionsSearchHelper() {
) {
fun toCondition(searchCondition: SearchCondition.Series?): Pair<Condition, Set<RequiredJoin>> {
val base = toCondition()
val search = toConditionInternal(searchCondition)
@ -25,7 +25,7 @@ class SeriesSearchHelper(
}
fun toCondition(): Pair<Condition, Set<RequiredJoin>> {
val restrictions = toConditionInternal(context.restrictions)
val restrictions = ContentRestrictionsSearchHelper(context.restrictions).toCondition()
val authorizedLibraries = toConditionInternal(context.libraryIds)
return restrictions.first.and(authorizedLibraries.first) to (restrictions.second + authorizedLibraries.second)
}

View file

@ -10,6 +10,9 @@ import org.jooq.Condition
import org.jooq.Field
import org.jooq.SortField
import org.jooq.impl.DSL
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import java.io.ByteArrayOutputStream
import java.util.zip.GZIPInputStream
@ -136,3 +139,18 @@ fun ObjectMapper.deserializeMediaExtension(
fun rlbAlias(readListId: String) = Tables.READLIST_BOOK.`as`("RLB_$readListId")
fun csAlias(collectionId: String) = Tables.COLLECTION_SERIES.`as`("CS_$collectionId")
fun <T> buildPage(
items: List<T>,
pageable: Pageable,
count: Int,
sort: Sort?,
): PageImpl<T> =
PageImpl(
items,
if (pageable.isPaged)
PageRequest.of(pageable.pageNumber, pageable.pageSize, sort ?: pageable.sort)
else
PageRequest.of(0, maxOf(count, 20), sort ?: pageable.sort),
count.toLong(),
)

View file

@ -3,24 +3,35 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.Author
import org.gotson.komga.domain.model.FilterBy
import org.gotson.komga.domain.model.FilterByEntity
import org.gotson.komga.domain.model.FilterTags
import org.gotson.komga.domain.model.SearchContext
import org.gotson.komga.domain.persistence.ReferentialRepository
import org.gotson.komga.infrastructure.jooq.ContentRestrictionsSearchHelper
import org.gotson.komga.infrastructure.jooq.RequiredJoin
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.buildPage
import org.gotson.komga.infrastructure.jooq.udfStripAccents
import org.gotson.komga.infrastructure.jooq.unicode3
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.BookMetadataAggregationAuthorRecord
import org.gotson.komga.jooq.main.tables.records.BookMetadataAuthorRecord
import org.gotson.komga.language.stripAccents
import org.jooq.Condition
import org.jooq.DSLContext
import org.jooq.impl.DSL.noCondition
import org.jooq.OrderField
import org.jooq.SelectFieldOrAsterisk
import org.jooq.TableField
import org.jooq.impl.DSL
import org.jooq.impl.DSL.select
import org.jooq.impl.TableImpl
import org.jooq.impl.TableRecordImpl
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.data.domain.Sort.Order
import org.springframework.stereotype.Component
import java.time.LocalDate
@ -43,7 +54,9 @@ class ReferentialDao(
private val cs = Tables.COLLECTION_SERIES
private val rb = Tables.READLIST_BOOK
private val sl = Tables.SERIES_METADATA_SHARING
private val at = Tables.SERIES_AND_BOOK_TAG
@Deprecated("Use findAuthors instead")
override fun findAllAuthorsByName(
search: String,
filterOnLibraryIds: Collection<String>?,
@ -58,6 +71,7 @@ class ReferentialDao(
.fetchInto(a)
.map { it.toDomain() }
@Deprecated("Use findAuthors instead")
override fun findAllAuthorsByNameAndLibrary(
search: String,
libraryId: String,
@ -75,6 +89,7 @@ class ReferentialDao(
.fetchInto(bmaa)
.map { it.toDomain() }
@Deprecated("Use findAuthors instead")
override fun findAllAuthorsByNameAndCollection(
search: String,
collectionId: String,
@ -93,6 +108,7 @@ class ReferentialDao(
.fetchInto(bmaa)
.map { it.toDomain() }
@Deprecated("Use findAuthors instead")
override fun findAllAuthorsByNameAndSeries(
search: String,
seriesId: String,
@ -115,55 +131,23 @@ class ReferentialDao(
role: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<Author> {
val query =
dslRO
.selectDistinct(bmaa.NAME, bmaa.ROLE)
.from(bmaa)
.apply { if (!context.libraryIds.isNullOrEmpty() || filterBy?.type == FilterByEntity.LIBRARY) leftJoin(s).on(bmaa.SERIES_ID.eq(s.ID)) }
.apply { if (filterBy?.type == FilterByEntity.COLLECTION) leftJoin(cs).on(bmaa.SERIES_ID.eq(cs.SERIES_ID)) }
.apply {
if (filterBy?.type == FilterByEntity.READLIST)
leftJoin(b)
.on(bmaa.SERIES_ID.eq(b.SERIES_ID))
.leftJoin(rb)
.on(b.ID.eq(rb.BOOK_ID))
}.where(noCondition())
.apply { search?.let { and(bmaa.NAME.udfStripAccents().contains(search.stripAccents())) } }
.apply { role?.let { and(bmaa.ROLE.eq(role)) } }
.apply { context.libraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } }
.apply {
filterBy?.let {
when (it.type) {
FilterByEntity.LIBRARY -> and(s.LIBRARY_ID.`in`(it.ids))
FilterByEntity.COLLECTION -> and(cs.COLLECTION_ID.`in`(it.ids))
FilterByEntity.SERIES -> and(bmaa.SERIES_ID.`in`(it.ids))
FilterByEntity.READLIST -> and(rb.READLIST_ID.`in`(it.ids))
}
}
}
): Page<Author> = findGeneric(context, search, filterBy, pageable, bmaa, bmaa.NAME, bmaa.SERIES_ID, { it?.toDomain() }, Sort.by("name"), listOf(bmaa.ROLE), role?.let { bmaa.ROLE.eq(role) })
val count = dslRO.fetchCount(query)
val sort = bmaa.NAME.unicode3()
override fun findAuthorsRoles(
context: SearchContext,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String> = findGeneric(context, null, filterBy, pageable, bmaa, null, bmaa.SERIES_ID, { it?.role }, Sort.by("role"), listOf(bmaa.ROLE), sortField = bmaa.ROLE)
val items =
query
.orderBy(sort)
.apply { if (pageable.isPaged) limit(pageable.pageSize).offset(pageable.offset) }
.fetchInto(a)
.map { it.toDomain() }
val pageSort = pageable.sort
return PageImpl(
items,
if (pageable.isPaged)
PageRequest.of(pageable.pageNumber, pageable.pageSize, pageSort)
else
PageRequest.of(0, maxOf(count, 20), pageSort),
count.toLong(),
)
}
override fun findAuthorsNames(
context: SearchContext,
search: String?,
role: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String> = findGeneric(context, search, filterBy, pageable, bmaa, bmaa.NAME, bmaa.SERIES_ID, { it?.name }, Sort.by("name"), listOf(bmaa.ROLE), role?.let { bmaa.ROLE.eq(role) })
@Deprecated("Use findAuthorsNames instead")
override fun findAllAuthorsNamesByName(
search: String,
filterOnLibraryIds: Collection<String>?,
@ -177,6 +161,7 @@ class ReferentialDao(
.orderBy(a.NAME.unicode3())
.fetch(a.NAME)
@Deprecated("Use findAuthorsRoles instead")
override fun findAllAuthorsRoles(filterOnLibraryIds: Collection<String>?): List<String> =
dslRO
.selectDistinct(a.ROLE)
@ -190,6 +175,7 @@ class ReferentialDao(
}.orderBy(a.ROLE)
.fetch(a.ROLE)
@Deprecated("Use findGenres instead")
override fun findAllGenres(filterOnLibraryIds: Collection<String>?): Set<String> =
dslRO
.selectDistinct(g.GENRE)
@ -203,6 +189,7 @@ class ReferentialDao(
}.orderBy(g.GENRE.unicode3())
.fetchSet(g.GENRE)
@Deprecated("Use findGenres instead")
override fun findAllGenresByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
@ -217,6 +204,7 @@ class ReferentialDao(
.orderBy(g.GENRE.unicode3())
.fetchSet(g.GENRE)
@Deprecated("Use findGenres instead")
override fun findAllGenresByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
@ -232,6 +220,18 @@ class ReferentialDao(
.orderBy(g.GENRE.unicode3())
.fetchSet(g.GENRE)
override fun findGenres(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String> {
filterBy?.let { require(it.type in setOf(FilterByEntity.LIBRARY, FilterByEntity.COLLECTION)) }
return findGeneric(context, search, filterBy, pageable, g, g.GENRE, g.SERIES_ID, { it?.genre }, Sort.by("genre"))
}
@Deprecated("Use findTags instead")
override fun findAllSeriesAndBookTags(filterOnLibraryIds: Collection<String>?): Set<String> =
dslRO
.select(bt.TAG.`as`("tag"))
@ -245,6 +245,7 @@ class ReferentialDao(
.sortedBy { it.stripAccents().lowercase() }
.toSet()
@Deprecated("Use findTags instead")
override fun findAllSeriesAndBookTagsByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
@ -267,6 +268,7 @@ class ReferentialDao(
.sortedBy { it.stripAccents().lowercase() }
.toSet()
@Deprecated("Use findTags instead")
override fun findAllSeriesAndBookTagsByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
@ -293,6 +295,7 @@ class ReferentialDao(
.sortedBy { it.stripAccents().lowercase() }
.toSet()
@Deprecated("Use findTags instead")
override fun findAllSeriesTags(filterOnLibraryIds: Collection<String>?): Set<String> =
dslRO
.select(st.TAG)
@ -306,6 +309,7 @@ class ReferentialDao(
}.orderBy(st.TAG.unicode3())
.fetchSet(st.TAG)
@Deprecated("Use findTags instead")
override fun findAllSeriesTagsByLibrary(
libraryId: String,
filterOnLibraryIds: Collection<String>?,
@ -320,6 +324,7 @@ class ReferentialDao(
.orderBy(st.TAG.unicode3())
.fetchSet(st.TAG)
@Deprecated("Use findTags instead")
override fun findAllBookTagsBySeries(
seriesId: String,
filterOnLibraryIds: Collection<String>?,
@ -334,6 +339,7 @@ class ReferentialDao(
.orderBy(bt.TAG.unicode3())
.fetchSet(bt.TAG)
@Deprecated("Use findTags instead")
override fun findAllBookTagsByReadList(
readListId: String,
filterOnLibraryIds: Collection<String>?,
@ -350,6 +356,20 @@ class ReferentialDao(
.orderBy(bt.TAG.unicode3())
.fetchSet(bt.TAG)
override fun findTags(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
filterTags: FilterTags,
pageable: Pageable,
): Page<String> =
when (filterTags) {
FilterTags.SERIES -> findGeneric(context, search, filterBy, pageable, st, st.TAG, st.SERIES_ID, { it?.tag }, Sort.by("tag"))
FilterTags.BOOK -> findGeneric(context, search, filterBy, pageable, bmat, bmat.TAG, bmat.SERIES_ID, { it?.tag }, Sort.by("tag"))
FilterTags.BOTH -> findGeneric(context, search, filterBy, pageable, at, at.TAG, at.SERIES_ID, { it?.tag }, Sort.by("tag"))
}
@Deprecated("Use findTags instead")
override fun findAllSeriesTagsByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
@ -365,6 +385,7 @@ class ReferentialDao(
.orderBy(st.TAG.unicode3())
.fetchSet(st.TAG)
@Deprecated("Use findTags instead")
override fun findAllBookTags(filterOnLibraryIds: Collection<String>?): Set<String> =
dslRO
.select(bt.TAG)
@ -378,6 +399,7 @@ class ReferentialDao(
}.orderBy(bt.TAG.unicode3())
.fetchSet(bt.TAG)
@Deprecated("Use findLanguages instead")
override fun findAllLanguages(filterOnLibraryIds: Collection<String>?): Set<String> =
dslRO
.selectDistinct(sd.LANGUAGE)
@ -388,6 +410,7 @@ class ReferentialDao(
.orderBy(sd.LANGUAGE)
.fetchSet(sd.LANGUAGE)
@Deprecated("Use findLanguages instead")
override fun findAllLanguagesByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
@ -403,6 +426,7 @@ class ReferentialDao(
.orderBy(sd.LANGUAGE)
.fetchSet(sd.LANGUAGE)
@Deprecated("Use findLanguages instead")
override fun findAllLanguagesByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
@ -419,6 +443,18 @@ class ReferentialDao(
.orderBy(sd.LANGUAGE)
.fetchSet(sd.LANGUAGE)
override fun findLanguages(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String> {
filterBy?.let { require(it.type in setOf(FilterByEntity.LIBRARY, FilterByEntity.COLLECTION)) }
return findGeneric(context, search, filterBy, pageable, sd, sd.LANGUAGE, sd.SERIES_ID, { it?.language }, Sort.by("language"), extraCondition = sd.LANGUAGE.ne(""))
}
@Deprecated("Use findPublishers instead")
override fun findAllPublishers(filterOnLibraryIds: Collection<String>?): Set<String> =
dslRO
.selectDistinct(sd.PUBLISHER)
@ -429,6 +465,7 @@ class ReferentialDao(
.orderBy(sd.PUBLISHER.unicode3())
.fetchSet(sd.PUBLISHER)
@Deprecated("Use findPublishers instead")
override fun findAllPublishers(
filterOnLibraryIds: Collection<String>?,
pageable: Pageable,
@ -461,6 +498,7 @@ class ReferentialDao(
)
}
@Deprecated("Use findPublishers instead")
override fun findAllPublishersByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
@ -476,6 +514,7 @@ class ReferentialDao(
.orderBy(sd.PUBLISHER.unicode3())
.fetchSet(sd.PUBLISHER)
@Deprecated("Use findPublishers instead")
override fun findAllPublishersByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
@ -492,6 +531,18 @@ class ReferentialDao(
.orderBy(sd.PUBLISHER.unicode3())
.fetchSet(sd.PUBLISHER)
override fun findPublishers(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String> {
filterBy?.let { require(it.type in setOf(FilterByEntity.LIBRARY, FilterByEntity.COLLECTION)) }
return findGeneric(context, search, filterBy, pageable, sd, sd.PUBLISHER, sd.SERIES_ID, { it?.publisher }, Sort.by("publisher"), extraCondition = sd.PUBLISHER.ne(""))
}
@Deprecated("Use findAgeRatings instead")
override fun findAllAgeRatings(filterOnLibraryIds: Collection<String>?): Set<Int?> =
dslRO
.selectDistinct(sd.AGE_RATING)
@ -505,6 +556,7 @@ class ReferentialDao(
}.orderBy(sd.AGE_RATING)
.fetchSet(sd.AGE_RATING)
@Deprecated("Use findAgeRatings instead")
override fun findAllAgeRatingsByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
@ -519,6 +571,7 @@ class ReferentialDao(
.orderBy(sd.AGE_RATING)
.fetchSet(sd.AGE_RATING)
@Deprecated("Use findAgeRatings instead")
override fun findAllAgeRatingsByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
@ -534,6 +587,17 @@ class ReferentialDao(
.orderBy(sd.AGE_RATING)
.fetchSet(sd.AGE_RATING)
override fun findAgeRatings(
context: SearchContext,
filterBy: FilterBy?,
pageable: Pageable,
): Page<Int> {
filterBy?.let { require(it.type in setOf(FilterByEntity.LIBRARY, FilterByEntity.COLLECTION)) }
return findGeneric(context, null, filterBy, pageable, sd, null, sd.SERIES_ID, { it?.ageRating }, Sort.by("ageRating"), listOf(sd.AGE_RATING), sortField = sd.AGE_RATING)
}
@Deprecated("Use findSeriesReleaseDates instead")
override fun findAllSeriesReleaseDates(filterOnLibraryIds: Collection<String>?): Set<LocalDate> =
dslRO
.selectDistinct(bma.RELEASE_DATE)
@ -544,6 +608,7 @@ class ReferentialDao(
.orderBy(bma.RELEASE_DATE.desc())
.fetchSet(bma.RELEASE_DATE)
@Deprecated("Use findSeriesReleaseDates instead")
override fun findAllSeriesReleaseDatesByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
@ -559,6 +624,7 @@ class ReferentialDao(
.orderBy(bma.RELEASE_DATE.desc())
.fetchSet(bma.RELEASE_DATE)
@Deprecated("Use findSeriesReleaseDates instead")
override fun findAllSeriesReleaseDatesByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
@ -575,6 +641,17 @@ class ReferentialDao(
.orderBy(bma.RELEASE_DATE.desc())
.fetchSet(bma.RELEASE_DATE)
override fun findSeriesReleaseDates(
context: SearchContext,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String> {
filterBy?.let { require(it.type in setOf(FilterByEntity.LIBRARY, FilterByEntity.COLLECTION)) }
return findGeneric(context, null, filterBy, pageable, bma, null, bma.SERIES_ID, { it?.releaseDate?.year?.toString() }, Sort.by(Order.desc("year")), listOf(bma.RELEASE_DATE), sortField = bma.RELEASE_DATE.desc())
}
@Deprecated("Use findSharingLabels instead")
override fun findAllSharingLabels(filterOnLibraryIds: Collection<String>?): Set<String> =
dslRO
.selectDistinct(sl.LABEL)
@ -588,6 +665,7 @@ class ReferentialDao(
}.orderBy(sl.LABEL.unicode3())
.fetchSet(sl.LABEL)
@Deprecated("Use findSharingLabels instead")
override fun findAllSharingLabelsByLibraries(
libraryIds: Set<String>,
filterOnLibraryIds: Collection<String>?,
@ -602,6 +680,7 @@ class ReferentialDao(
.orderBy(sl.LABEL.unicode3())
.fetchSet(sl.LABEL)
@Deprecated("Use findSharingLabels instead")
override fun findAllSharingLabelsByCollection(
collectionId: String,
filterOnLibraryIds: Collection<String>?,
@ -617,6 +696,89 @@ class ReferentialDao(
.orderBy(sl.LABEL.unicode3())
.fetchSet(sl.LABEL)
override fun findSharingLabels(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
pageable: Pageable,
): Page<String> {
filterBy?.let { require(it.type in setOf(FilterByEntity.LIBRARY, FilterByEntity.COLLECTION)) }
return findGeneric(context, search, filterBy, pageable, sl, sl.LABEL, sl.SERIES_ID, { it?.label }, Sort.by("label"))
}
private fun <R : TableRecordImpl<*>, T : TableImpl<R>, O : Any> findGeneric(
context: SearchContext,
search: String?,
filterBy: FilterBy?,
pageable: Pageable,
table: T,
searchableField: TableField<R, String>?,
seriesIdField: TableField<*, String>,
mapper: (R?) -> O?,
sort: Sort,
extraFields: List<SelectFieldOrAsterisk> = emptyList(),
extraCondition: Condition? = DSL.noCondition(),
sortField: OrderField<*>? = null,
): Page<O> {
val restrictionCondition = ContentRestrictionsSearchHelper(context.restrictions).toCondition()
val query =
dslRO
.selectDistinct(*(listOfNotNull(searchableField) + extraFields).toTypedArray())
.from(table)
.apply {
restrictionCondition.second.forEach { join ->
when (join) {
RequiredJoin.SeriesMetadata -> if (table != sd) innerJoin(sd).on(seriesIdField.eq(sd.SERIES_ID))
// shouldn't be required
RequiredJoin.BookMetadata -> Unit
RequiredJoin.BookMetadataAggregation -> Unit
is RequiredJoin.Collection -> Unit
RequiredJoin.Media -> Unit
is RequiredJoin.ReadList -> Unit
is RequiredJoin.ReadProgress -> Unit
}
}
}.apply { if (!context.libraryIds.isNullOrEmpty() || filterBy?.type == FilterByEntity.LIBRARY) leftJoin(s).on(seriesIdField.eq(s.ID)) }
.apply { if (filterBy?.type == FilterByEntity.COLLECTION) leftJoin(cs).on(seriesIdField.eq(cs.SERIES_ID)) }
.apply {
if (filterBy?.type == FilterByEntity.READLIST)
leftJoin(b)
.on(seriesIdField.eq(b.SERIES_ID))
.leftJoin(rb)
.on(b.ID.eq(rb.BOOK_ID))
}.where(restrictionCondition.first)
.apply { extraCondition?.let { and(it) } }
.apply { if (search != null && searchableField != null) and(searchableField.udfStripAccents().contains(search.stripAccents())) }
.apply { context.libraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } }
.apply {
filterBy?.let {
when (it.type) {
FilterByEntity.LIBRARY -> and(s.LIBRARY_ID.`in`(it.ids))
FilterByEntity.COLLECTION -> and(cs.COLLECTION_ID.`in`(it.ids))
FilterByEntity.SERIES -> and(seriesIdField.`in`(it.ids))
FilterByEntity.READLIST -> and(rb.READLIST_ID.`in`(it.ids))
}
}
}
val count = dslRO.fetchCount(query)
val items =
query
.apply {
if (sortField != null)
orderBy(sortField)
else if (searchableField != null)
orderBy(searchableField.unicode3())
}.apply { if (pageable.isPaged) limit(pageable.pageSize).offset(pageable.offset) }
.fetchInto(table)
.mapNotNull { mapper(it) }
return buildPage(items, pageable, count, sort)
}
private fun BookMetadataAuthorRecord.toDomain(): Author =
Author(
name = name,

View file

@ -21,7 +21,7 @@ class ReferentialV1Controller(
private val referentialRepository: ReferentialRepository,
) {
@GetMapping("authors")
@Deprecated("Use GET /v2/authors instead", ReplaceWith("getAuthors"))
@Deprecated("Use GET /v2/authors instead")
@Operation(summary = "List authors", description = "Use GET /api/v2/authors instead. Deprecated since 1.20.0.", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getAuthorsDeprecated(
@AuthenticationPrincipal principal: KomgaPrincipal,
@ -39,20 +39,26 @@ class ReferentialV1Controller(
}.map { it.toDto() }
@GetMapping("authors/names")
@Operation(summary = "List authors' names")
@Deprecated("Use GET /v2/authors/names instead")
// TODO: add deprecation release
@Operation(summary = "List authors' names", description = "Use GET /v2/authors/names instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getAuthorsNames(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "search", defaultValue = "") search: String,
): List<String> = referentialRepository.findAllAuthorsNamesByName(search, principal.user.getAuthorizedLibraryIds(null))
@GetMapping("authors/roles")
@Operation(summary = "List authors' roles")
@Deprecated("Use GET /v2/authors/roles instead")
// TODO: add deprecation release
@Operation(summary = "List authors' roles", description = "Use GET /v2/authors/roles instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getAuthorsRoles(
@AuthenticationPrincipal principal: KomgaPrincipal,
): List<String> = referentialRepository.findAllAuthorsRoles(principal.user.getAuthorizedLibraryIds(null))
@GetMapping("genres")
@Operation(summary = "List genres", description = "Can be filtered by various criteria")
@Deprecated("Use GET /v2/genres instead")
// TODO: add deprecation release
@Operation(summary = "List genres", description = "Use GET /v2/genres instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getGenres(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@ -65,7 +71,9 @@ class ReferentialV1Controller(
}
@GetMapping("sharing-labels")
@Operation(summary = "List sharing labels", description = "Can be filtered by various criteria")
@Deprecated("Use GET /v2/sharing-labels instead")
// TODO: add deprecation release
@Operation(summary = "List sharing labels", description = "Use GET /v2/sharing-labels instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getSharingLabels(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@ -78,7 +86,9 @@ class ReferentialV1Controller(
}
@GetMapping("tags")
@Operation(summary = "List tags", description = "Can be filtered by various criteria")
@Deprecated("Use GET /v2/tags instead")
// TODO: add deprecation release
@Operation(summary = "List tags", description = "Use GET /v2/sharing-labels instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getTags(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@ -91,7 +101,9 @@ class ReferentialV1Controller(
}
@GetMapping("tags/book")
@Operation(summary = "List book tags", description = "Can be filtered by various criteria")
@Deprecated("Use GET /v2/tags instead")
// TODO: add deprecation release
@Operation(summary = "List book tags", description = "Use GET /v2/sharing-labels instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getBookTags(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "series_id", required = false) seriesId: String?,
@ -106,7 +118,9 @@ class ReferentialV1Controller(
}
@GetMapping("tags/series")
@Operation(summary = "List series tags", description = "Can be filtered by various criteria")
@Deprecated("Use GET /v2/tags instead")
// TODO: add deprecation release
@Operation(summary = "List series tags", description = "Use GET /v2/sharing-labels instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getSeriesTags(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryId: String?,
@ -119,7 +133,9 @@ class ReferentialV1Controller(
}
@GetMapping("languages")
@Operation(summary = "List languages", description = "Can be filtered by various criteria")
@Deprecated("Use GET /v2/languages instead")
// TODO: add deprecation release
@Operation(summary = "List languages", description = "Use GET /v2/languages instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getLanguages(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@ -132,7 +148,9 @@ class ReferentialV1Controller(
}
@GetMapping("publishers")
@Operation(summary = "List publishers", description = "Can be filtered by various criteria")
@Deprecated("Use GET /v2/publishers instead")
// TODO: add deprecation release
@Operation(summary = "List publishers", description = "Use GET /v2/genres instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getPublishers(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@ -145,7 +163,9 @@ class ReferentialV1Controller(
}
@GetMapping("age-ratings")
@Operation(summary = "List age ratings", description = "Can be filtered by various criteria")
@Deprecated("Use GET /v2/age-ratings instead")
// TODO: add deprecation release
@Operation(summary = "List age ratings", description = "Use GET /v2/genres instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getAgeRatings(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@ -158,7 +178,9 @@ class ReferentialV1Controller(
}.map { it?.toString() ?: "None" }.toSet()
@GetMapping("series/release-dates")
@Operation(summary = "List series release dates", description = "Can be filtered by various criteria")
@Deprecated("Use GET /v2/age-ratings instead")
// TODO: add deprecation release
@Operation(summary = "List series release dates", description = "Use GET /v2/genres instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED])
fun getSeriesReleaseDates(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),

View file

@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.tags.Tag
import org.gotson.komga.domain.model.FilterBy
import org.gotson.komga.domain.model.FilterByEntity
import org.gotson.komga.domain.model.FilterTags
import org.gotson.komga.domain.model.SearchContext
import org.gotson.komga.domain.persistence.ReferentialRepository
import org.gotson.komga.infrastructure.openapi.OpenApiConfiguration
@ -36,9 +37,9 @@ class ReferentialV2Controller(
@RequestParam(name = "search", required = false) search: String?,
@RequestParam(name = "role", required = false) role: String?,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionId: String?,
@RequestParam(name = "series_id", required = false) seriesId: String?,
@RequestParam(name = "readlist_id", required = false) readListId: String?,
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "series_id", required = false) seriesIds: Set<String> = emptySet(),
@RequestParam(name = "readlist_id", required = false) readListIds: Set<String> = emptySet(),
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<AuthorDto> {
@ -51,12 +52,302 @@ class ReferentialV2Controller(
page.pageSize,
)
return when {
libraryIds.isNotEmpty() -> referentialRepository.findAuthors(SearchContext(principal.user), search, role, FilterBy(FilterByEntity.LIBRARY, libraryIds), pageRequest)
collectionId != null -> referentialRepository.findAuthors(SearchContext(principal.user), search, role, FilterBy(FilterByEntity.COLLECTION, setOf(collectionId)), pageRequest)
seriesId != null -> referentialRepository.findAuthors(SearchContext(principal.user), search, role, FilterBy(FilterByEntity.SERIES, setOf(seriesId)), pageRequest)
readListId != null -> referentialRepository.findAuthors(SearchContext(principal.user), search, role, FilterBy(FilterByEntity.READLIST, setOf(readListId)), pageRequest)
else -> referentialRepository.findAuthors(SearchContext(principal.user), search, role, null, pageRequest)
}.map { it.toDto() }
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
seriesIds.isNotEmpty() -> FilterBy(FilterByEntity.SERIES, seriesIds)
readListIds.isNotEmpty() -> FilterBy(FilterByEntity.READLIST, readListIds)
else -> null
}
return referentialRepository.findAuthors(SearchContext(principal.user), search, role, filterBy, pageRequest).map { it.toDto() }
}
@PageableWithoutSortAsQueryParam
@GetMapping("authors/roles")
@Operation(summary = "List authors roles", description = "Can be filtered by various criteria")
fun getAuthorsRoles(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "series_id", required = false) seriesIds: Set<String> = emptySet(),
@RequestParam(name = "readlist_id", required = false) readListIds: Set<String> = emptySet(),
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<String> {
val pageRequest =
if (unpaged)
Pageable.unpaged()
else
PageRequest.of(
page.pageNumber,
page.pageSize,
)
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
seriesIds.isNotEmpty() -> FilterBy(FilterByEntity.SERIES, seriesIds)
readListIds.isNotEmpty() -> FilterBy(FilterByEntity.READLIST, readListIds)
else -> null
}
return referentialRepository.findAuthorsRoles(SearchContext(principal.user), filterBy, pageRequest)
}
@PageableWithoutSortAsQueryParam
@GetMapping("authors/names")
@Operation(summary = "List authors names", description = "Can be filtered by various criteria")
fun getAuthorsNames(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "search", required = false) search: String?,
@RequestParam(name = "role", required = false) role: String?,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "series_id", required = false) seriesIds: Set<String> = emptySet(),
@RequestParam(name = "readlist_id", required = false) readListIds: Set<String> = emptySet(),
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<String> {
val pageRequest =
if (unpaged)
Pageable.unpaged()
else
PageRequest.of(
page.pageNumber,
page.pageSize,
)
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
seriesIds.isNotEmpty() -> FilterBy(FilterByEntity.SERIES, seriesIds)
readListIds.isNotEmpty() -> FilterBy(FilterByEntity.READLIST, readListIds)
else -> null
}
return referentialRepository.findAuthorsNames(SearchContext(principal.user), search, role, filterBy, pageRequest)
}
@PageableWithoutSortAsQueryParam
@GetMapping("genres")
@Operation(summary = "List genres", description = "Can be filtered by various criteria")
fun getGenres(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "search", required = false) search: String?,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<String> {
val pageRequest =
if (unpaged)
Pageable.unpaged()
else
PageRequest.of(
page.pageNumber,
page.pageSize,
)
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
else -> null
}
return referentialRepository.findGenres(SearchContext(principal.user), search, filterBy, pageRequest)
}
@PageableWithoutSortAsQueryParam
@GetMapping("sharing-labels")
@Operation(summary = "List sharing labels", description = "Can be filtered by various criteria")
fun getSharingLabels(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "search", required = false) search: String?,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<String> {
val pageRequest =
if (unpaged)
Pageable.unpaged()
else
PageRequest.of(
page.pageNumber,
page.pageSize,
)
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
else -> null
}
return referentialRepository.findSharingLabels(SearchContext(principal.user), search, filterBy, pageRequest)
}
@PageableWithoutSortAsQueryParam
@GetMapping("languages")
@Operation(summary = "List languages", description = "Can be filtered by various criteria")
fun getLanguages(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "search", required = false) search: String?,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<String> {
val pageRequest =
if (unpaged)
Pageable.unpaged()
else
PageRequest.of(
page.pageNumber,
page.pageSize,
)
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
else -> null
}
return referentialRepository.findLanguages(SearchContext(principal.user), search, filterBy, pageRequest)
}
@PageableWithoutSortAsQueryParam
@GetMapping("publishers")
@Operation(summary = "List publishers", description = "Can be filtered by various criteria")
fun getPublishers(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "search", required = false) search: String?,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<String> {
val pageRequest =
if (unpaged)
Pageable.unpaged()
else
PageRequest.of(
page.pageNumber,
page.pageSize,
)
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
else -> null
}
return referentialRepository.findPublishers(SearchContext(principal.user), search, filterBy, pageRequest)
}
@PageableWithoutSortAsQueryParam
@GetMapping("tags")
@Operation(summary = "List tags", description = "Can be filtered by various criteria")
fun getTags(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "search", required = false) search: String?,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "series_id", required = false) seriesIds: Set<String> = emptySet(),
@RequestParam(name = "readlist_id", required = false) readListIds: Set<String> = emptySet(),
@RequestParam(name = "include", required = false) includeTags: FilterTags = FilterTags.BOTH,
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<String> {
val pageRequest =
if (unpaged)
Pageable.unpaged()
else
PageRequest.of(
page.pageNumber,
page.pageSize,
)
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
seriesIds.isNotEmpty() -> FilterBy(FilterByEntity.SERIES, seriesIds)
readListIds.isNotEmpty() -> FilterBy(FilterByEntity.READLIST, readListIds)
else -> null
}
return referentialRepository.findTags(SearchContext(principal.user), search, filterBy, includeTags, pageRequest)
}
@PageableWithoutSortAsQueryParam
@GetMapping("series/release-dates")
@Operation(summary = "List series release dates", description = "Can be filtered by various criteria")
fun getSeriesReleaseDates(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<String> {
val pageRequest =
if (unpaged)
Pageable.unpaged()
else
PageRequest.of(
page.pageNumber,
page.pageSize,
)
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
else -> null
}
return referentialRepository.findSeriesReleaseDates(SearchContext(principal.user), filterBy, pageRequest)
}
@PageableWithoutSortAsQueryParam
@GetMapping("age-ratings")
@Operation(summary = "List age ratings", description = "Can be filtered by various criteria")
fun getAgeRatings(
@AuthenticationPrincipal principal: KomgaPrincipal,
@RequestParam(name = "library_id", required = false) libraryIds: Set<String> = emptySet(),
@RequestParam(name = "collection_id", required = false) collectionIds: Set<String> = emptySet(),
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
@Parameter(hidden = true) page: Pageable,
): Page<Int> {
val pageRequest =
if (unpaged)
Pageable.unpaged()
else
PageRequest.of(
page.pageNumber,
page.pageSize,
)
val filterBy =
when {
libraryIds.isNotEmpty() -> FilterBy(FilterByEntity.LIBRARY, libraryIds)
collectionIds.isNotEmpty() -> FilterBy(FilterByEntity.COLLECTION, collectionIds)
else -> null
}
return referentialRepository.findAgeRatings(SearchContext(principal.user), filterBy, pageRequest)
}
}

View file

@ -0,0 +1,773 @@
package org.gotson.komga.infrastructure.jooq.main
import com.ninjasquad.springmockk.MockkBean
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import org.assertj.core.api.Assertions.assertThat
import org.gotson.komga.domain.model.AgeRestriction
import org.gotson.komga.domain.model.AllowExclude
import org.gotson.komga.domain.model.Author
import org.gotson.komga.domain.model.ContentRestrictions
import org.gotson.komga.domain.model.FilterBy
import org.gotson.komga.domain.model.FilterByEntity
import org.gotson.komga.domain.model.FilterTags
import org.gotson.komga.domain.model.KomgaUser
import org.gotson.komga.domain.model.SearchContext
import org.gotson.komga.domain.model.makeBook
import org.gotson.komga.domain.model.makeLibrary
import org.gotson.komga.domain.model.makeSeries
import org.gotson.komga.domain.persistence.BookMetadataRepository
import org.gotson.komga.domain.persistence.BookRepository
import org.gotson.komga.domain.persistence.KomgaUserRepository
import org.gotson.komga.domain.persistence.LibraryRepository
import org.gotson.komga.domain.persistence.SeriesMetadataRepository
import org.gotson.komga.domain.persistence.SeriesRepository
import org.gotson.komga.domain.service.BookLifecycle
import org.gotson.komga.domain.service.KomgaUserLifecycle
import org.gotson.komga.domain.service.LibraryLifecycle
import org.gotson.komga.domain.service.SeriesLifecycle
import org.gotson.komga.domain.service.SeriesMetadataLifecycle
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.domain.Pageable
import java.time.LocalDate
@SpringBootTest
class ReferentialDaoTest(
@Autowired private val referentialDao: ReferentialDao,
@Autowired private val userRepository: KomgaUserRepository,
@Autowired private val bookRepository: BookRepository,
@Autowired private val seriesRepository: SeriesRepository,
@Autowired private val seriesMetadataLifecycle: SeriesMetadataLifecycle,
@Autowired private val bookMetadataRepository: BookMetadataRepository,
@Autowired private val bookLifecycle: BookLifecycle,
@Autowired private val userLifecycle: KomgaUserLifecycle,
@Autowired private val seriesLifecycle: SeriesLifecycle,
@Autowired private val libraryRepository: LibraryRepository,
@Autowired private val libraryLifecycle: LibraryLifecycle,
@Autowired private val seriesMetadataRepository: SeriesMetadataRepository,
) {
private val library1 = makeLibrary()
private val library2 = makeLibrary()
private val series1 = makeSeries("Series 1").copy(libraryId = library1.id)
private val series2 = makeSeries("Series 2").copy(libraryId = library2.id)
private val seriesEmpty = makeSeries("Series Empty").copy(libraryId = library2.id)
private val seriesShared = makeSeries("Series Shared").copy(libraryId = library1.id)
private val seriesAge10 = makeSeries("Series Age 10").copy(libraryId = library1.id)
private val userAll = KomgaUser("user1@example.org", "p")
private val userLib1 = KomgaUser("user2@example.org", "p", sharedLibrariesIds = setOf(library1.id), sharedAllLibraries = false)
private val userLabelAllow = KomgaUser("user3@example.org", "p", restrictions = ContentRestrictions(labelsAllow = setOf("item_shared")))
private val userAge10 = KomgaUser("user4@example.org", "p", restrictions = ContentRestrictions(ageRestriction = AgeRestriction(10, AllowExclude.ALLOW_ONLY)))
@MockkBean
private lateinit var mockEventPublisher: ApplicationEventPublisher
@BeforeAll
fun setup() {
every { mockEventPublisher.publishEvent(any()) } just Runs
libraryRepository.insert(library1)
libraryRepository.insert(library2)
seriesLifecycle.createSeries(series1)
seriesLifecycle.createSeries(series2)
seriesLifecycle.createSeries(seriesEmpty)
seriesLifecycle.createSeries(seriesShared)
seriesLifecycle.createSeries(seriesAge10)
userRepository.insert(userAll)
userRepository.insert(userLib1)
userRepository.insert(userLabelAllow)
userRepository.insert(userAge10)
// setup restrictions context
seriesMetadataRepository.findById(seriesShared.id).let {
seriesMetadataRepository.update(it.copy(sharingLabels = setOf("item_shared")))
}
seriesMetadataRepository.findById(seriesAge10.id).let {
seriesMetadataRepository.update(it.copy(ageRating = 10))
}
// prepare metadata
makeBook("1", libraryId = library1.id, seriesId = series1.id).let { book ->
seriesLifecycle.addBooks(series1, listOf(book))
bookMetadataRepository.findById(book.id).let {
bookMetadataRepository.update(it.copy(authors = listOf(Author("item1", "writer")), releaseDate = LocalDate.of(2001, 1, 1), tags = setOf("bt1")))
}
seriesMetadataRepository.findById(series1.id).let {
seriesMetadataRepository.update(it.copy(genres = setOf("item1"), sharingLabels = setOf("item1"), language = "fr", publisher = "item1", ageRating = 18, tags = setOf("st1")))
}
}
seriesMetadataLifecycle.aggregateMetadata(series1)
makeBook("2", libraryId = library2.id, seriesId = series2.id).let { book ->
seriesLifecycle.addBooks(series2, listOf(book))
bookMetadataRepository.findById(book.id).let {
bookMetadataRepository.update(it.copy(authors = listOf(Author("item2", "inker")), releaseDate = LocalDate.of(2002, 1, 1), tags = setOf("bt2")))
}
seriesMetadataRepository.findById(series2.id).let {
seriesMetadataRepository.update(it.copy(genres = setOf("item2"), sharingLabels = setOf("item2"), language = "en", publisher = "item2", ageRating = 19, tags = setOf("st2")))
}
}
seriesMetadataLifecycle.aggregateMetadata(series2)
makeBook("Empty", libraryId = library2.id, seriesId = seriesEmpty.id).let { book ->
seriesLifecycle.addBooks(seriesEmpty, listOf(book))
}
seriesMetadataLifecycle.aggregateMetadata(seriesEmpty)
makeBook("shared", libraryId = library1.id, seriesId = seriesShared.id).let { book ->
seriesLifecycle.addBooks(seriesShared, listOf(book))
bookMetadataRepository.findById(book.id).let {
bookMetadataRepository.update(it.copy(authors = listOf(Author("item_shared", "penciller")), releaseDate = LocalDate.of(2003, 1, 1), tags = setOf("bt_shared")))
}
seriesMetadataRepository.findById(seriesShared.id).let {
seriesMetadataRepository.update(it.copy(genres = setOf("item_shared"), language = "ja", publisher = "item_shared", tags = setOf("st_shared")))
}
}
seriesMetadataLifecycle.aggregateMetadata(seriesShared)
makeBook("10", libraryId = library1.id, seriesId = seriesAge10.id).let { book ->
seriesLifecycle.addBooks(seriesAge10, listOf(book))
bookMetadataRepository.findById(book.id).let {
bookMetadataRepository.update(it.copy(authors = listOf(Author("item_10", "cover")), releaseDate = LocalDate.of(2004, 1, 1), tags = setOf("bt_10")))
}
seriesMetadataRepository.findById(seriesAge10.id).let {
seriesMetadataRepository.update(it.copy(genres = setOf("item_10"), sharingLabels = setOf("item_10"), language = "sp", publisher = "item_10", tags = setOf("st_10")))
}
}
seriesMetadataLifecycle.aggregateMetadata(seriesAge10)
}
@BeforeEach
fun resetMocks() {
every { mockEventPublisher.publishEvent(any()) } just Runs
}
@AfterEach
fun deleteBooks() {
bookLifecycle.deleteMany(bookRepository.findAll())
assertThat(bookRepository.count()).isEqualTo(0)
}
@AfterAll
fun tearDown() {
every { mockEventPublisher.publishEvent(any()) } just Runs
userRepository.findAll().forEach {
userLifecycle.deleteUser(it)
}
libraryRepository.findAll().forEach {
libraryLifecycle.deleteLibrary(it)
}
}
@Nested
inner class Author {
@Test
fun `given search when getting authors then matching authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthors(context, "shared", null, null, Pageable.unpaged()).content
assertThat(items.map { it.name }).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given role when getting authors then matching authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthors(context, null, "writer", null, Pageable.unpaged()).content
assertThat(items.map { it.name }).containsExactlyInAnyOrder("item1")
}
@Test
fun `given filter by library when getting authors then only matching authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthors(context, null, null, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content
assertThat(items.map { it.name }).containsExactlyInAnyOrder("item2")
}
@Test
fun `given user without restrictions when getting authors then all authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthors(context, null, null, null, Pageable.unpaged()).content
assertThat(items.map { it.name }).containsExactlyInAnyOrder("item1", "item2", "item_shared", "item_10")
}
@Test
fun `given user with restricted library access when getting authors then only allowed authors are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findAuthors(context, null, null, null, Pageable.unpaged()).content
assertThat(items.map { it.name }).containsExactlyInAnyOrder("item1", "item_shared", "item_10")
}
@Test
fun `given user with restricted label access when getting authors then only allowed authors are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findAuthors(context, null, null, null, Pageable.unpaged()).content
assertThat(items.map { it.name }).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given user with restricted age access when getting authors then only allowed authors are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findAuthors(context, null, null, null, Pageable.unpaged()).content
assertThat(items.map { it.name }).containsExactlyInAnyOrder("item_10")
}
}
@Nested
inner class AuthorName {
@Test
fun `given search when getting authors then matching authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthorsNames(context, "shared", null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given role when getting authors then matching authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthorsNames(context, null, "writer", null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item1")
}
@Test
fun `given filter by library when getting authors then only matching authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthorsNames(context, null, null, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item2")
}
@Test
fun `given user without restrictions when getting authors then all authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthorsNames(context, null, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item1", "item2", "item_shared", "item_10")
}
@Test
fun `given user with restricted library access when getting authors then only allowed authors are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findAuthorsNames(context, null, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item1", "item_shared", "item_10")
}
@Test
fun `given user with restricted label access when getting authors then only allowed authors are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findAuthorsNames(context, null, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given user with restricted age access when getting authors then only allowed authors are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findAuthorsNames(context, null, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_10")
}
}
@Nested
inner class AuthorRole {
@Test
fun `given filter by library when getting authors then only matching authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthorsRoles(context, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("inker")
}
@Test
fun `given user without restrictions when getting authors then all authors are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAuthorsRoles(context, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("writer", "inker", "penciller", "cover")
}
@Test
fun `given user with restricted library access when getting authors then only allowed authors are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findAuthorsRoles(context, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("writer", "penciller", "cover")
}
@Test
fun `given user with restricted label access when getting authors then only allowed authors are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findAuthorsRoles(context, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("penciller")
}
@Test
fun `given user with restricted age access when getting authors then only allowed authors are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findAuthorsRoles(context, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("cover")
}
}
@Nested
inner class Genre {
@Test
fun `given search when getting genres then all genres are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findGenres(context, "shared", null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given filter by library when getting genres then only matching genres are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findGenres(context, null, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item2")
}
@Test
fun `given user without restrictions when getting genres then all genres are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findGenres(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item1", "item2", "item_shared", "item_10")
}
@Test
fun `given user with restricted library access when getting genres then only allowed genres are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findGenres(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item1", "item_shared", "item_10")
}
@Test
fun `given user with restricted label access when getting genres then only allowed genres are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findGenres(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given user with restricted age access when getting genres then only allowed genres are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findGenres(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_10")
}
}
@Nested
inner class BookTag {
@Test
fun `given search when getting tags then all tags are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findTags(context, "shared", null, FilterTags.BOOK, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("bt_shared")
}
@Test
fun `given filter by library when getting tags then only matching tags are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findTags(context, null, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), FilterTags.BOOK, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("bt2")
}
@Test
fun `given user without restrictions when getting tags then all tags are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findTags(context, null, null, FilterTags.BOOK, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("bt1", "bt2", "bt_shared", "bt_10")
}
@Test
fun `given user with restricted library access when getting tags then only allowed tags are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findTags(context, null, null, FilterTags.BOOK, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("bt1", "bt_shared", "bt_10")
}
@Test
fun `given user with restricted label access when getting tags then only allowed tags are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findTags(context, null, null, FilterTags.BOOK, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("bt_shared")
}
@Test
fun `given user with restricted age access when getting tags then only allowed tags are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findTags(context, null, null, FilterTags.BOOK, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("bt_10")
}
}
@Nested
inner class SeriesTag {
@Test
fun `given search when getting tags then all tags are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findTags(context, "shared", null, FilterTags.SERIES, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st_shared")
}
@Test
fun `given filter by library when getting tags then only matching tags are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findTags(context, null, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), FilterTags.SERIES, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st2")
}
@Test
fun `given user without restrictions when getting tags then all tags are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findTags(context, null, null, FilterTags.SERIES, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st1", "st2", "st_shared", "st_10")
}
@Test
fun `given user with restricted library access when getting tags then only allowed tags are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findTags(context, null, null, FilterTags.SERIES, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st1", "st_shared", "st_10")
}
@Test
fun `given user with restricted label access when getting tags then only allowed tags are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findTags(context, null, null, FilterTags.SERIES, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st_shared")
}
@Test
fun `given user with restricted age access when getting tags then only allowed tags are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findTags(context, null, null, FilterTags.SERIES, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st_10")
}
}
@Nested
inner class BothTag {
@Test
fun `given search when getting tags then all tags are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findTags(context, "shared", null, FilterTags.BOTH, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st_shared", "bt_shared")
}
@Test
fun `given filter by library when getting tags then only matching tags are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findTags(context, null, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), FilterTags.BOTH, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st2", "bt2")
}
@Test
fun `given user without restrictions when getting tags then all tags are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findTags(context, null, null, FilterTags.BOTH, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st1", "st2", "st_shared", "st_10", "bt1", "bt2", "bt_shared", "bt_10")
}
@Test
fun `given user with restricted library access when getting tags then only allowed tags are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findTags(context, null, null, FilterTags.BOTH, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st1", "st_shared", "st_10", "bt1", "bt_shared", "bt_10")
}
@Test
fun `given user with restricted label access when getting tags then only allowed tags are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findTags(context, null, null, FilterTags.BOTH, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st_shared", "bt_shared")
}
@Test
fun `given user with restricted age access when getting tags then only allowed tags are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findTags(context, null, null, FilterTags.BOTH, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("st_10", "bt_10")
}
}
@Nested
inner class SharingLabel {
@Test
fun `given search when getting sharing labels then all sharing labels are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findSharingLabels(context, "shared", null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given filter by library when getting sharing labels then only matching sharing labels are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findSharingLabels(context, null, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item2")
}
@Test
fun `given user without restrictions when getting sharing labels then all sharing labels are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findSharingLabels(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item1", "item2", "item_shared", "item_10")
}
@Test
fun `given user with restricted library access when getting sharing labels then only allowed sharing labels are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findSharingLabels(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item1", "item_shared", "item_10")
}
@Test
fun `given user with restricted label access when getting sharing labels then only allowed sharing labels are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findSharingLabels(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given user with restricted age access when getting sharing labels then only allowed sharing labels are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findSharingLabels(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_10")
}
}
@Nested
inner class Publisher {
@Test
fun `given search when getting publishers then all publishers are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findPublishers(context, "shared", null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given filter by library when getting publishers then only matching publishers are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findPublishers(context, null, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item2")
}
@Test
fun `given user without restrictions when getting publishers then all publishers are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findPublishers(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item1", "item2", "item_shared", "item_10")
}
@Test
fun `given user with restricted library access when getting publishers then only allowed publishers are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findPublishers(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item1", "item_shared", "item_10")
}
@Test
fun `given user with restricted label access when getting publishers then only allowed publishers are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findPublishers(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_shared")
}
@Test
fun `given user with restricted age access when getting publishers then only allowed publishers are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findPublishers(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("item_10")
}
}
@Nested
inner class Language {
@Test
fun `given search when getting languages then all languages are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findLanguages(context, "j", null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("ja")
}
@Test
fun `given filter by library when getting languages then only matching languages are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findLanguages(context, null, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("en")
}
@Test
fun `given user without restrictions when getting languages then all languages are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findLanguages(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("fr", "en", "ja", "sp")
}
@Test
fun `given user with restricted library access when getting languages then only allowed languages are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findLanguages(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("fr", "ja", "sp")
}
@Test
fun `given user with restricted label access when getting languages then only allowed languages are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findLanguages(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("ja")
}
@Test
fun `given user with restricted age access when getting languages then only allowed languages are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findLanguages(context, null, null, Pageable.unpaged()).content
assertThat(items).containsExactlyInAnyOrder("sp")
}
}
@Nested
inner class AgeRating {
@Test
fun `given filter by library when getting age ratings then only matching age ratings are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAgeRatings(context, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content
assertThat(items).containsExactly(19)
}
@Test
fun `given user without restrictions when getting age ratings then all age ratings are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findAgeRatings(context, null, Pageable.unpaged()).content
assertThat(items).containsExactly(10, 18, 19)
}
@Test
fun `given user with restricted library access when getting age ratings then only allowed age ratings are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findAgeRatings(context, null, Pageable.unpaged()).content
assertThat(items).containsExactly(10, 18)
}
@Test
fun `given user with restricted label access when getting age ratings then only allowed age ratings are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findAgeRatings(context, null, Pageable.unpaged()).content
assertThat(items).isEmpty()
}
@Test
fun `given user with restricted age access when getting age ratings then only allowed age ratings are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findAgeRatings(context, null, Pageable.unpaged()).content
assertThat(items).containsExactly(10)
}
}
@Nested
inner class SeriesReleaseDate {
@Test
fun `given filter by library when getting series release dates then only matching series release dates are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findSeriesReleaseDates(context, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content
assertThat(items).containsExactly("2002")
}
@Test
fun `given user without restrictions when getting series release dates then all series release dates are returned`() {
val context = SearchContext(userAll)
val items = referentialDao.findSeriesReleaseDates(context, null, Pageable.unpaged()).content
assertThat(items).containsExactly("2004", "2003", "2002", "2001")
}
@Test
fun `given user with restricted library access when getting series release dates then only allowed series release dates are returned`() {
val context = SearchContext(userLib1)
val items = referentialDao.findSeriesReleaseDates(context, null, Pageable.unpaged()).content
assertThat(items).containsExactly("2004", "2003", "2001")
}
@Test
fun `given user with restricted label access when getting series release dates then only allowed series release dates are returned`() {
val context = SearchContext(userLabelAllow)
val items = referentialDao.findSeriesReleaseDates(context, null, Pageable.unpaged()).content
assertThat(items).containsExactly("2003")
}
@Test
fun `given user with restricted age access when getting series release dates then only allowed series release dates are returned`() {
val context = SearchContext(userAge10)
val items = referentialDao.findSeriesReleaseDates(context, null, Pageable.unpaged()).content
assertThat(items).containsExactly("2004")
}
}
}