From edf376e3dd668b111a592d65d91880b650072720 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 19:01:34 -0400 Subject: [PATCH] Update graphqlkotlin to v10 alpha (major) (#1923) * Update graphqlkotlin to v9 * Update to the v10 alpha due to nullability issues in v9 * Fixes * Remove asDataFetcherResult --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Syer10 --- gradle/libs.versions.toml | 2 +- .../tachidesk/graphql/AsDataFetcherResult.kt | 27 - .../tachidesk/graphql/cache/CustomCacheMap.kt | 16 +- .../graphql/dataLoaders/ChapterDataLoader.kt | 50 +- .../dataLoaders/ExtensionDataLoader.kt | 8 +- .../graphql/dataLoaders/MangaDataLoader.kt | 6 +- .../graphql/dataLoaders/MetaDataLoader.kt | 6 +- .../graphql/dataLoaders/SourceDataLoader.kt | 4 +- .../graphql/mutations/BackupMutation.kt | 2 + .../graphql/mutations/CategoryMutation.kt | 559 +++++++++--------- .../graphql/mutations/ChapterMutation.kt | 402 ++++++------- .../graphql/mutations/DownloadMutation.kt | 291 +++++---- .../graphql/mutations/ExtensionMutation.kt | 109 ++-- .../graphql/mutations/ImageMutation.kt | 2 + .../graphql/mutations/InfoMutation.kt | 67 +-- .../graphql/mutations/KoreaderSyncMutation.kt | 99 ++-- .../graphql/mutations/MangaMutation.kt | 278 +++++---- .../graphql/mutations/MetaMutation.kt | 133 ++--- .../graphql/mutations/SettingsMutation.kt | 2 + .../graphql/mutations/SourceMutation.kt | 332 +++++------ .../graphql/mutations/TrackMutation.kt | 31 +- .../graphql/mutations/UpdateMutation.kt | 61 +- .../graphql/mutations/UserMutation.kt | 2 + .../server/JavalinGraphQLRequestParser.kt | 7 +- .../graphql/server/TachideskGraphQLServer.kt | 2 +- .../graphql/server/primitives/Cursor.kt | 2 +- .../server/primitives/DurationAsString.kt | 2 +- .../graphql/server/primitives/LongAsString.kt | 2 +- .../ApolloSubscriptionProtocolHandler.kt | 6 +- 29 files changed, 1197 insertions(+), 1313 deletions(-) delete mode 100644 server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 13c45dbe..01244fb8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ dex2jar = "2.4.36" polyglot = "25.0.3" settings = "1.3.0" twelvemonkeys = "3.13.1" -graphqlkotlin = "8.9.0" +graphqlkotlin = "10.0.0-alpha.3" xmlserialization = "0.91.3" ktlint = "1.8.0" koin = "4.2.1" diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt deleted file mode 100644 index 6c3a0f74..00000000 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt +++ /dev/null @@ -1,27 +0,0 @@ -package suwayomi.tachidesk.graphql - -import com.expediagroup.graphql.server.extensions.toGraphQLError -import graphql.execution.DataFetcherResult -import io.github.oshai.kotlinlogging.KotlinLogging - -val logger = KotlinLogging.logger { } - -inline fun asDataFetcherResult(block: () -> T): DataFetcherResult { - val result = - runCatching { - block() - } - - if (result.isFailure) { - logger.error(result.exceptionOrNull()) { "asDataFetcherResult: failed due to" } - return DataFetcherResult - .newResult() - .error(result.exceptionOrNull()?.toGraphQLError()) - .build() - } - - return DataFetcherResult - .newResult() - .data(result.getOrNull()) - .build() -} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/cache/CustomCacheMap.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/cache/CustomCacheMap.kt index 742da59a..6fd089b3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/cache/CustomCacheMap.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/cache/CustomCacheMap.kt @@ -3,12 +3,8 @@ package suwayomi.tachidesk.graphql.cache import org.dataloader.CacheMap import java.util.concurrent.CompletableFuture -class CustomCacheMap : CacheMap { - private val cache: MutableMap> - - init { - cache = HashMap() - } +class CustomCacheMap : CacheMap { + private val cache: MutableMap> = HashMap() override fun containsKey(key: K): Boolean = cache.containsKey(key) @@ -18,12 +14,12 @@ class CustomCacheMap : CacheMap { override fun getAll(): Collection> = cache.values - override fun set( + override fun putIfAbsentAtomically( key: K, value: CompletableFuture, - ): CacheMap { + ): CompletableFuture { cache[key] = value - return this + return value } override fun delete(key: K): CacheMap { @@ -35,4 +31,6 @@ class CustomCacheMap : CacheMap { cache.clear() return this } + + override fun size(): Int = cache.size } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt index f0fe0a89..44ef6de3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt @@ -24,11 +24,11 @@ import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.server.JavalinSetup.future -class ChapterDataLoader : KotlinDataLoader { +class ChapterDataLoader : KotlinDataLoader { override val dataLoaderName = "ChapterDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -48,7 +48,7 @@ class ChaptersForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "ChaptersForMangaDataLoader" override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -68,7 +68,7 @@ class DownloadedChapterCountForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "DownloadedChapterCountForMangaDataLoader" override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -90,7 +90,7 @@ class UnreadChapterCountForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "UnreadChapterCountForMangaDataLoader" override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -112,7 +112,7 @@ class BookmarkedChapterCountForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "BookmarkedChapterCountForMangaDataLoader" override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -157,11 +157,11 @@ class HasDuplicateChaptersForMangaDataLoader : KotlinDataLoader { } } -class LastReadChapterForMangaDataLoader : KotlinDataLoader { +class LastReadChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "LastReadChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -177,11 +177,11 @@ class LastReadChapterForMangaDataLoader : KotlinDataLoader { } } -class LatestReadChapterForMangaDataLoader : KotlinDataLoader { +class LatestReadChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "LatestReadChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -197,11 +197,11 @@ class LatestReadChapterForMangaDataLoader : KotlinDataLoader } } -class LatestFetchedChapterForMangaDataLoader : KotlinDataLoader { +class LatestFetchedChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "LatestFetchedChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -217,11 +217,11 @@ class LatestFetchedChapterForMangaDataLoader : KotlinDataLoader { +class LatestUploadedChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "LatestUploadedChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -237,11 +237,11 @@ class LatestUploadedChapterForMangaDataLoader : KotlinDataLoader { +class FirstUnreadChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "FirstUnreadChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) @@ -257,11 +257,11 @@ class FirstUnreadChapterForMangaDataLoader : KotlinDataLoader } } -class HighestNumberedChapterForMangaDataLoader : KotlinDataLoader { +class HighestNumberedChapterForMangaDataLoader : KotlinDataLoader { override val dataLoaderName = "HighestNumberedChapterForMangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ExtensionDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ExtensionDataLoader.kt index 2c7be2fa..c3b4b695 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ExtensionDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ExtensionDataLoader.kt @@ -20,10 +20,10 @@ import suwayomi.tachidesk.manga.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.SourceTable import suwayomi.tachidesk.server.JavalinSetup.future -class ExtensionDataLoader : KotlinDataLoader { +class ExtensionDataLoader : KotlinDataLoader { override val dataLoaderName = "ExtensionDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { @@ -40,10 +40,10 @@ class ExtensionDataLoader : KotlinDataLoader { } } -class ExtensionForSourceDataLoader : KotlinDataLoader { +class ExtensionForSourceDataLoader : KotlinDataLoader { override val dataLoaderName = "ExtensionForSourceDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt index 0775d71a..a49b3260 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt @@ -25,10 +25,10 @@ import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.server.JavalinSetup.future -class MangaDataLoader : KotlinDataLoader { +class MangaDataLoader : KotlinDataLoader { override val dataLoaderName = "MangaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { @@ -122,6 +122,6 @@ class MangaForIdsDataLoader : KotlinDataLoader, MangaNodeList> { } } }, - DataLoaderOptions.newOptions().setCacheMap(CustomCacheMap, MangaNodeList>()), + DataLoaderOptions.newOptions().setCacheMap(CustomCacheMap, MangaNodeList>()).build(), ) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt index a01f2392..d2515bca 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt @@ -20,11 +20,11 @@ import suwayomi.tachidesk.manga.model.table.MangaMetaTable import suwayomi.tachidesk.manga.model.table.SourceMetaTable import suwayomi.tachidesk.server.JavalinSetup.future -class GlobalMetaDataLoader : KotlinDataLoader { +class GlobalMetaDataLoader : KotlinDataLoader { override val dataLoaderName = "GlobalMetaDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = - DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/SourceDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/SourceDataLoader.kt index be3c60e0..3fc2269c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/SourceDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/SourceDataLoader.kt @@ -22,10 +22,10 @@ import suwayomi.tachidesk.manga.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.SourceTable import suwayomi.tachidesk.server.JavalinSetup.future -class SourceDataLoader : KotlinDataLoader { +class SourceDataLoader : KotlinDataLoader { override val dataLoaderName = "SourceDataLoader" - override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = + override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt index 3f8b6af2..19ba10cc 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt index 132a399e..dd988fd1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import graphql.execution.DataFetcherResult @@ -15,7 +17,6 @@ import org.jetbrains.exposed.sql.or import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.CategoryMetaType import suwayomi.tachidesk.graphql.types.CategoryType @@ -42,14 +43,13 @@ class CategoryMutation { ) @RequireAuth - fun setCategoryMeta(input: SetCategoryMetaInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, meta) = input + fun setCategoryMeta(input: SetCategoryMetaInput): SetCategoryMetaPayload? { + val (clientMutationId, meta) = input - Category.modifyMeta(meta.categoryId, meta.key, meta.value) + Category.modifyMeta(meta.categoryId, meta.key, meta.value) - SetCategoryMetaPayload(clientMutationId, meta) - } + return SetCategoryMetaPayload(clientMutationId, meta) + } data class DeleteCategoryMetaInput( val clientMutationId: String? = null, @@ -64,34 +64,33 @@ class CategoryMutation { ) @RequireAuth - fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, categoryId, key) = input + fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DeleteCategoryMetaPayload? { + val (clientMutationId, categoryId, key) = input - val (meta, category) = - transaction { - val meta = - CategoryMetaTable - .selectAll() - .where { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } - .firstOrNull() + val (meta, category) = + transaction { + val meta = + CategoryMetaTable + .selectAll() + .where { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } + .firstOrNull() - CategoryMetaTable.deleteWhere { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } + CategoryMetaTable.deleteWhere { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } - val category = - transaction { - CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq categoryId }.first()) - } + val category = + transaction { + CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq categoryId }.first()) + } - if (meta != null) { - CategoryMetaType(meta) - } else { - null - } to category - } + if (meta != null) { + CategoryMetaType(meta) + } else { + null + } to category + } - DeleteCategoryMetaPayload(clientMutationId, meta, category) - } + return DeleteCategoryMetaPayload(clientMutationId, meta, category) + } data class SetCategoryMetasItem( val categoryIds: List, @@ -110,43 +109,42 @@ class CategoryMutation { ) @RequireAuth - fun setCategoryMetas(input: SetCategoryMetasInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, items) = input + fun setCategoryMetas(input: SetCategoryMetasInput): SetCategoryMetasPayload? { + val (clientMutationId, items) = input - val metaByCategoryId = - items - .flatMap { item -> - val metaMap = item.metas.associate { it.key to it.value } - item.categoryIds.map { categoryId -> categoryId to metaMap } - }.groupBy({ it.first }, { it.second }) - .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + val metaByCategoryId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.categoryIds.map { categoryId -> categoryId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } - Category.modifyCategoriesMetas(metaByCategoryId) + Category.modifyCategoriesMetas(metaByCategoryId) - val allCategoryIds = metaByCategoryId.keys - val allMetaKeys = metaByCategoryId.values.flatMap { item -> item.keys }.distinct() + val allCategoryIds = metaByCategoryId.keys + val allMetaKeys = metaByCategoryId.values.flatMap { item -> item.keys }.distinct() - val (updatedMetas, categories) = - transaction { - val updatedMetas = - CategoryMetaTable - .selectAll() - .where { (CategoryMetaTable.ref inList allCategoryIds) and (CategoryMetaTable.key inList allMetaKeys) } - .map { CategoryMetaType(it) } + val (updatedMetas, categories) = + transaction { + val updatedMetas = + CategoryMetaTable + .selectAll() + .where { (CategoryMetaTable.ref inList allCategoryIds) and (CategoryMetaTable.key inList allMetaKeys) } + .map { CategoryMetaType(it) } - val categories = - CategoryTable - .selectAll() - .where { CategoryTable.id inList allCategoryIds } - .map { CategoryType(it) } - .distinctBy { it.id } + val categories = + CategoryTable + .selectAll() + .where { CategoryTable.id inList allCategoryIds } + .map { CategoryType(it) } + .distinctBy { it.id } - updatedMetas to categories - } + updatedMetas to categories + } - SetCategoryMetasPayload(clientMutationId, updatedMetas, categories) - } + return SetCategoryMetasPayload(clientMutationId, updatedMetas, categories) + } data class DeleteCategoryMetasItem( val categoryIds: List, @@ -166,64 +164,63 @@ class CategoryMutation { ) @RequireAuth - fun deleteCategoryMetas(input: DeleteCategoryMetasInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, items) = input + fun deleteCategoryMetas(input: DeleteCategoryMetasInput): DeleteCategoryMetasPayload? { + val (clientMutationId, items) = input - items.forEach { item -> - require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided for each item" + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allCategoryIds) = + transaction { + val deletedMetas = mutableListOf() + val categoryIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { CategoryMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (CategoryMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (CategoryMetaTable.ref inList item.categoryIds) and metaKeyCondition + + deletedMetas += + CategoryMetaTable + .selectAll() + .where { condition } + .map { CategoryMetaType(it) } + + CategoryMetaTable.deleteWhere { condition } + categoryIds += item.categoryIds } + + deletedMetas to categoryIds } - val (allDeletedMetas, allCategoryIds) = - transaction { - val deletedMetas = mutableListOf() - val categoryIds = mutableSetOf() + val categories = + transaction { + CategoryTable + .selectAll() + .where { CategoryTable.id inList allCategoryIds } + .map { CategoryType(it) } + .distinctBy { it.id } + } - items.forEach { item -> - val keyCondition: Op? = - item.keys?.takeIf { it.isNotEmpty() }?.let { CategoryMetaTable.key inList it } - - val prefixCondition: Op? = - item.prefixes - ?.filter { it.isNotEmpty() } - ?.map { (CategoryMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val metaKeyCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val condition = (CategoryMetaTable.ref inList item.categoryIds) and metaKeyCondition - - deletedMetas += - CategoryMetaTable - .selectAll() - .where { condition } - .map { CategoryMetaType(it) } - - CategoryMetaTable.deleteWhere { condition } - categoryIds += item.categoryIds - } - - deletedMetas to categoryIds - } - - val categories = - transaction { - CategoryTable - .selectAll() - .where { CategoryTable.id inList allCategoryIds } - .map { CategoryType(it) } - .distinctBy { it.id } - } - - DeleteCategoryMetasPayload(clientMutationId, allDeletedMetas, categories) - } + return DeleteCategoryMetasPayload(clientMutationId, allDeletedMetas, categories) + } data class UpdateCategoryPatch( val name: String? = null, @@ -291,40 +288,38 @@ class CategoryMutation { } @RequireAuth - fun updateCategory(input: UpdateCategoryInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, id, patch) = input + fun updateCategory(input: UpdateCategoryInput): UpdateCategoryPayload? { + val (clientMutationId, id, patch) = input - updateCategories(listOf(id), patch) + updateCategories(listOf(id), patch) - val category = - transaction { - CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq id }.first()) - } + val category = + transaction { + CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq id }.first()) + } - UpdateCategoryPayload( - clientMutationId = clientMutationId, - category = category, - ) - } + return UpdateCategoryPayload( + clientMutationId = clientMutationId, + category = category, + ) + } @RequireAuth - fun updateCategories(input: UpdateCategoriesInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, ids, patch) = input + fun updateCategories(input: UpdateCategoriesInput): UpdateCategoriesPayload? { + val (clientMutationId, ids, patch) = input - updateCategories(ids, patch) + updateCategories(ids, patch) - val categories = - transaction { - CategoryTable.selectAll().where { CategoryTable.id inList ids }.map { CategoryType(it) } - } + val categories = + transaction { + CategoryTable.selectAll().where { CategoryTable.id inList ids }.map { CategoryType(it) } + } - UpdateCategoriesPayload( - clientMutationId = clientMutationId, - categories = categories, - ) - } + return UpdateCategoriesPayload( + clientMutationId = clientMutationId, + categories = categories, + ) + } data class UpdateCategoryOrderPayload( val clientMutationId: String?, @@ -338,50 +333,49 @@ class CategoryMutation { ) @RequireAuth - fun updateCategoryOrder(input: UpdateCategoryOrderInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, categoryId, position) = input - require(position > 0) { - "'order' must not be <= 0" - } - - transaction { - val currentOrder = - CategoryTable - .selectAll() - .where { CategoryTable.id eq categoryId } - .first()[CategoryTable.order] - - if (currentOrder != position) { - if (position < currentOrder) { - CategoryTable.update({ CategoryTable.order greaterEq position }) { - it[CategoryTable.order] = CategoryTable.order + 1 - } - } else { - CategoryTable.update({ CategoryTable.order lessEq position }) { - it[CategoryTable.order] = CategoryTable.order - 1 - } - } - - CategoryTable.update({ CategoryTable.id eq categoryId }) { - it[CategoryTable.order] = position - } - } - } - - Category.normalizeCategories() - - val categories = - transaction { - CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) } - } - - UpdateCategoryOrderPayload( - clientMutationId = clientMutationId, - categories = categories, - ) + fun updateCategoryOrder(input: UpdateCategoryOrderInput): UpdateCategoryOrderPayload? { + val (clientMutationId, categoryId, position) = input + require(position > 0) { + "'order' must not be <= 0" } + transaction { + val currentOrder = + CategoryTable + .selectAll() + .where { CategoryTable.id eq categoryId } + .first()[CategoryTable.order] + + if (currentOrder != position) { + if (position < currentOrder) { + CategoryTable.update({ CategoryTable.order greaterEq position }) { + it[CategoryTable.order] = CategoryTable.order + 1 + } + } else { + CategoryTable.update({ CategoryTable.order lessEq position }) { + it[CategoryTable.order] = CategoryTable.order - 1 + } + } + + CategoryTable.update({ CategoryTable.id eq categoryId }) { + it[CategoryTable.order] = position + } + } + } + + Category.normalizeCategories() + + val categories = + transaction { + CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) } + } + + return UpdateCategoryOrderPayload( + clientMutationId = clientMutationId, + categories = categories, + ) + } + data class CreateCategoryInput( val clientMutationId: String? = null, val name: String, @@ -397,53 +391,52 @@ class CategoryMutation { ) @RequireAuth - fun createCategory(input: CreateCategoryInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input - transaction { - require(CategoryTable.selectAll().where { CategoryTable.name eq input.name }.isEmpty()) { - "'name' must be unique" - } + fun createCategory(input: CreateCategoryInput): CreateCategoryPayload? { + val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input + transaction { + require(CategoryTable.selectAll().where { CategoryTable.name eq input.name }.isEmpty()) { + "'name' must be unique" } - require(!name.equals(Category.DEFAULT_CATEGORY_NAME, ignoreCase = true)) { - "'name' must not be ${Category.DEFAULT_CATEGORY_NAME}" - } - if (order != null) { - require(order > 0) { - "'order' must not be <= 0" - } + } + require(!name.equals(Category.DEFAULT_CATEGORY_NAME, ignoreCase = true)) { + "'name' must not be ${Category.DEFAULT_CATEGORY_NAME}" + } + if (order != null) { + require(order > 0) { + "'order' must not be <= 0" } + } - val category = - transaction { - if (order != null) { - CategoryTable.update({ CategoryTable.order greaterEq order }) { - it[CategoryTable.order] = CategoryTable.order + 1 + val category = + transaction { + if (order != null) { + CategoryTable.update({ CategoryTable.order greaterEq order }) { + it[CategoryTable.order] = CategoryTable.order + 1 + } + } + + val id = + CategoryTable.insertAndGetId { + it[CategoryTable.name] = input.name + it[CategoryTable.order] = order ?: Int.MAX_VALUE + if (default != null) { + it[CategoryTable.isDefault] = default + } + if (includeInUpdate != null) { + it[CategoryTable.includeInUpdate] = includeInUpdate.value + } + if (includeInDownload != null) { + it[CategoryTable.includeInDownload] = includeInDownload.value } } - val id = - CategoryTable.insertAndGetId { - it[CategoryTable.name] = input.name - it[CategoryTable.order] = order ?: Int.MAX_VALUE - if (default != null) { - it[CategoryTable.isDefault] = default - } - if (includeInUpdate != null) { - it[CategoryTable.includeInUpdate] = includeInUpdate.value - } - if (includeInDownload != null) { - it[CategoryTable.includeInDownload] = includeInDownload.value - } - } + Category.normalizeCategories() - Category.normalizeCategories() + CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq id }.first()) + } - CategoryType(CategoryTable.selectAll().where { CategoryTable.id eq id }.first()) - } - - CreateCategoryPayload(clientMutationId, category) - } + return CreateCategoryPayload(clientMutationId, category) + } data class DeleteCategoryInput( val clientMutationId: String? = null, @@ -457,47 +450,45 @@ class CategoryMutation { ) @RequireAuth - fun deleteCategory(input: DeleteCategoryInput): DataFetcherResult { - return asDataFetcherResult { - val (clientMutationId, categoryId) = input - if (categoryId == 0) { // Don't delete default category - return@asDataFetcherResult DeleteCategoryPayload( - clientMutationId, - null, - emptyList(), - ) + fun deleteCategory(input: DeleteCategoryInput): DeleteCategoryPayload? { + val (clientMutationId, categoryId) = input + if (categoryId == 0) { // Don't delete default category + return DeleteCategoryPayload( + clientMutationId, + null, + emptyList(), + ) + } + + val (category, mangas) = + transaction { + val category = + CategoryTable + .selectAll() + .where { CategoryTable.id eq categoryId } + .firstOrNull() + + val mangas = + transaction { + MangaTable + .innerJoin(CategoryMangaTable) + .selectAll() + .where { CategoryMangaTable.category eq categoryId } + .map { MangaType(it) } + } + + CategoryTable.deleteWhere { CategoryTable.id eq categoryId } + + Category.normalizeCategories() + + if (category != null) { + CategoryType(category) + } else { + null + } to mangas } - val (category, mangas) = - transaction { - val category = - CategoryTable - .selectAll() - .where { CategoryTable.id eq categoryId } - .firstOrNull() - - val mangas = - transaction { - MangaTable - .innerJoin(CategoryMangaTable) - .selectAll() - .where { CategoryMangaTable.category eq categoryId } - .map { MangaType(it) } - } - - CategoryTable.deleteWhere { CategoryTable.id eq categoryId } - - Category.normalizeCategories() - - if (category != null) { - CategoryType(category) - } else { - null - } to mangas - } - - DeleteCategoryPayload(clientMutationId, category, mangas) - } + return DeleteCategoryPayload(clientMutationId, category, mangas) } data class UpdateMangaCategoriesPatch( @@ -547,38 +538,36 @@ class CategoryMutation { } @RequireAuth - fun updateMangaCategories(input: UpdateMangaCategoriesInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, id, patch) = input + fun updateMangaCategories(input: UpdateMangaCategoriesInput): UpdateMangaCategoriesPayload? { + val (clientMutationId, id, patch) = input - updateMangas(listOf(id), patch) + updateMangas(listOf(id), patch) - val manga = - transaction { - MangaType(MangaTable.selectAll().where { MangaTable.id eq id }.first()) - } + val manga = + transaction { + MangaType(MangaTable.selectAll().where { MangaTable.id eq id }.first()) + } - UpdateMangaCategoriesPayload( - clientMutationId = clientMutationId, - manga = manga, - ) - } + return UpdateMangaCategoriesPayload( + clientMutationId = clientMutationId, + manga = manga, + ) + } @RequireAuth - fun updateMangasCategories(input: UpdateMangasCategoriesInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, ids, patch) = input + fun updateMangasCategories(input: UpdateMangasCategoriesInput): UpdateMangasCategoriesPayload? { + val (clientMutationId, ids, patch) = input - updateMangas(ids, patch) + updateMangas(ids, patch) - val mangas = - transaction { - MangaTable.selectAll().where { MangaTable.id inList ids }.map { MangaType(it) } - } + val mangas = + transaction { + MangaTable.selectAll().where { MangaTable.id inList ids }.map { MangaType(it) } + } - UpdateMangasCategoriesPayload( - clientMutationId = clientMutationId, - mangas = mangas, - ) - } + return UpdateMangasCategoriesPayload( + clientMutationId = clientMutationId, + mangas = mangas, + ) + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt index 9d33fcf3..a3ac94af 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt @@ -1,6 +1,7 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.jetbrains.exposed.dao.id.EntityID @@ -16,7 +17,6 @@ import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.statements.BatchUpdateStatement import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.ChapterMetaType import suwayomi.tachidesk.graphql.types.ChapterType @@ -120,40 +120,38 @@ class ChapterMutation { } @RequireAuth - fun updateChapter(input: UpdateChapterInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, id, patch) = input + fun updateChapter(input: UpdateChapterInput): UpdateChapterPayload? { + val (clientMutationId, id, patch) = input - updateChapters(listOf(id), patch) + updateChapters(listOf(id), patch) - val chapter = - transaction { - ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq id }.first()) - } + val chapter = + transaction { + ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq id }.first()) + } - UpdateChapterPayload( - clientMutationId = clientMutationId, - chapter = chapter, - ) - } + return UpdateChapterPayload( + clientMutationId = clientMutationId, + chapter = chapter, + ) + } @RequireAuth - fun updateChapters(input: UpdateChaptersInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, ids, patch) = input + fun updateChapters(input: UpdateChaptersInput): UpdateChaptersPayload? { + val (clientMutationId, ids, patch) = input - updateChapters(ids, patch) + updateChapters(ids, patch) - val chapters = - transaction { - ChapterTable.selectAll().where { ChapterTable.id inList ids }.map { ChapterType(it) } - } + val chapters = + transaction { + ChapterTable.selectAll().where { ChapterTable.id inList ids }.map { ChapterType(it) } + } - UpdateChaptersPayload( - clientMutationId = clientMutationId, - chapters = chapters, - ) - } + return UpdateChaptersPayload( + clientMutationId = clientMutationId, + chapters = chapters, + ) + } data class FetchChaptersInput( val clientMutationId: String? = null, @@ -166,27 +164,25 @@ class ChapterMutation { ) @RequireAuth - fun fetchChapters(input: FetchChaptersInput): CompletableFuture> { + fun fetchChapters(input: FetchChaptersInput): CompletableFuture { val (clientMutationId, mangaId) = input return future { - asDataFetcherResult { - Chapter.fetchChapterList(mangaId) + Chapter.fetchChapterList(mangaId) - val chapters = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.manga eq mangaId } - .orderBy(ChapterTable.sourceOrder) - .map { ChapterType(it) } - } + val chapters = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.manga eq mangaId } + .orderBy(ChapterTable.sourceOrder) + .map { ChapterType(it) } + } - FetchChaptersPayload( - clientMutationId = clientMutationId, - chapters = chapters, - ) - } + FetchChaptersPayload( + clientMutationId = clientMutationId, + chapters = chapters, + ) } } @@ -201,14 +197,13 @@ class ChapterMutation { ) @RequireAuth - fun setChapterMeta(input: SetChapterMetaInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, meta) = input + fun setChapterMeta(input: SetChapterMetaInput): SetChapterMetaPayload? { + val (clientMutationId, meta) = input - Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value) + Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value) - SetChapterMetaPayload(clientMutationId, meta) - } + return SetChapterMetaPayload(clientMutationId, meta) + } data class DeleteChapterMetaInput( val clientMutationId: String? = null, @@ -223,34 +218,33 @@ class ChapterMutation { ) @RequireAuth - fun deleteChapterMeta(input: DeleteChapterMetaInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, chapterId, key) = input + fun deleteChapterMeta(input: DeleteChapterMetaInput): DeleteChapterMetaPayload? { + val (clientMutationId, chapterId, key) = input - val (meta, chapter) = - transaction { - val meta = - ChapterMetaTable - .selectAll() - .where { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } - .firstOrNull() + val (meta, chapter) = + transaction { + val meta = + ChapterMetaTable + .selectAll() + .where { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } + .firstOrNull() - ChapterMetaTable.deleteWhere { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } + ChapterMetaTable.deleteWhere { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } - val chapter = - transaction { - ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq chapterId }.first()) - } + val chapter = + transaction { + ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq chapterId }.first()) + } - if (meta != null) { - ChapterMetaType(meta) - } else { - null - } to chapter - } + if (meta != null) { + ChapterMetaType(meta) + } else { + null + } to chapter + } - DeleteChapterMetaPayload(clientMutationId, meta, chapter) - } + return DeleteChapterMetaPayload(clientMutationId, meta, chapter) + } data class SetChapterMetasItem( val chapterIds: List, @@ -269,43 +263,42 @@ class ChapterMutation { ) @RequireAuth - fun setChapterMetas(input: SetChapterMetasInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, items) = input + fun setChapterMetas(input: SetChapterMetasInput): SetChapterMetasPayload? { + val (clientMutationId, items) = input - val metaByChapterId = - items - .flatMap { item -> - val metaMap = item.metas.associate { it.key to it.value } - item.chapterIds.map { chapterId -> chapterId to metaMap } - }.groupBy({ it.first }, { it.second }) - .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + val metaByChapterId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.chapterIds.map { chapterId -> chapterId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } - Chapter.modifyChaptersMetas(metaByChapterId) + Chapter.modifyChaptersMetas(metaByChapterId) - val allChapterIds = metaByChapterId.keys - val allMetaKeys = metaByChapterId.values.flatMap { it.keys }.distinct() + val allChapterIds = metaByChapterId.keys + val allMetaKeys = metaByChapterId.values.flatMap { it.keys }.distinct() - val (updatedMetas, chapters) = - transaction { - val updatedMetas = - ChapterMetaTable - .selectAll() - .where { (ChapterMetaTable.ref inList allChapterIds) and (ChapterMetaTable.key inList allMetaKeys) } - .map { ChapterMetaType(it) } + val (updatedMetas, chapters) = + transaction { + val updatedMetas = + ChapterMetaTable + .selectAll() + .where { (ChapterMetaTable.ref inList allChapterIds) and (ChapterMetaTable.key inList allMetaKeys) } + .map { ChapterMetaType(it) } - val chapters = - ChapterTable - .selectAll() - .where { ChapterTable.id inList allChapterIds } - .map { ChapterType(it) } - .distinctBy { it.id } + val chapters = + ChapterTable + .selectAll() + .where { ChapterTable.id inList allChapterIds } + .map { ChapterType(it) } + .distinctBy { it.id } - updatedMetas to chapters - } + updatedMetas to chapters + } - SetChapterMetasPayload(clientMutationId, updatedMetas, chapters) - } + return SetChapterMetasPayload(clientMutationId, updatedMetas, chapters) + } data class DeleteChapterMetasItem( val chapterIds: List, @@ -325,64 +318,63 @@ class ChapterMutation { ) @RequireAuth - fun deleteChapterMetas(input: DeleteChapterMetasInput): DataFetcherResult = - asDataFetcherResult { - val (clientMutationId, items) = input + fun deleteChapterMetas(input: DeleteChapterMetasInput): DeleteChapterMetasPayload? { + val (clientMutationId, items) = input - items.forEach { item -> - require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided for each item" + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allChapterIds) = + transaction { + val deletedMetas = mutableListOf() + val chapterIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { ChapterMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (ChapterMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (ChapterMetaTable.ref inList item.chapterIds) and metaKeyCondition + + deletedMetas += + ChapterMetaTable + .selectAll() + .where { condition } + .map { ChapterMetaType(it) } + + ChapterMetaTable.deleteWhere { condition } + chapterIds += item.chapterIds } + + deletedMetas to chapterIds } - val (allDeletedMetas, allChapterIds) = - transaction { - val deletedMetas = mutableListOf() - val chapterIds = mutableSetOf() + val chapters = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.id inList allChapterIds } + .map { ChapterType(it) } + .distinctBy { it.id } + } - items.forEach { item -> - val keyCondition: Op? = - item.keys?.takeIf { it.isNotEmpty() }?.let { ChapterMetaTable.key inList it } - - val prefixCondition: Op? = - item.prefixes - ?.filter { it.isNotEmpty() } - ?.map { (ChapterMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val metaKeyCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val condition = (ChapterMetaTable.ref inList item.chapterIds) and metaKeyCondition - - deletedMetas += - ChapterMetaTable - .selectAll() - .where { condition } - .map { ChapterMetaType(it) } - - ChapterMetaTable.deleteWhere { condition } - chapterIds += item.chapterIds - } - - deletedMetas to chapterIds - } - - val chapters = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.id inList allChapterIds } - .map { ChapterType(it) } - .distinctBy { it.id } - } - - DeleteChapterMetasPayload(clientMutationId, allDeletedMetas, chapters) - } + return DeleteChapterMetasPayload(clientMutationId, allDeletedMetas, chapters) + } data class FetchChapterPagesInput( val clientMutationId: String? = null, @@ -405,67 +397,65 @@ class ChapterMutation { ) @RequireAuth - fun fetchChapterPages(input: FetchChapterPagesInput): CompletableFuture> { + fun fetchChapterPages(input: FetchChapterPagesInput): CompletableFuture { val (clientMutationId, chapterId) = input val paramsMap = input.toParams() return future { - asDataFetcherResult { - var chapter = getChapterDownloadReadyById(chapterId) - val syncResult = KoreaderSyncService.checkAndPullProgress(chapter.id) - var syncConflictInfo: SyncConflictInfoType? = null + var chapter = getChapterDownloadReadyById(chapterId) + val syncResult = KoreaderSyncService.checkAndPullProgress(chapter.id) + var syncConflictInfo: SyncConflictInfoType? = null - if (syncResult != null) { - if (syncResult.isConflict) { - syncConflictInfo = - SyncConflictInfoType( - deviceName = syncResult.device, - remotePage = syncResult.pageRead, - ) - } - - if (syncResult.shouldUpdate) { - // Update DB for SILENT and RECEIVE - transaction { - ChapterTable.update({ ChapterTable.id eq chapter.id }) { - it[lastPageRead] = syncResult.pageRead - it[lastReadAt] = syncResult.timestamp - } - } - } - // For PROMPT, SILENT, and RECEIVE, return the remote progress - chapter = - chapter.copy( - lastPageRead = if (syncResult.shouldUpdate) syncResult.pageRead else chapter.lastPageRead, - lastReadAt = if (syncResult.shouldUpdate) syncResult.timestamp else chapter.lastReadAt, + if (syncResult != null) { + if (syncResult.isConflict) { + syncConflictInfo = + SyncConflictInfoType( + deviceName = syncResult.device, + remotePage = syncResult.pageRead, ) } - val params = - buildString { - if (paramsMap.isNotEmpty()) { - append("?") - paramsMap.entries.forEach { entry -> - if (length > 1) { - append("&") - } - append(entry.key) - append("=") - append(URLEncoder.encode(entry.value, Charsets.UTF_8)) - } + if (syncResult.shouldUpdate) { + // Update DB for SILENT and RECEIVE + transaction { + ChapterTable.update({ ChapterTable.id eq chapter.id }) { + it[lastPageRead] = syncResult.pageRead + it[lastReadAt] = syncResult.timestamp } } - - FetchChapterPagesPayload( - clientMutationId = clientMutationId, - pages = - List(chapter.pageCount) { index -> - "/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/${index}$params" - }, - chapter = ChapterType(chapter), - syncConflict = syncConflictInfo, - ) + } + // For PROMPT, SILENT, and RECEIVE, return the remote progress + chapter = + chapter.copy( + lastPageRead = if (syncResult.shouldUpdate) syncResult.pageRead else chapter.lastPageRead, + lastReadAt = if (syncResult.shouldUpdate) syncResult.timestamp else chapter.lastReadAt, + ) } + + val params = + buildString { + if (paramsMap.isNotEmpty()) { + append("?") + paramsMap.entries.forEach { entry -> + if (length > 1) { + append("&") + } + append(entry.key) + append("=") + append(URLEncoder.encode(entry.value, Charsets.UTF_8)) + } + } + } + + FetchChapterPagesPayload( + clientMutationId = clientMutationId, + pages = + List(chapter.pageCount) { index -> + "/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/${index}$params" + }, + chapter = ChapterType(chapter), + syncConflict = syncConflictInfo, + ) } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt index 57e5dfdf..7bb0fea5 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import graphql.execution.DataFetcherResult @@ -5,7 +7,6 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeout import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.graphql.types.DownloadStatus @@ -30,23 +31,21 @@ class DownloadMutation { ) @RequireAuth - fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DataFetcherResult { + fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DeleteDownloadedChaptersPayload? { val (clientMutationId, chapters) = input - return asDataFetcherResult { - Chapter.deleteChapters(chapters) + Chapter.deleteChapters(chapters) - DeleteDownloadedChaptersPayload( - clientMutationId = clientMutationId, - chapters = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.id inList chapters } - .map { ChapterType(it) } - }, - ) - } + return DeleteDownloadedChaptersPayload( + clientMutationId = clientMutationId, + chapters = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.id inList chapters } + .map { ChapterType(it) } + }, + ) } data class DeleteDownloadedChapterInput( @@ -60,20 +59,18 @@ class DownloadMutation { ) @RequireAuth - fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DataFetcherResult { + fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DeleteDownloadedChapterPayload? { val (clientMutationId, chapter) = input - return asDataFetcherResult { - Chapter.deleteChapters(listOf(chapter)) + Chapter.deleteChapters(listOf(chapter)) - DeleteDownloadedChapterPayload( - clientMutationId = clientMutationId, - chapters = - transaction { - ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq chapter }.first()) - }, - ) - } + return DeleteDownloadedChapterPayload( + clientMutationId = clientMutationId, + chapters = + transaction { + ChapterType(ChapterTable.selectAll().where { ChapterTable.id eq chapter }.first()) + }, + ) } data class EnqueueChapterDownloadsInput( @@ -87,28 +84,24 @@ class DownloadMutation { ) @RequireAuth - fun enqueueChapterDownloads( - input: EnqueueChapterDownloadsInput, - ): CompletableFuture> { + fun enqueueChapterDownloads(input: EnqueueChapterDownloadsInput): CompletableFuture { val (clientMutationId, chapters) = input return future { - asDataFetcherResult { - DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters)) + DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters)) - EnqueueChapterDownloadsPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { - DownloadManager.getStatus().queue.any { it.chapterId in chapters } - }.let { DownloadManager.getStatus() }, - ) - }, - ) - } + EnqueueChapterDownloadsPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { + DownloadManager.getStatus().queue.any { it.chapterId in chapters } + }.let { DownloadManager.getStatus() }, + ) + }, + ) } } @@ -123,25 +116,23 @@ class DownloadMutation { ) @RequireAuth - fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture> { + fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture { val (clientMutationId, chapter) = input return future { - asDataFetcherResult { - DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter))) + DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter))) - EnqueueChapterDownloadPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.updates.any { it.downloadQueueItem.chapterId == chapter } } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + EnqueueChapterDownloadPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.updates.any { it.downloadQueueItem.chapterId == chapter } } + .let { DownloadManager.getStatus() }, + ) + }, + ) } } @@ -156,30 +147,26 @@ class DownloadMutation { ) @RequireAuth - fun dequeueChapterDownloads( - input: DequeueChapterDownloadsInput, - ): CompletableFuture> { + fun dequeueChapterDownloads(input: DequeueChapterDownloadsInput): CompletableFuture { val (clientMutationId, chapters) = input return future { - asDataFetcherResult { - DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters)) + DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters)) - DequeueChapterDownloadsPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { - it.updates.any { - it.downloadQueueItem.chapterId in chapters && it.type == DEQUEUED - } - }.let { DownloadManager.getStatus() }, - ) - }, - ) - } + DequeueChapterDownloadsPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { + it.updates.any { + it.downloadQueueItem.chapterId in chapters && it.type == DEQUEUED + } + }.let { DownloadManager.getStatus() }, + ) + }, + ) } } @@ -194,28 +181,26 @@ class DownloadMutation { ) @RequireAuth - fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture> { + fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture { val (clientMutationId, chapter) = input return future { - asDataFetcherResult { - DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter))) + DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter))) - DequeueChapterDownloadPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { - it.updates.any { - it.downloadQueueItem.chapterId == chapter && it.type == DEQUEUED - } - }.let { DownloadManager.getStatus() }, - ) - }, - ) - } + DequeueChapterDownloadPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { + it.updates.any { + it.downloadQueueItem.chapterId == chapter && it.type == DEQUEUED + } + }.let { DownloadManager.getStatus() }, + ) + }, + ) } } @@ -229,23 +214,21 @@ class DownloadMutation { ) @RequireAuth - fun startDownloader(input: StartDownloaderInput): CompletableFuture> = + fun startDownloader(input: StartDownloaderInput): CompletableFuture = future { - asDataFetcherResult { - DownloadManager.start() + DownloadManager.start() - StartDownloaderPayload( - input.clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.status == Status.Started } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + StartDownloaderPayload( + input.clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.status == Status.Started } + .let { DownloadManager.getStatus() }, + ) + }, + ) } data class StopDownloaderInput( @@ -258,23 +241,21 @@ class DownloadMutation { ) @RequireAuth - fun stopDownloader(input: StopDownloaderInput): CompletableFuture> = + fun stopDownloader(input: StopDownloaderInput): CompletableFuture = future { - asDataFetcherResult { - DownloadManager.stop() + DownloadManager.stop() - StopDownloaderPayload( - input.clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.status == Status.Stopped } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + StopDownloaderPayload( + input.clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.status == Status.Stopped } + .let { DownloadManager.getStatus() }, + ) + }, + ) } data class ClearDownloaderInput( @@ -287,23 +268,21 @@ class DownloadMutation { ) @RequireAuth - fun clearDownloader(input: ClearDownloaderInput): CompletableFuture> = + fun clearDownloader(input: ClearDownloaderInput): CompletableFuture = future { - asDataFetcherResult { - DownloadManager.clear() + DownloadManager.clear() - ClearDownloaderPayload( - input.clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.status == Status.Stopped } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + ClearDownloaderPayload( + input.clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.status == Status.Stopped } + .let { DownloadManager.getStatus() }, + ) + }, + ) } data class ReorderChapterDownloadInput( @@ -318,25 +297,23 @@ class DownloadMutation { ) @RequireAuth - fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture> { + fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture { val (clientMutationId, chapter, to) = input return future { - asDataFetcherResult { - DownloadManager.reorder(chapter, to) + DownloadManager.reorder(chapter, to) - ReorderChapterDownloadPayload( - clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.updates - .first { it.updates.indexOfFirst { it.downloadQueueItem.chapterId == chapter } <= to } - .let { DownloadManager.getStatus() }, - ) - }, - ) - } + ReorderChapterDownloadPayload( + clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.updates + .first { it.updates.indexOfFirst { it.downloadQueueItem.chapterId == chapter } <= to } + .let { DownloadManager.getStatus() }, + ) + }, + ) } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt index b484890c..78965755 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import eu.kanade.tachiyomi.source.local.LocalSource @@ -5,7 +7,6 @@ import graphql.execution.DataFetcherResult import io.javalin.http.UploadedFile import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.ExtensionType import suwayomi.tachidesk.manga.impl.extension.Extension @@ -75,51 +76,47 @@ class ExtensionMutation { } @RequireAuth - fun updateExtension(input: UpdateExtensionInput): CompletableFuture> { + fun updateExtension(input: UpdateExtensionInput): CompletableFuture { val (clientMutationId, id, patch) = input return future { - asDataFetcherResult { - updateExtensions(listOf(id), patch) + updateExtensions(listOf(id), patch) - val extension = - transaction { - ExtensionTable - .selectAll() - .where { ExtensionTable.pkgName eq id } - .firstOrNull() - ?.let { ExtensionType(it) } - } + val extension = + transaction { + ExtensionTable + .selectAll() + .where { ExtensionTable.pkgName eq id } + .firstOrNull() + ?.let { ExtensionType(it) } + } - UpdateExtensionPayload( - clientMutationId = clientMutationId, - extension = extension, - ) - } + UpdateExtensionPayload( + clientMutationId = clientMutationId, + extension = extension, + ) } } @RequireAuth - fun updateExtensions(input: UpdateExtensionsInput): CompletableFuture> { + fun updateExtensions(input: UpdateExtensionsInput): CompletableFuture { val (clientMutationId, ids, patch) = input return future { - asDataFetcherResult { - updateExtensions(ids, patch) + updateExtensions(ids, patch) - val extensions = - transaction { - ExtensionTable - .selectAll() - .where { ExtensionTable.pkgName inList ids } - .map { ExtensionType(it) } - } + val extensions = + transaction { + ExtensionTable + .selectAll() + .where { ExtensionTable.pkgName inList ids } + .map { ExtensionType(it) } + } - UpdateExtensionsPayload( - clientMutationId = clientMutationId, - extensions = extensions, - ) - } + UpdateExtensionsPayload( + clientMutationId = clientMutationId, + extensions = extensions, + ) } } @@ -133,26 +130,24 @@ class ExtensionMutation { ) @RequireAuth - fun fetchExtensions(input: FetchExtensionsInput): CompletableFuture> { + fun fetchExtensions(input: FetchExtensionsInput): CompletableFuture { val (clientMutationId) = input return future { - asDataFetcherResult { - ExtensionsList.fetchExtensions() + ExtensionsList.fetchExtensions() - val extensions = - transaction { - ExtensionTable - .selectAll() - .where { ExtensionTable.name neq LocalSource.EXTENSION_NAME } - .map { ExtensionType(it) } - } + val extensions = + transaction { + ExtensionTable + .selectAll() + .where { ExtensionTable.name neq LocalSource.EXTENSION_NAME } + .map { ExtensionType(it) } + } - FetchExtensionsPayload( - clientMutationId = clientMutationId, - extensions = extensions, - ) - } + FetchExtensionsPayload( + clientMutationId = clientMutationId, + extensions = extensions, + ) } } @@ -167,23 +162,19 @@ class ExtensionMutation { ) @RequireAuth - fun installExternalExtension( - input: InstallExternalExtensionInput, - ): CompletableFuture> { + fun installExternalExtension(input: InstallExternalExtensionInput): CompletableFuture { val (clientMutationId, extensionFile) = input return future { - asDataFetcherResult { - Extension.installExternalExtension(extensionFile.content(), extensionFile.filename()) + Extension.installExternalExtension(extensionFile.content(), extensionFile.filename()) - val dbExtension = - transaction { ExtensionTable.selectAll().where { ExtensionTable.apkName eq extensionFile.filename() }.first() } + val dbExtension = + transaction { ExtensionTable.selectAll().where { ExtensionTable.apkName eq extensionFile.filename() }.first() } - InstallExternalExtensionPayload( - clientMutationId, - extension = ExtensionType(dbExtension), - ) - } + InstallExternalExtensionPayload( + clientMutationId, + extension = ExtensionType(dbExtension), + ) } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ImageMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ImageMutation.kt index f899fe86..bff20e71 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ImageMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ImageMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import suwayomi.tachidesk.graphql.directives.RequireAuth diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt index 519f6aa7..3735024f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt @@ -1,9 +1,10 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import graphql.execution.DataFetcherResult import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeout -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.UpdateState.DOWNLOADING import suwayomi.tachidesk.graphql.types.UpdateState.ERROR @@ -26,55 +27,51 @@ class InfoMutation { ) @RequireAuth - fun updateWebUI(input: WebUIUpdateInput): CompletableFuture> { + fun updateWebUI(input: WebUIUpdateInput): CompletableFuture { return future { - asDataFetcherResult { - withTimeout(30.seconds) { - if (WebInterfaceManager.status.value.state === DOWNLOADING) { - return@withTimeout WebUIUpdatePayload(input.clientMutationId, WebInterfaceManager.status.value) - } + withTimeout(30.seconds) { + if (WebInterfaceManager.status.value.state === DOWNLOADING) { + return@withTimeout WebUIUpdatePayload(input.clientMutationId, WebInterfaceManager.status.value) + } - val flavor = WebUIFlavor.current + val flavor = WebUIFlavor.current - val (version, updateAvailable) = WebInterfaceManager.isUpdateAvailable(flavor) + val (version, updateAvailable) = WebInterfaceManager.isUpdateAvailable(flavor) - if (!updateAvailable) { - val didUpdateCheckFail = version.isEmpty() + if (!updateAvailable) { + val didUpdateCheckFail = version.isEmpty() - return@withTimeout WebUIUpdatePayload( - input.clientMutationId, - WebInterfaceManager.getStatus(version, if (didUpdateCheckFail) ERROR else IDLE), - ) - } - try { - WebInterfaceManager.startDownloadInScope(flavor, version) - } catch (e: Exception) { - // ignore since we use the status anyway - } - - WebUIUpdatePayload( + return@withTimeout WebUIUpdatePayload( input.clientMutationId, - updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING }, + WebInterfaceManager.getStatus(version, if (didUpdateCheckFail) ERROR else IDLE), ) } + try { + WebInterfaceManager.startDownloadInScope(flavor, version) + } catch (e: Exception) { + // ignore since we use the status anyway + } + + WebUIUpdatePayload( + input.clientMutationId, + updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING }, + ) } } } @RequireAuth - fun resetWebUIUpdateStatus(): CompletableFuture> = + fun resetWebUIUpdateStatus(): CompletableFuture = future { - asDataFetcherResult { - withTimeout(30.seconds) { - val isUpdateFinished = WebInterfaceManager.status.value.state != DOWNLOADING - if (!isUpdateFinished) { - throw Exception("Status reset is not allowed during status \"$DOWNLOADING\"") - } - - WebInterfaceManager.resetStatus() - - WebInterfaceManager.status.first { it.state == IDLE } + withTimeout(30.seconds) { + val isUpdateFinished = WebInterfaceManager.status.value.state != DOWNLOADING + if (!isUpdateFinished) { + throw Exception("Status reset is not allowed during status \"$DOWNLOADING\"") } + + WebInterfaceManager.resetStatus() + + WebInterfaceManager.status.first { it.state == IDLE } } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/KoreaderSyncMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/KoreaderSyncMutation.kt index e1ae1e4c..d509a688 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/KoreaderSyncMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/KoreaderSyncMutation.kt @@ -1,10 +1,11 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import graphql.execution.DataFetcherResult import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.graphql.types.KoSyncConnectPayload @@ -62,26 +63,24 @@ class KoreaderSyncMutation { ) @RequireAuth - fun pushKoSyncProgress(input: PushKoSyncProgressInput): CompletableFuture> = + fun pushKoSyncProgress(input: PushKoSyncProgressInput): CompletableFuture = future { - asDataFetcherResult { - KoreaderSyncService.pushProgress(input.chapterId) + KoreaderSyncService.pushProgress(input.chapterId) - val chapter = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.id eq input.chapterId } - .firstOrNull() - ?.let { ChapterType(it) } - } + val chapter = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.id eq input.chapterId } + .firstOrNull() + ?.let { ChapterType(it) } + } - PushKoSyncProgressPayload( - clientMutationId = input.clientMutationId, - success = true, - chapter = chapter, - ) - } + PushKoSyncProgressPayload( + clientMutationId = input.clientMutationId, + success = true, + chapter = chapter, + ) } data class PullKoSyncProgressInput( @@ -96,45 +95,43 @@ class KoreaderSyncMutation { ) @RequireAuth - fun pullKoSyncProgress(input: PullKoSyncProgressInput): CompletableFuture> = + fun pullKoSyncProgress(input: PullKoSyncProgressInput): CompletableFuture = future { - asDataFetcherResult { - val syncResult = KoreaderSyncService.checkAndPullProgress(input.chapterId) - var syncConflictInfo: SyncConflictInfoType? = null + val syncResult = KoreaderSyncService.checkAndPullProgress(input.chapterId) + var syncConflictInfo: SyncConflictInfoType? = null - if (syncResult != null) { - if (syncResult.isConflict) { - syncConflictInfo = - SyncConflictInfoType( - deviceName = syncResult.device, - remotePage = syncResult.pageRead, - ) - } + if (syncResult != null) { + if (syncResult.isConflict) { + syncConflictInfo = + SyncConflictInfoType( + deviceName = syncResult.device, + remotePage = syncResult.pageRead, + ) + } - if (syncResult.shouldUpdate) { - transaction { - ChapterTable.update({ ChapterTable.id eq input.chapterId }) { - it[lastPageRead] = syncResult.pageRead - it[lastReadAt] = syncResult.timestamp - } + if (syncResult.shouldUpdate) { + transaction { + ChapterTable.update({ ChapterTable.id eq input.chapterId }) { + it[lastPageRead] = syncResult.pageRead + it[lastReadAt] = syncResult.timestamp } } } - - val chapter = - transaction { - ChapterTable - .selectAll() - .where { ChapterTable.id eq input.chapterId } - .firstOrNull() - ?.let { ChapterType(it) } - } - - PullKoSyncProgressPayload( - clientMutationId = input.clientMutationId, - chapter = chapter, - syncConflict = syncConflictInfo, - ) } + + val chapter = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.id eq input.chapterId } + .firstOrNull() + ?.let { ChapterType(it) } + } + + PullKoSyncProgressPayload( + clientMutationId = input.clientMutationId, + chapter = chapter, + syncConflict = syncConflictInfo, + ) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt index 175c790f..20df97bf 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt @@ -1,6 +1,7 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations -import graphql.execution.DataFetcherResult import org.jetbrains.exposed.sql.LikePattern import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq @@ -12,7 +13,6 @@ import org.jetbrains.exposed.sql.or import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.MangaMetaType import suwayomi.tachidesk.graphql.types.MangaType @@ -98,44 +98,40 @@ class MangaMutation { } @RequireAuth - fun updateManga(input: UpdateMangaInput): CompletableFuture> { + fun updateManga(input: UpdateMangaInput): CompletableFuture { val (clientMutationId, id, patch) = input return future { - asDataFetcherResult { - updateMangas(listOf(id), patch) + updateMangas(listOf(id), patch) - val manga = - transaction { - MangaType(MangaTable.selectAll().where { MangaTable.id eq id }.first()) - } + val manga = + transaction { + MangaType(MangaTable.selectAll().where { MangaTable.id eq id }.first()) + } - UpdateMangaPayload( - clientMutationId = clientMutationId, - manga = manga, - ) - } + UpdateMangaPayload( + clientMutationId = clientMutationId, + manga = manga, + ) } } @RequireAuth - fun updateMangas(input: UpdateMangasInput): CompletableFuture> { + fun updateMangas(input: UpdateMangasInput): CompletableFuture { val (clientMutationId, ids, patch) = input return future { - asDataFetcherResult { - updateMangas(ids, patch) + updateMangas(ids, patch) - val mangas = - transaction { - MangaTable.selectAll().where { MangaTable.id inList ids }.map { MangaType(it) } - } + val mangas = + transaction { + MangaTable.selectAll().where { MangaTable.id inList ids }.map { MangaType(it) } + } - UpdateMangasPayload( - clientMutationId = clientMutationId, - mangas = mangas, - ) - } + UpdateMangasPayload( + clientMutationId = clientMutationId, + mangas = mangas, + ) } } @@ -150,22 +146,20 @@ class MangaMutation { ) @RequireAuth - fun fetchManga(input: FetchMangaInput): CompletableFuture> { + fun fetchManga(input: FetchMangaInput): CompletableFuture { val (clientMutationId, id) = input return future { - asDataFetcherResult { - Manga.fetchManga(id) + Manga.fetchManga(id) - val manga = - transaction { - MangaTable.selectAll().where { MangaTable.id eq id }.first() - } - FetchMangaPayload( - clientMutationId = clientMutationId, - manga = MangaType(manga), - ) - } + val manga = + transaction { + MangaTable.selectAll().where { MangaTable.id eq id }.first() + } + FetchMangaPayload( + clientMutationId = clientMutationId, + manga = MangaType(manga), + ) } } @@ -180,14 +174,12 @@ class MangaMutation { ) @RequireAuth - fun setMangaMeta(input: SetMangaMetaInput): DataFetcherResult { + fun setMangaMeta(input: SetMangaMetaInput): SetMangaMetaPayload? { val (clientMutationId, meta) = input - return asDataFetcherResult { - Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value) + Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value) - SetMangaMetaPayload(clientMutationId, meta) - } + return SetMangaMetaPayload(clientMutationId, meta) } data class DeleteMangaMetaInput( @@ -203,34 +195,32 @@ class MangaMutation { ) @RequireAuth - fun deleteMangaMeta(input: DeleteMangaMetaInput): DataFetcherResult { + fun deleteMangaMeta(input: DeleteMangaMetaInput): DeleteMangaMetaPayload? { val (clientMutationId, mangaId, key) = input - return asDataFetcherResult { - val (meta, manga) = - transaction { - val meta = - MangaMetaTable - .selectAll() - .where { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } - .firstOrNull() + val (meta, manga) = + transaction { + val meta = + MangaMetaTable + .selectAll() + .where { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } + .firstOrNull() - MangaMetaTable.deleteWhere { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } + MangaMetaTable.deleteWhere { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } - val manga = - transaction { - MangaType(MangaTable.selectAll().where { MangaTable.id eq mangaId }.first()) - } + val manga = + transaction { + MangaType(MangaTable.selectAll().where { MangaTable.id eq mangaId }.first()) + } - if (meta != null) { - MangaMetaType(meta) - } else { - null - } to manga - } + if (meta != null) { + MangaMetaType(meta) + } else { + null + } to manga + } - DeleteMangaMetaPayload(clientMutationId, meta, manga) - } + return DeleteMangaMetaPayload(clientMutationId, meta, manga) } data class SetMangaMetasItem( @@ -250,43 +240,41 @@ class MangaMutation { ) @RequireAuth - fun setMangaMetas(input: SetMangaMetasInput): DataFetcherResult { + fun setMangaMetas(input: SetMangaMetasInput): SetMangaMetasPayload? { val (clientMutationId, items) = input - return asDataFetcherResult { - val metaByMangaId = - items - .flatMap { item -> - val metaMap = item.metas.associate { it.key to it.value } - item.mangaIds.map { mangaId -> mangaId to metaMap } - }.groupBy({ it.first }, { it.second }) - .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + val metaByMangaId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.mangaIds.map { mangaId -> mangaId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } - Manga.modifyMangasMetas(metaByMangaId) + Manga.modifyMangasMetas(metaByMangaId) - val allMangaIds = metaByMangaId.keys - val allMetaKeys = metaByMangaId.values.flatMap { it.keys }.distinct() + val allMangaIds = metaByMangaId.keys + val allMetaKeys = metaByMangaId.values.flatMap { it.keys }.distinct() - val (updatedMetas, mangas) = - transaction { - val updatedMetas = - MangaMetaTable - .selectAll() - .where { (MangaMetaTable.ref inList allMangaIds) and (MangaMetaTable.key inList allMetaKeys) } - .map { MangaMetaType(it) } + val (updatedMetas, mangas) = + transaction { + val updatedMetas = + MangaMetaTable + .selectAll() + .where { (MangaMetaTable.ref inList allMangaIds) and (MangaMetaTable.key inList allMetaKeys) } + .map { MangaMetaType(it) } - val mangas = - MangaTable - .selectAll() - .where { MangaTable.id inList allMangaIds } - .map { MangaType(it) } - .distinctBy { it.id } + val mangas = + MangaTable + .selectAll() + .where { MangaTable.id inList allMangaIds } + .map { MangaType(it) } + .distinctBy { it.id } - updatedMetas to mangas - } + updatedMetas to mangas + } - SetMangaMetasPayload(clientMutationId, updatedMetas, mangas) - } + return SetMangaMetasPayload(clientMutationId, updatedMetas, mangas) } data class DeleteMangaMetasItem( @@ -307,63 +295,61 @@ class MangaMutation { ) @RequireAuth - fun deleteMangaMetas(input: DeleteMangaMetasInput): DataFetcherResult { + fun deleteMangaMetas(input: DeleteMangaMetasInput): DeleteMangaMetasPayload? { val (clientMutationId, items) = input - return asDataFetcherResult { - items.forEach { item -> - require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided for each item" + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allMangaIds) = + transaction { + val deletedMetas = mutableListOf() + val mangaIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { MangaMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (MangaMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (MangaMetaTable.ref inList item.mangaIds) and metaKeyCondition + + deletedMetas += + MangaMetaTable + .selectAll() + .where { condition } + .map { MangaMetaType(it) } + + MangaMetaTable.deleteWhere { condition } + mangaIds += item.mangaIds } + + deletedMetas to mangaIds } - val (allDeletedMetas, allMangaIds) = - transaction { - val deletedMetas = mutableListOf() - val mangaIds = mutableSetOf() + val mangas = + transaction { + MangaTable + .selectAll() + .where { MangaTable.id inList allMangaIds } + .map { MangaType(it) } + .distinctBy { it.id } + } - items.forEach { item -> - val keyCondition: Op? = - item.keys?.takeIf { it.isNotEmpty() }?.let { MangaMetaTable.key inList it } - - val prefixCondition: Op? = - item.prefixes - ?.filter { it.isNotEmpty() } - ?.map { (MangaMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val metaKeyCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val condition = (MangaMetaTable.ref inList item.mangaIds) and metaKeyCondition - - deletedMetas += - MangaMetaTable - .selectAll() - .where { condition } - .map { MangaMetaType(it) } - - MangaMetaTable.deleteWhere { condition } - mangaIds += item.mangaIds - } - - deletedMetas to mangaIds - } - - val mangas = - transaction { - MangaTable - .selectAll() - .where { MangaTable.id inList allMangaIds } - .map { MangaType(it) } - .distinctBy { it.id } - } - - DeleteMangaMetasPayload(clientMutationId, allDeletedMetas, mangas) - } + return DeleteMangaMetasPayload(clientMutationId, allDeletedMetas, mangas) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt index f2926528..cb14d2e7 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import graphql.execution.DataFetcherResult @@ -12,7 +14,6 @@ import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.global.impl.GlobalMeta import suwayomi.tachidesk.global.model.table.GlobalMetaTable -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.GlobalMetaType import suwayomi.tachidesk.graphql.types.MetaInput @@ -29,14 +30,12 @@ class MetaMutation { ) @RequireAuth - fun setGlobalMeta(input: SetGlobalMetaInput): DataFetcherResult { + fun setGlobalMeta(input: SetGlobalMetaInput): SetGlobalMetaPayload? { val (clientMutationId, meta) = input - return asDataFetcherResult { - GlobalMeta.modifyMeta(meta.key, meta.value) + GlobalMeta.modifyMeta(meta.key, meta.value) - SetGlobalMetaPayload(clientMutationId, meta) - } + return SetGlobalMetaPayload(clientMutationId, meta) } data class DeleteGlobalMetaInput( @@ -50,29 +49,27 @@ class MetaMutation { ) @RequireAuth - fun deleteGlobalMeta(input: DeleteGlobalMetaInput): DataFetcherResult { + fun deleteGlobalMeta(input: DeleteGlobalMetaInput): DeleteGlobalMetaPayload? { val (clientMutationId, key) = input - return asDataFetcherResult { - val meta = - transaction { - val meta = - GlobalMetaTable - .selectAll() - .where { GlobalMetaTable.key eq key } - .firstOrNull() + val meta = + transaction { + val meta = + GlobalMetaTable + .selectAll() + .where { GlobalMetaTable.key eq key } + .firstOrNull() - GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key } + GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key } - if (meta != null) { - GlobalMetaType(meta) - } else { - null - } + if (meta != null) { + GlobalMetaType(meta) + } else { + null } + } - DeleteGlobalMetaPayload(clientMutationId, meta) - } + return DeleteGlobalMetaPayload(clientMutationId, meta) } data class SetGlobalMetasInput( @@ -86,23 +83,21 @@ class MetaMutation { ) @RequireAuth - fun setGlobalMetas(input: SetGlobalMetasInput): DataFetcherResult { + fun setGlobalMetas(input: SetGlobalMetasInput): SetGlobalMetasPayload? { val (clientMutationId, metas) = input - return asDataFetcherResult { - val metaMap = metas.associate { it.key to it.value } - GlobalMeta.modifyMetas(metaMap) + val metaMap = metas.associate { it.key to it.value } + GlobalMeta.modifyMetas(metaMap) - val updatedMetas = - transaction { - GlobalMetaTable - .selectAll() - .where { GlobalMetaTable.key inList metaMap.keys } - .map { GlobalMetaType(it) } - } + val updatedMetas = + transaction { + GlobalMetaTable + .selectAll() + .where { GlobalMetaTable.key inList metaMap.keys } + .map { GlobalMetaType(it) } + } - SetGlobalMetasPayload(clientMutationId, updatedMetas) - } + return SetGlobalMetasPayload(clientMutationId, updatedMetas) } data class DeleteGlobalMetasInput( @@ -117,43 +112,41 @@ class MetaMutation { ) @RequireAuth - fun deleteGlobalMetas(input: DeleteGlobalMetasInput): DataFetcherResult { + fun deleteGlobalMetas(input: DeleteGlobalMetasInput): DeleteGlobalMetasPayload? { val (clientMutationId, keys, prefixes) = input - return asDataFetcherResult { - require(!keys.isNullOrEmpty() || !prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided" + require(!keys.isNullOrEmpty() || !prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided" + } + + val metas = + transaction { + val keyCondition: Op? = keys?.takeIf { it.isNotEmpty() }?.let { GlobalMetaTable.key inList it } + + val prefixCondition: Op? = + prefixes + ?.filter { it.isNotEmpty() } + ?.map { (GlobalMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val finalCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val metas = + GlobalMetaTable + .selectAll() + .where { finalCondition } + .map { GlobalMetaType(it) } + + GlobalMetaTable.deleteWhere { finalCondition } + + metas } - val metas = - transaction { - val keyCondition: Op? = keys?.takeIf { it.isNotEmpty() }?.let { GlobalMetaTable.key inList it } - - val prefixCondition: Op? = - prefixes - ?.filter { it.isNotEmpty() } - ?.map { (GlobalMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val finalCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val metas = - GlobalMetaTable - .selectAll() - .where { finalCondition } - .map { GlobalMetaType(it) } - - GlobalMetaTable.deleteWhere { finalCondition } - - metas - } - - DeleteGlobalMetasPayload(clientMutationId, metas) - } + return DeleteGlobalMetasPayload(clientMutationId, metas) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt index 7a97f95c..42244be1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.generator.annotations.GraphQLIgnore diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt index c9b619e7..62b83906 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import androidx.preference.CheckBoxPreference @@ -5,7 +7,6 @@ import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.MultiSelectListPreference import androidx.preference.SwitchPreferenceCompat -import graphql.execution.DataFetcherResult import org.jetbrains.exposed.sql.LikePattern import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq @@ -16,7 +17,6 @@ import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.or import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.FilterChange import suwayomi.tachidesk.graphql.types.MangaType @@ -47,14 +47,12 @@ class SourceMutation { ) @RequireAuth - fun setSourceMeta(input: SetSourceMetaInput): DataFetcherResult { + fun setSourceMeta(input: SetSourceMetaInput): SetSourceMetaPayload? { val (clientMutationId, meta) = input - return asDataFetcherResult { - Source.modifyMeta(meta.sourceId, meta.key, meta.value) + Source.modifyMeta(meta.sourceId, meta.key, meta.value) - SetSourceMetaPayload(clientMutationId, meta) - } + return SetSourceMetaPayload(clientMutationId, meta) } data class DeleteSourceMetaInput( @@ -70,38 +68,36 @@ class SourceMutation { ) @RequireAuth - fun deleteSourceMeta(input: DeleteSourceMetaInput): DataFetcherResult { + fun deleteSourceMeta(input: DeleteSourceMetaInput): DeleteSourceMetaPayload? { val (clientMutationId, sourceId, key) = input - return asDataFetcherResult { - val (meta, source) = - transaction { - val meta = - SourceMetaTable + val (meta, source) = + transaction { + val meta = + SourceMetaTable + .selectAll() + .where { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + .firstOrNull() + + SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + + val source = + transaction { + SourceTable .selectAll() - .where { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + .where { SourceTable.id eq sourceId } .firstOrNull() + ?.let { SourceType(it) } + } - SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + if (meta != null) { + SourceMetaType(meta) + } else { + null + } to source + } - val source = - transaction { - SourceTable - .selectAll() - .where { SourceTable.id eq sourceId } - .firstOrNull() - ?.let { SourceType(it) } - } - - if (meta != null) { - SourceMetaType(meta) - } else { - null - } to source - } - - DeleteSourceMetaPayload(clientMutationId, meta, source) - } + return DeleteSourceMetaPayload(clientMutationId, meta, source) } data class SetSourceMetasItem( @@ -121,43 +117,41 @@ class SourceMutation { ) @RequireAuth - fun setSourceMetas(input: SetSourceMetasInput): DataFetcherResult { + fun setSourceMetas(input: SetSourceMetasInput): SetSourceMetasPayload? { val (clientMutationId, items) = input - return asDataFetcherResult { - val metaBySourceId = - items - .flatMap { item -> - val metaMap = item.metas.associate { it.key to it.value } - item.sourceIds.map { sourceId -> sourceId to metaMap } - }.groupBy({ it.first }, { it.second }) - .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + val metaBySourceId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.sourceIds.map { sourceId -> sourceId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } - Source.modifySourceMetas(metaBySourceId) + Source.modifySourceMetas(metaBySourceId) - val allSourceIds = metaBySourceId.keys - val allMetaKeys = metaBySourceId.values.flatMap { it.keys }.distinct() + val allSourceIds = metaBySourceId.keys + val allMetaKeys = metaBySourceId.values.flatMap { it.keys }.distinct() - val (updatedMetas, sources) = - transaction { - val updatedMetas = - SourceMetaTable - .selectAll() - .where { (SourceMetaTable.ref inList allSourceIds) and (SourceMetaTable.key inList allMetaKeys) } - .map { SourceMetaType(it) } + val (updatedMetas, sources) = + transaction { + val updatedMetas = + SourceMetaTable + .selectAll() + .where { (SourceMetaTable.ref inList allSourceIds) and (SourceMetaTable.key inList allMetaKeys) } + .map { SourceMetaType(it) } - val sources = - SourceTable - .selectAll() - .where { SourceTable.id inList allSourceIds } - .mapNotNull { SourceType(it) } - .distinctBy { it.id } + val sources = + SourceTable + .selectAll() + .where { SourceTable.id inList allSourceIds } + .mapNotNull { SourceType(it) } + .distinctBy { it.id } - updatedMetas to sources - } + updatedMetas to sources + } - SetSourceMetasPayload(clientMutationId, updatedMetas, sources) - } + return SetSourceMetasPayload(clientMutationId, updatedMetas, sources) } data class DeleteSourceMetasItem( @@ -178,64 +172,62 @@ class SourceMutation { ) @RequireAuth - fun deleteSourceMetas(input: DeleteSourceMetasInput): DataFetcherResult { + fun deleteSourceMetas(input: DeleteSourceMetasInput): DeleteSourceMetasPayload? { val (clientMutationId, items) = input - return asDataFetcherResult { - items.forEach { item -> - require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { - "Either 'keys' or 'prefixes' must be provided for each item" + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allSourceIds) = + transaction { + val deletedMetas = mutableListOf() + val sourceIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { SourceMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (SourceMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (SourceMetaTable.ref inList item.sourceIds) and metaKeyCondition + + deletedMetas += + SourceMetaTable + .selectAll() + .where { condition } + .map { SourceMetaType(it) } + + SourceMetaTable.deleteWhere { condition } + sourceIds += item.sourceIds } + + deletedMetas to sourceIds } - val (allDeletedMetas, allSourceIds) = - transaction { - val deletedMetas = mutableListOf() - val sourceIds = mutableSetOf() + val sources = + transaction { + SourceTable + .selectAll() + .where { SourceTable.id inList allSourceIds } + .mapNotNull { SourceType(it) } + .distinctBy { it.id } + } - items.forEach { item -> - val keyCondition: Op? = - item.keys?.takeIf { it.isNotEmpty() }?.let { SourceMetaTable.key inList it } - - val prefixCondition: Op? = - item.prefixes - ?.filter { it.isNotEmpty() } - ?.map { (SourceMetaTable.key like LikePattern("$it%")) as Op } - ?.reduceOrNull { acc, op -> acc or op } - - val metaKeyCondition = - if (keyCondition != null && prefixCondition != null) { - keyCondition or prefixCondition - } else { - keyCondition ?: prefixCondition!! - } - - val condition = (SourceMetaTable.ref inList item.sourceIds) and metaKeyCondition - - deletedMetas += - SourceMetaTable - .selectAll() - .where { condition } - .map { SourceMetaType(it) } - - SourceMetaTable.deleteWhere { condition } - sourceIds += item.sourceIds - } - - deletedMetas to sourceIds - } - - val sources = - transaction { - SourceTable - .selectAll() - .where { SourceTable.id inList allSourceIds } - .mapNotNull { SourceType(it) } - .distinctBy { it.id } - } - - DeleteSourceMetasPayload(clientMutationId, allDeletedMetas, sources) - } + return DeleteSourceMetasPayload(clientMutationId, allDeletedMetas, sources) } enum class FetchSourceMangaType { @@ -260,50 +252,48 @@ class SourceMutation { ) @RequireAuth - fun fetchSourceManga(input: FetchSourceMangaInput): CompletableFuture> { + fun fetchSourceManga(input: FetchSourceMangaInput): CompletableFuture { val (clientMutationId, sourceId, type, page, query, filters) = input return future { - asDataFetcherResult { - val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!! - val mangasPage = - when (type) { - FetchSourceMangaType.SEARCH -> { - source.getSearchManga( - page = page, - query = query.orEmpty(), - filters = updateFilterList(source, filters), - ) - } - - FetchSourceMangaType.POPULAR -> { - source.getPopularManga(page) - } - - FetchSourceMangaType.LATEST -> { - if (!source.supportsLatest) throw Exception("Source does not support latest") - source.getLatestUpdates(page) - } + val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!! + val mangasPage = + when (type) { + FetchSourceMangaType.SEARCH -> { + source.getSearchManga( + page = page, + query = query.orEmpty(), + filters = updateFilterList(source, filters), + ) } - val mangaIds = mangasPage.insertOrUpdate(sourceId) - - val mangas = - transaction { - MangaTable - .selectAll() - .where { MangaTable.id inList mangaIds } - .map { MangaType(it) } - }.sortedBy { - mangaIds.indexOf(it.id) + FetchSourceMangaType.POPULAR -> { + source.getPopularManga(page) } - FetchSourceMangaPayload( - clientMutationId = clientMutationId, - mangas = mangas, - hasNextPage = mangasPage.hasNextPage, - ) - } + FetchSourceMangaType.LATEST -> { + if (!source.supportsLatest) throw Exception("Source does not support latest") + source.getLatestUpdates(page) + } + } + + val mangaIds = mangasPage.insertOrUpdate(sourceId) + + val mangas = + transaction { + MangaTable + .selectAll() + .where { MangaTable.id inList mangaIds } + .map { MangaType(it) } + }.sortedBy { + mangaIds.indexOf(it.id) + } + + FetchSourceMangaPayload( + clientMutationId = clientMutationId, + mangas = mangas, + hasNextPage = mangasPage.hasNextPage, + ) } } @@ -329,29 +319,27 @@ class SourceMutation { ) @RequireAuth - fun updateSourcePreference(input: UpdateSourcePreferenceInput): DataFetcherResult { + fun updateSourcePreference(input: UpdateSourcePreferenceInput): UpdateSourcePreferencePayload? { val (clientMutationId, sourceId, change) = input - return asDataFetcherResult { - Source.setSourcePreference(sourceId, change.position, "") { preference -> - when (preference) { - is SwitchPreferenceCompat -> change.switchState - is CheckBoxPreference -> change.checkBoxState - is EditTextPreference -> change.editTextState - is ListPreference -> change.listState - is MultiSelectListPreference -> change.multiSelectState?.toSet() - else -> throw RuntimeException("sealed class cannot have more subtypes!") - } ?: throw Exception("Expected change to ${preference::class.simpleName}") - } - - UpdateSourcePreferencePayload( - clientMutationId = clientMutationId, - preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) }, - source = - transaction { - SourceType(SourceTable.selectAll().where { SourceTable.id eq sourceId }.first())!! - }, - ) + Source.setSourcePreference(sourceId, change.position, "") { preference -> + when (preference) { + is SwitchPreferenceCompat -> change.switchState + is CheckBoxPreference -> change.checkBoxState + is EditTextPreference -> change.editTextState + is ListPreference -> change.listState + is MultiSelectListPreference -> change.multiSelectState?.toSet() + else -> throw RuntimeException("sealed class cannot have more subtypes!") + } ?: throw Exception("Expected change to ${preference::class.simpleName}") } + + return UpdateSourcePreferencePayload( + clientMutationId = clientMutationId, + preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) }, + source = + transaction { + SourceType(SourceTable.selectAll().where { SourceTable.id eq sourceId }.first())!! + }, + ) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt index ac618133..41e06dde 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated @@ -6,7 +8,6 @@ import graphql.execution.DataFetcherResult import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.TrackRecordType import suwayomi.tachidesk.graphql.types.TrackerType @@ -222,24 +223,22 @@ class TrackMutation { ) @RequireAuth - fun trackProgress(input: TrackProgressInput): CompletableFuture> { + fun trackProgress(input: TrackProgressInput): CompletableFuture { val (clientMutationId, mangaId) = input return future { - asDataFetcherResult { - Track.trackChapter(mangaId) - val trackRecords = - transaction { - TrackRecordTable - .selectAll() - .where { TrackRecordTable.mangaId eq mangaId } - .toList() - } - TrackProgressPayload( - clientMutationId, - trackRecords.map { TrackRecordType(it) }, - ) - } + Track.trackChapter(mangaId) + val trackRecords = + transaction { + TrackRecordTable + .selectAll() + .where { TrackRecordTable.mangaId eq mangaId } + .toList() + } + TrackProgressPayload( + clientMutationId, + trackRecords.map { TrackRecordType(it) }, + ) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt index d8cbaa76..4f668362 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt @@ -1,9 +1,10 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import graphql.execution.DataFetcherResult import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeout -import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.LibraryUpdateStatus import suwayomi.tachidesk.graphql.types.UpdateStatus @@ -28,7 +29,7 @@ class UpdateMutation { ) @RequireAuth - fun updateLibrary(input: UpdateLibraryInput): CompletableFuture> { + fun updateLibrary(input: UpdateLibraryInput): CompletableFuture { updater.addCategoriesToUpdateQueue( Category.getCategoryList().filter { input.categories?.contains(it.id) ?: true }, clear = true, @@ -36,17 +37,15 @@ class UpdateMutation { ) return future { - asDataFetcherResult { - UpdateLibraryPayload( - input.clientMutationId, - updateStatus = - withTimeout(30.seconds) { - LibraryUpdateStatus( - updater.updates.first(), - ) - }, - ) - } + UpdateLibraryPayload( + input.clientMutationId, + updateStatus = + withTimeout(30.seconds) { + LibraryUpdateStatus( + updater.updates.first(), + ) + }, + ) } } @@ -60,7 +59,7 @@ class UpdateMutation { ) @RequireAuth - fun updateLibraryManga(input: UpdateLibraryMangaInput): CompletableFuture> { + fun updateLibraryManga(input: UpdateLibraryMangaInput): CompletableFuture { updateLibrary( UpdateLibraryInput( clientMutationId = input.clientMutationId, @@ -69,15 +68,13 @@ class UpdateMutation { ) return future { - asDataFetcherResult { - UpdateLibraryMangaPayload( - input.clientMutationId, - updateStatus = - withTimeout(30.seconds) { - UpdateStatus(updater.status.first()) - }, - ) - } + UpdateLibraryMangaPayload( + input.clientMutationId, + updateStatus = + withTimeout(30.seconds) { + UpdateStatus(updater.status.first()) + }, + ) } } @@ -92,7 +89,7 @@ class UpdateMutation { ) @RequireAuth - fun updateCategoryManga(input: UpdateCategoryMangaInput): CompletableFuture> { + fun updateCategoryManga(input: UpdateCategoryMangaInput): CompletableFuture { updateLibrary( UpdateLibraryInput( clientMutationId = input.clientMutationId, @@ -101,15 +98,13 @@ class UpdateMutation { ) return future { - asDataFetcherResult { - UpdateCategoryMangaPayload( - input.clientMutationId, - updateStatus = - withTimeout(30.seconds) { - UpdateStatus(updater.status.first()) - }, - ) - } + UpdateCategoryMangaPayload( + input.clientMutationId, + updateStatus = + withTimeout(30.seconds) { + UpdateStatus(updater.status.first()) + }, + ) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UserMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UserMutation.kt index af0f86dc..20f640c9 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UserMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UserMutation.kt @@ -1,3 +1,5 @@ +@file:Suppress("RedundantNullableReturnType", "unused") + package suwayomi.tachidesk.graphql.mutations import graphql.schema.DataFetchingEnvironment diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt index e8fefcb9..f07d9962 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt @@ -11,6 +11,7 @@ import com.expediagroup.graphql.server.execution.GraphQLRequestParser import com.expediagroup.graphql.server.types.GraphQLBatchRequest import com.expediagroup.graphql.server.types.GraphQLRequest import com.expediagroup.graphql.server.types.GraphQLServerRequest +import io.github.oshai.kotlinlogging.KotlinLogging import io.javalin.http.Context import io.javalin.http.UploadedFile import io.javalin.json.JavalinJackson @@ -19,11 +20,12 @@ import io.javalin.json.fromJsonString import java.io.IOException class JavalinGraphQLRequestParser : GraphQLRequestParser { - val jsonMapper = JavalinJackson() + private val logger = KotlinLogging.logger {} @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") override suspend fun parseRequest(context: Context): GraphQLServerRequest? { return try { + val jsonMapper = context.jsonMapper() val contentType = context.contentType() val formParam = if ( @@ -77,7 +79,8 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser { ) } } - } catch (_: IOException) { + } catch (e: IOException) { + logger.error(e) { "Error when parsing request" } null } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt index fbfd6945..0b640b5e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt @@ -10,7 +10,6 @@ package suwayomi.tachidesk.graphql.server import com.expediagroup.graphql.generator.execution.FlowSubscriptionExecutionStrategy import com.expediagroup.graphql.server.execution.GraphQLRequestHandler import com.expediagroup.graphql.server.execution.GraphQLServer -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import graphql.ExceptionWhileDataFetching import graphql.GraphQL import graphql.execution.AsyncExecutionStrategy @@ -27,6 +26,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import suwayomi.tachidesk.graphql.server.subscriptions.ApolloSubscriptionProtocolHandler import suwayomi.tachidesk.server.JavalinSetup.future +import tools.jackson.module.kotlin.jacksonObjectMapper class TachideskGraphQLServer( requestParser: JavalinGraphQLRequestParser, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/Cursor.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/Cursor.kt index 413c23dd..7029663c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/Cursor.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/Cursor.kt @@ -58,7 +58,7 @@ private class GraphqlCursorCoercing : Coercing { ), ) } - return Cursor(input.value) + return Cursor(input.value!!) } private fun valueToLiteralImpl(input: Any): StringValue = StringValue.newStringValue(input.toString()).build() diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt index 3469900a..633ed85b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt @@ -71,7 +71,7 @@ private class GraphqlDurationAsStringCoercing : Coercing { ) } return try { - Duration.parse(input.value) + Duration.parse(input.value!!) } catch (e: IllegalArgumentException) { throw CoercingParseLiteralException( "Invalid duration format: ${input.value}. Expected ISO-8601 duration string (e.g., 'PT30M', 'P1D')", diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/LongAsString.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/LongAsString.kt index 651c4fb8..6563c08f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/LongAsString.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/LongAsString.kt @@ -53,7 +53,7 @@ private class GraphqlLongAsStringCoercing : Coercing { ), ) } - return input.value.toLong() + return input.value!!.toLong() } private fun valueToLiteralImpl(input: Any): StringValue = StringValue.newStringValue(input.toString()).build() diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt index aea05df0..72ec0b7d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt @@ -9,9 +9,6 @@ package suwayomi.tachidesk.graphql.server.subscriptions import com.expediagroup.graphql.server.execution.GraphQLRequestHandler import com.expediagroup.graphql.server.types.GraphQLRequest -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.convertValue -import com.fasterxml.jackson.module.kotlin.readValue import io.github.oshai.kotlinlogging.KotlinLogging import io.javalin.http.Header import io.javalin.websocket.WsContext @@ -41,6 +38,9 @@ import suwayomi.tachidesk.server.JavalinSetup.Attribute import suwayomi.tachidesk.server.JavalinSetup.getAttributeOrSet import suwayomi.tachidesk.server.user.UserType import suwayomi.tachidesk.server.user.getUserFromToken +import tools.jackson.databind.ObjectMapper +import tools.jackson.module.kotlin.convertValue +import tools.jackson.module.kotlin.readValue /** * Implementation of the `graphql-transport-ws` protocol defined by Denis Badurina