diff --git a/komga/docs/openapi.json b/komga/docs/openapi.json index 533814df..0be7763e 100644 --- a/komga/docs/openapi.json +++ b/komga/docs/openapi.json @@ -234,7 +234,7 @@ "/api/v1/age-ratings": { "get": { "deprecated": true, - "description": "Use GET /v2/genres instead. Deprecated since 1.x.0", + "description": "Use GET /v2/age-ratings instead. Deprecated since 1.x.0", "operationId": "getAgeRatings_1", "parameters": [ { @@ -4760,7 +4760,7 @@ "/api/v1/publishers": { "get": { "deprecated": true, - "description": "Use GET /v2/genres instead. Deprecated since 1.x.0", + "description": "Use GET /v2/publishers instead. Deprecated since 1.x.0", "operationId": "getPublishers_1", "parameters": [ { @@ -6575,8 +6575,8 @@ "/api/v1/series/release-dates": { "get": { "deprecated": true, - "description": "Use GET /v2/genres instead. Deprecated since 1.x.0", - "operationId": "getSeriesReleaseDates_1", + "description": "Use GET /v2/series/release-years instead. Deprecated since 1.x.0", + "operationId": "getSeriesReleaseDates", "parameters": [ { "in": "query", @@ -7675,7 +7675,7 @@ "/api/v1/tags": { "get": { "deprecated": true, - "description": "Use GET /v2/sharing-labels instead. Deprecated since 1.x.0", + "description": "Use GET /v2/tags instead. Deprecated since 1.x.0", "operationId": "getTags_1", "parameters": [ { @@ -7735,7 +7735,7 @@ "/api/v1/tags/book": { "get": { "deprecated": true, - "description": "Use GET /v2/sharing-labels instead. Deprecated since 1.x.0", + "description": "Use GET /v2/tags instead. Deprecated since 1.x.0", "operationId": "getBookTags", "parameters": [ { @@ -7803,7 +7803,7 @@ "/api/v1/tags/series": { "get": { "deprecated": true, - "description": "Use GET /v2/sharing-labels instead. Deprecated since 1.x.0", + "description": "Use GET /v2/tags instead. Deprecated since 1.x.0", "operationId": "getSeriesTags", "parameters": [ { @@ -8736,10 +8736,10 @@ ] } }, - "/api/v2/series/release-dates": { + "/api/v2/series/release-years": { "get": { "description": "Can be filtered by various criteria", - "operationId": "getSeriesReleaseDates", + "operationId": "getSeriesReleaseYears", "parameters": [ { "in": "query", @@ -8812,7 +8812,7 @@ "description": "Bad Request" } }, - "summary": "List series release dates", + "summary": "List series release years", "tags": [ "Referential metadata" ] @@ -15342,4 +15342,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/ReferentialRepository.kt b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/ReferentialRepository.kt index a2c9c8a9..a5e977b2 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/ReferentialRepository.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/ReferentialRepository.kt @@ -228,7 +228,7 @@ interface ReferentialRepository { filterOnLibraryIds: Collection?, ): Set - fun findSeriesReleaseDates( + fun findSeriesReleaseYears( context: SearchContext, filterBy: FilterBy?, pageable: Pageable, diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt index fb3ad40f..892807b1 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt @@ -641,14 +641,60 @@ class ReferentialDao( .orderBy(bma.RELEASE_DATE.desc()) .fetchSet(bma.RELEASE_DATE) - override fun findSeriesReleaseDates( + override fun findSeriesReleaseYears( context: SearchContext, filterBy: FilterBy?, pageable: Pageable, ): Page { 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()) + val sortField = bma.RELEASE_DATE.desc() + val restrictionCondition = ContentRestrictionsSearchHelper(context.restrictions).toCondition() + val query = + dslRO + .selectDistinct(DSL.year(bma.RELEASE_DATE)) + .from(bma) + .apply { + restrictionCondition.second.forEach { join -> + when (join) { + RequiredJoin.SeriesMetadata -> innerJoin(sd).on(bma.SERIES_ID.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(bma.SERIES_ID.eq(s.ID)) } + .apply { if (filterBy?.type == FilterByEntity.COLLECTION) leftJoin(cs).on(bma.SERIES_ID.eq(cs.SERIES_ID)) } + .apply { + if (filterBy?.type == FilterByEntity.READLIST) + leftJoin(b) + .on(bma.SERIES_ID.eq(b.SERIES_ID)) + .leftJoin(rb) + .on(b.ID.eq(rb.BOOK_ID)) + }.where(restrictionCondition.first) + .apply { context.libraryIds?.let { this.and(s.LIBRARY_ID.`in`(it)) } } + .apply { + filterBy?.let { + when (it.type) { + FilterByEntity.LIBRARY -> this.and(s.LIBRARY_ID.`in`(it.ids)) + FilterByEntity.COLLECTION -> this.and(cs.COLLECTION_ID.`in`(it.ids)) + FilterByEntity.SERIES -> this.and(bma.SERIES_ID.`in`(it.ids)) + FilterByEntity.READLIST -> this.and(rb.READLIST_ID.`in`(it.ids)) + } + } + } + val count = dslRO.fetchCount(query) + val items = + query + .orderBy(sortField) + .apply { if (pageable.isPaged) limit(pageable.pageSize).offset(pageable.offset) } + .fetchArray(0) + .mapNotNull { it?.toString() } + return buildPage(items, pageable, count, Sort.by(Order.desc("year"))) } @Deprecated("Use findSharingLabels instead") diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReferentialV1Controller.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReferentialV1Controller.kt index d6e2aab2..9bcac7de 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReferentialV1Controller.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReferentialV1Controller.kt @@ -88,7 +88,7 @@ class ReferentialV1Controller( @GetMapping("tags") @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]) + @Operation(summary = "List tags", description = "Use GET /v2/tags instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED]) fun getTags( @AuthenticationPrincipal principal: KomgaPrincipal, @RequestParam(name = "library_id", required = false) libraryIds: Set = emptySet(), @@ -103,7 +103,7 @@ class ReferentialV1Controller( @GetMapping("tags/book") @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]) + @Operation(summary = "List book tags", description = "Use GET /v2/tags instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED]) fun getBookTags( @AuthenticationPrincipal principal: KomgaPrincipal, @RequestParam(name = "series_id", required = false) seriesId: String?, @@ -120,7 +120,7 @@ class ReferentialV1Controller( @GetMapping("tags/series") @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]) + @Operation(summary = "List series tags", description = "Use GET /v2/tags instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED]) fun getSeriesTags( @AuthenticationPrincipal principal: KomgaPrincipal, @RequestParam(name = "library_id", required = false) libraryId: String?, @@ -150,7 +150,7 @@ class ReferentialV1Controller( @GetMapping("publishers") @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]) + @Operation(summary = "List publishers", description = "Use GET /v2/publishers instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED]) fun getPublishers( @AuthenticationPrincipal principal: KomgaPrincipal, @RequestParam(name = "library_id", required = false) libraryIds: Set = emptySet(), @@ -165,7 +165,7 @@ class ReferentialV1Controller( @GetMapping("age-ratings") @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]) + @Operation(summary = "List age ratings", description = "Use GET /v2/age-ratings instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED]) fun getAgeRatings( @AuthenticationPrincipal principal: KomgaPrincipal, @RequestParam(name = "library_id", required = false) libraryIds: Set = emptySet(), @@ -178,9 +178,9 @@ class ReferentialV1Controller( }.map { it?.toString() ?: "None" }.toSet() @GetMapping("series/release-dates") - @Deprecated("Use GET /v2/age-ratings instead") + @Deprecated("Use GET /v2/series/release-years 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]) + @Operation(summary = "List series release dates", description = "Use GET /v2/series/release-years instead. Deprecated since 1.x.0", tags = [OpenApiConfiguration.TagNames.DEPRECATED]) fun getSeriesReleaseDates( @AuthenticationPrincipal principal: KomgaPrincipal, @RequestParam(name = "library_id", required = false) libraryIds: Set = emptySet(), diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReferentialV2Controller.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReferentialV2Controller.kt index fad4d4b0..c34d9ad1 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReferentialV2Controller.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/ReferentialV2Controller.kt @@ -292,9 +292,9 @@ class ReferentialV2Controller( } @PageableWithoutSortAsQueryParam - @GetMapping("series/release-dates") - @Operation(summary = "List series release dates", description = "Can be filtered by various criteria") - fun getSeriesReleaseDates( + @GetMapping("series/release-years") + @Operation(summary = "List series release years", description = "Can be filtered by various criteria") + fun getSeriesReleaseYears( @AuthenticationPrincipal principal: KomgaPrincipal, @RequestParam(name = "library_id", required = false) libraryIds: Set = emptySet(), @RequestParam(name = "collection_id", required = false) collectionIds: Set = emptySet(), @@ -318,7 +318,7 @@ class ReferentialV2Controller( else -> null } - return referentialRepository.findSeriesReleaseDates(SearchContext(principal.user), filterBy, pageRequest) + return referentialRepository.findSeriesReleaseYears(SearchContext(principal.user), filterBy, pageRequest) } @PageableWithoutSortAsQueryParam diff --git a/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDaoTest.kt b/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDaoTest.kt index 4c1d5179..f102f6a8 100644 --- a/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDaoTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDaoTest.kt @@ -98,7 +98,7 @@ class ReferentialDaoTest( 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"))) + bookMetadataRepository.update(it.copy(authors = listOf(Author("item1", "writer")), releaseDate = LocalDate.of(2002, 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"))) @@ -109,7 +109,7 @@ class ReferentialDaoTest( 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"))) + bookMetadataRepository.update(it.copy(authors = listOf(Author("item2", "inker")), releaseDate = LocalDate.of(2002, 2, 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"))) @@ -729,11 +729,11 @@ class ReferentialDaoTest( } @Nested - inner class SeriesReleaseDate { + inner class SeriesReleaseYear { @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 + val items = referentialDao.findSeriesReleaseYears(context, FilterBy(FilterByEntity.LIBRARY, setOf(library2.id)), Pageable.unpaged()).content assertThat(items).containsExactly("2002") } @@ -741,23 +741,23 @@ class ReferentialDaoTest( @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 + val items = referentialDao.findSeriesReleaseYears(context, null, Pageable.unpaged()).content - assertThat(items).containsExactly("2004", "2003", "2002", "2001") + assertThat(items).containsExactly("2004", "2003", "2002") } @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 + val items = referentialDao.findSeriesReleaseYears(context, null, Pageable.unpaged()).content - assertThat(items).containsExactly("2004", "2003", "2001") + assertThat(items).containsExactly("2004", "2003", "2002") } @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 + val items = referentialDao.findSeriesReleaseYears(context, null, Pageable.unpaged()).content assertThat(items).containsExactly("2003") } @@ -765,7 +765,7 @@ class ReferentialDaoTest( @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 + val items = referentialDao.findSeriesReleaseYears(context, null, Pageable.unpaged()).content assertThat(items).containsExactly("2004") }