diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt new file mode 100644 index 00000000..76fdeb8e --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt @@ -0,0 +1,25 @@ +package suwayomi.tachidesk.graphql + +import com.expediagroup.graphql.server.extensions.toGraphQLError +import graphql.execution.DataFetcherResult +import mu.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/mutations/CategoryMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt index 50c15a27..25810623 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt @@ -1,5 +1,6 @@ package suwayomi.tachidesk.graphql.mutations +import graphql.execution.DataFetcherResult import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList import org.jetbrains.exposed.sql.SqlExpressionBuilder.minus @@ -12,6 +13,7 @@ import org.jetbrains.exposed.sql.select 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.types.CategoryMetaType import suwayomi.tachidesk.graphql.types.CategoryType import suwayomi.tachidesk.graphql.types.MangaType @@ -36,12 +38,14 @@ class CategoryMutation { val meta: CategoryMetaType, ) - fun setCategoryMeta(input: SetCategoryMetaInput): SetCategoryMetaPayload { - val (clientMutationId, meta) = input + fun setCategoryMeta(input: SetCategoryMetaInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, meta) = input - Category.modifyMeta(meta.categoryId, meta.key, meta.value) + Category.modifyMeta(meta.categoryId, meta.key, meta.value) - return SetCategoryMetaPayload(clientMutationId, meta) + SetCategoryMetaPayload(clientMutationId, meta) + } } data class DeleteCategoryMetaInput( @@ -56,30 +60,32 @@ class CategoryMutation { val category: CategoryType, ) - fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DeleteCategoryMetaPayload { - val (clientMutationId, categoryId, key) = input + fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, categoryId, key) = input - val (meta, category) = - transaction { - val meta = - CategoryMetaTable.select { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) } - .firstOrNull() + val (meta, category) = + transaction { + val meta = + CategoryMetaTable.select { (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.select { CategoryTable.id eq categoryId }.first()) - } + val category = + transaction { + CategoryType(CategoryTable.select { CategoryTable.id eq categoryId }.first()) + } - if (meta != null) { - CategoryMetaType(meta) - } else { - null - } to category - } + if (meta != null) { + CategoryMetaType(meta) + } else { + null + } to category + } - return DeleteCategoryMetaPayload(clientMutationId, meta, category) + DeleteCategoryMetaPayload(clientMutationId, meta, category) + } } data class UpdateCategoryPatch( @@ -147,36 +153,40 @@ class CategoryMutation { } } - fun updateCategory(input: UpdateCategoryInput): UpdateCategoryPayload { - val (clientMutationId, id, patch) = input + fun updateCategory(input: UpdateCategoryInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, id, patch) = input - updateCategories(listOf(id), patch) + updateCategories(listOf(id), patch) - val category = - transaction { - CategoryType(CategoryTable.select { CategoryTable.id eq id }.first()) - } + val category = + transaction { + CategoryType(CategoryTable.select { CategoryTable.id eq id }.first()) + } - return UpdateCategoryPayload( - clientMutationId = clientMutationId, - category = category, - ) + UpdateCategoryPayload( + clientMutationId = clientMutationId, + category = category, + ) + } } - fun updateCategories(input: UpdateCategoriesInput): UpdateCategoriesPayload { - val (clientMutationId, ids, patch) = input + fun updateCategories(input: UpdateCategoriesInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, ids, patch) = input - updateCategories(ids, patch) + updateCategories(ids, patch) - val categories = - transaction { - CategoryTable.select { CategoryTable.id inList ids }.map { CategoryType(it) } - } + val categories = + transaction { + CategoryTable.select { CategoryTable.id inList ids }.map { CategoryType(it) } + } - return UpdateCategoriesPayload( - clientMutationId = clientMutationId, - categories = categories, - ) + UpdateCategoriesPayload( + clientMutationId = clientMutationId, + categories = categories, + ) + } } data class UpdateCategoryOrderPayload( @@ -190,46 +200,48 @@ class CategoryMutation { val position: Int, ) - fun updateCategoryOrder(input: UpdateCategoryOrderInput): UpdateCategoryOrderPayload { - val (clientMutationId, categoryId, position) = input - require(position > 0) { - "'order' must not be <= 0" - } - - transaction { - val currentOrder = - CategoryTable - .select { 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 - } + fun updateCategoryOrder(input: UpdateCategoryOrderInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, categoryId, position) = input + require(position > 0) { + "'order' must not be <= 0" } - } - Category.normalizeCategories() - - val categories = transaction { - CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) } + val currentOrder = + CategoryTable + .select { 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 + } + } } - return UpdateCategoryOrderPayload( - clientMutationId = clientMutationId, - categories = categories, - ) + Category.normalizeCategories() + + val categories = + transaction { + CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) } + } + + UpdateCategoryOrderPayload( + clientMutationId = clientMutationId, + categories = categories, + ) + } } data class CreateCategoryInput( @@ -246,51 +258,53 @@ class CategoryMutation { val category: CategoryType, ) - fun createCategory(input: CreateCategoryInput): CreateCategoryPayload { - val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input - transaction { - require(CategoryTable.select { 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" - } - } - - val category = + fun createCategory(input: CreateCategoryInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input transaction { - if (order != null) { - CategoryTable.update({ CategoryTable.order greaterEq order }) { - it[CategoryTable.order] = CategoryTable.order + 1 + require(CategoryTable.select { 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" + } + } + + 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 + } + } + + Category.normalizeCategories() + + CategoryType(CategoryTable.select { CategoryTable.id eq id }.first()) } - 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() - - CategoryType(CategoryTable.select { CategoryTable.id eq id }.first()) - } - - return CreateCategoryPayload(clientMutationId, category) + CreateCategoryPayload(clientMutationId, category) + } } data class DeleteCategoryInput( @@ -304,41 +318,43 @@ class CategoryMutation { val mangas: List, ) - 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.select { CategoryTable.id eq categoryId } - .firstOrNull() - - val mangas = - transaction { - MangaTable.innerJoin(CategoryMangaTable) - .select { CategoryMangaTable.category eq categoryId } - .map { MangaType(it) } - } - - CategoryTable.deleteWhere { CategoryTable.id eq categoryId } - - Category.normalizeCategories() - - if (category != null) { - CategoryType(category) - } else { - null - } to mangas + 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(), + ) } - return DeleteCategoryPayload(clientMutationId, category, mangas) + val (category, mangas) = + transaction { + val category = + CategoryTable.select { CategoryTable.id eq categoryId } + .firstOrNull() + + val mangas = + transaction { + MangaTable.innerJoin(CategoryMangaTable) + .select { 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) + } } data class UpdateMangaCategoriesPatch( @@ -406,35 +422,39 @@ class CategoryMutation { } } - fun updateMangaCategories(input: UpdateMangaCategoriesInput): UpdateMangaCategoriesPayload { - val (clientMutationId, id, patch) = input + fun updateMangaCategories(input: UpdateMangaCategoriesInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, id, patch) = input - updateMangas(listOf(id), patch) + updateMangas(listOf(id), patch) - val manga = - transaction { - MangaType(MangaTable.select { MangaTable.id eq id }.first()) - } + val manga = + transaction { + MangaType(MangaTable.select { MangaTable.id eq id }.first()) + } - return UpdateMangaCategoriesPayload( - clientMutationId = clientMutationId, - manga = manga, - ) + UpdateMangaCategoriesPayload( + clientMutationId = clientMutationId, + manga = manga, + ) + } } - fun updateMangasCategories(input: UpdateMangasCategoriesInput): UpdateMangasCategoriesPayload { - val (clientMutationId, ids, patch) = input + fun updateMangasCategories(input: UpdateMangasCategoriesInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, ids, patch) = input - updateMangas(ids, patch) + updateMangas(ids, patch) - val mangas = - transaction { - MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) } - } + val mangas = + transaction { + MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) } + } - return UpdateMangasCategoriesPayload( - clientMutationId = clientMutationId, - mangas = mangas, - ) + 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 76c1a51e..adab0609 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt @@ -1,5 +1,6 @@ package suwayomi.tachidesk.graphql.mutations +import graphql.execution.DataFetcherResult import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.and @@ -7,6 +8,7 @@ import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.statements.BatchUpdateStatement import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.types.ChapterMetaType import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.manga.impl.Chapter @@ -89,36 +91,40 @@ class ChapterMutation { } } - fun updateChapter(input: UpdateChapterInput): UpdateChapterPayload { - val (clientMutationId, id, patch) = input + fun updateChapter(input: UpdateChapterInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, id, patch) = input - updateChapters(listOf(id), patch) + updateChapters(listOf(id), patch) - val chapter = - transaction { - ChapterType(ChapterTable.select { ChapterTable.id eq id }.first()) - } + val chapter = + transaction { + ChapterType(ChapterTable.select { ChapterTable.id eq id }.first()) + } - return UpdateChapterPayload( - clientMutationId = clientMutationId, - chapter = chapter, - ) + UpdateChapterPayload( + clientMutationId = clientMutationId, + chapter = chapter, + ) + } } - fun updateChapters(input: UpdateChaptersInput): UpdateChaptersPayload { - val (clientMutationId, ids, patch) = input + fun updateChapters(input: UpdateChaptersInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, ids, patch) = input - updateChapters(ids, patch) + updateChapters(ids, patch) - val chapters = - transaction { - ChapterTable.select { ChapterTable.id inList ids }.map { ChapterType(it) } - } + val chapters = + transaction { + ChapterTable.select { ChapterTable.id inList ids }.map { ChapterType(it) } + } - return UpdateChaptersPayload( - clientMutationId = clientMutationId, - chapters = chapters, - ) + UpdateChaptersPayload( + clientMutationId = clientMutationId, + chapters = chapters, + ) + } } data class FetchChaptersInput( @@ -131,23 +137,25 @@ class ChapterMutation { val chapters: List, ) - fun fetchChapters(input: FetchChaptersInput): CompletableFuture { + fun fetchChapters(input: FetchChaptersInput): CompletableFuture> { val (clientMutationId, mangaId) = input return future { - Chapter.fetchChapterList(mangaId) - }.thenApply { - val chapters = - transaction { - ChapterTable.select { ChapterTable.manga eq mangaId } - .orderBy(ChapterTable.sourceOrder) - .map { ChapterType(it) } - } + asDataFetcherResult { + Chapter.fetchChapterList(mangaId) - FetchChaptersPayload( - clientMutationId = clientMutationId, - chapters = chapters, - ) + val chapters = + transaction { + ChapterTable.select { ChapterTable.manga eq mangaId } + .orderBy(ChapterTable.sourceOrder) + .map { ChapterType(it) } + } + + FetchChaptersPayload( + clientMutationId = clientMutationId, + chapters = chapters, + ) + } } } @@ -161,12 +169,14 @@ class ChapterMutation { val meta: ChapterMetaType, ) - fun setChapterMeta(input: SetChapterMetaInput): SetChapterMetaPayload { - val (clientMutationId, meta) = input + fun setChapterMeta(input: SetChapterMetaInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, meta) = input - Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value) + Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value) - return SetChapterMetaPayload(clientMutationId, meta) + SetChapterMetaPayload(clientMutationId, meta) + } } data class DeleteChapterMetaInput( @@ -181,30 +191,32 @@ class ChapterMutation { val chapter: ChapterType, ) - fun deleteChapterMeta(input: DeleteChapterMetaInput): DeleteChapterMetaPayload { - val (clientMutationId, chapterId, key) = input + fun deleteChapterMeta(input: DeleteChapterMetaInput): DataFetcherResult { + return asDataFetcherResult { + val (clientMutationId, chapterId, key) = input - val (meta, chapter) = - transaction { - val meta = - ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } - .firstOrNull() + val (meta, chapter) = + transaction { + val meta = + ChapterMetaTable.select { (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.select { ChapterTable.id eq chapterId }.first()) - } + val chapter = + transaction { + ChapterType(ChapterTable.select { ChapterTable.id eq chapterId }.first()) + } - if (meta != null) { - ChapterMetaType(meta) - } else { - null - } to chapter - } + if (meta != null) { + ChapterMetaType(meta) + } else { + null + } to chapter + } - return DeleteChapterMetaPayload(clientMutationId, meta, chapter) + DeleteChapterMetaPayload(clientMutationId, meta, chapter) + } } data class FetchChapterPagesInput( @@ -218,20 +230,22 @@ class ChapterMutation { val chapter: ChapterType, ) - fun fetchChapterPages(input: FetchChapterPagesInput): CompletableFuture { + fun fetchChapterPages(input: FetchChapterPagesInput): CompletableFuture> { val (clientMutationId, chapterId) = input return future { - getChapterDownloadReadyById(chapterId) - }.thenApply { chapter -> - FetchChapterPagesPayload( - clientMutationId = clientMutationId, - pages = - List(chapter.pageCount) { index -> - "/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/$index" - }, - chapter = ChapterType(chapter), - ) + asDataFetcherResult { + val chapter = getChapterDownloadReadyById(chapterId) + + FetchChapterPagesPayload( + clientMutationId = clientMutationId, + pages = + List(chapter.pageCount) { index -> + "/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/$index" + }, + chapter = ChapterType(chapter), + ) + } } } } 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 50cb76e1..7addd0af 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt @@ -1,9 +1,11 @@ package suwayomi.tachidesk.graphql.mutations +import graphql.execution.DataFetcherResult import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeout import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.graphql.types.DownloadStatus import suwayomi.tachidesk.manga.impl.Chapter @@ -25,19 +27,21 @@ class DownloadMutation { val chapters: List, ) - fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DeleteDownloadedChaptersPayload { + fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DataFetcherResult { val (clientMutationId, chapters) = input - Chapter.deleteChapters(chapters) + return asDataFetcherResult { + Chapter.deleteChapters(chapters) - return DeleteDownloadedChaptersPayload( - clientMutationId = clientMutationId, - chapters = - transaction { - ChapterTable.select { ChapterTable.id inList chapters } - .map { ChapterType(it) } - }, - ) + DeleteDownloadedChaptersPayload( + clientMutationId = clientMutationId, + chapters = + transaction { + ChapterTable.select { ChapterTable.id inList chapters } + .map { ChapterType(it) } + }, + ) + } } data class DeleteDownloadedChapterInput( @@ -50,18 +54,20 @@ class DownloadMutation { val chapters: ChapterType, ) - fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DeleteDownloadedChapterPayload { + fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DataFetcherResult { val (clientMutationId, chapter) = input - Chapter.deleteChapters(listOf(chapter)) + return asDataFetcherResult { + Chapter.deleteChapters(listOf(chapter)) - return DeleteDownloadedChapterPayload( - clientMutationId = clientMutationId, - chapters = - transaction { - ChapterType(ChapterTable.select { ChapterTable.id eq chapter }.first()) - }, - ) + DeleteDownloadedChapterPayload( + clientMutationId = clientMutationId, + chapters = + transaction { + ChapterType(ChapterTable.select { ChapterTable.id eq chapter }.first()) + }, + ) + } } data class EnqueueChapterDownloadsInput( @@ -74,19 +80,23 @@ class DownloadMutation { val downloadStatus: DownloadStatus, ) - fun enqueueChapterDownloads(input: EnqueueChapterDownloadsInput): CompletableFuture { + fun enqueueChapterDownloads( + input: EnqueueChapterDownloadsInput, + ): CompletableFuture> { val (clientMutationId, chapters) = input - DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters)) - return future { - EnqueueChapterDownloadsPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id in chapters } }) - }, - ) + asDataFetcherResult { + DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters)) + + EnqueueChapterDownloadsPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id in chapters } }) + }, + ) + } } } @@ -100,19 +110,21 @@ class DownloadMutation { val downloadStatus: DownloadStatus, ) - fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture { + fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture> { val (clientMutationId, chapter) = input - DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter))) - return future { - EnqueueChapterDownloadPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id == chapter } }) - }, - ) + asDataFetcherResult { + DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter))) + + EnqueueChapterDownloadPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id == chapter } }) + }, + ) + } } } @@ -126,19 +138,23 @@ class DownloadMutation { val downloadStatus: DownloadStatus, ) - fun dequeueChapterDownloads(input: DequeueChapterDownloadsInput): CompletableFuture { + fun dequeueChapterDownloads( + input: DequeueChapterDownloadsInput, + ): CompletableFuture> { val (clientMutationId, chapters) = input - DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters)) - return future { - DequeueChapterDownloadsPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id in chapters } }) - }, - ) + asDataFetcherResult { + DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters)) + + DequeueChapterDownloadsPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id in chapters } }) + }, + ) + } } } @@ -152,19 +168,21 @@ class DownloadMutation { val downloadStatus: DownloadStatus, ) - fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture { + fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture> { val (clientMutationId, chapter) = input - DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter))) - return future { - DequeueChapterDownloadPayload( - clientMutationId = clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id == chapter } }) - }, - ) + asDataFetcherResult { + DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter))) + + DequeueChapterDownloadPayload( + clientMutationId = clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id == chapter } }) + }, + ) + } } } @@ -177,19 +195,21 @@ class DownloadMutation { val downloadStatus: DownloadStatus, ) - fun startDownloader(input: StartDownloaderInput): CompletableFuture { - DownloadManager.start() - + fun startDownloader(input: StartDownloaderInput): CompletableFuture> { return future { - StartDownloaderPayload( - input.clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.status.first { it.status == Status.Started }, - ) - }, - ) + asDataFetcherResult { + DownloadManager.start() + + StartDownloaderPayload( + input.clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.status.first { it.status == Status.Started }, + ) + }, + ) + } } } @@ -202,18 +222,21 @@ class DownloadMutation { val downloadStatus: DownloadStatus, ) - fun stopDownloader(input: StopDownloaderInput): CompletableFuture { + fun stopDownloader(input: StopDownloaderInput): CompletableFuture> { return future { - DownloadManager.stop() - StopDownloaderPayload( - input.clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.status.first { it.status == Status.Stopped }, - ) - }, - ) + asDataFetcherResult { + DownloadManager.stop() + + StopDownloaderPayload( + input.clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.status.first { it.status == Status.Stopped }, + ) + }, + ) + } } } @@ -226,18 +249,21 @@ class DownloadMutation { val downloadStatus: DownloadStatus, ) - fun clearDownloader(input: ClearDownloaderInput): CompletableFuture { + fun clearDownloader(input: ClearDownloaderInput): CompletableFuture> { return future { - DownloadManager.clear() - ClearDownloaderPayload( - input.clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.status.first { it.status == Status.Stopped && it.queue.isEmpty() }, - ) - }, - ) + asDataFetcherResult { + DownloadManager.clear() + + ClearDownloaderPayload( + input.clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.status.first { it.status == Status.Stopped && it.queue.isEmpty() }, + ) + }, + ) + } } } @@ -252,20 +278,23 @@ class DownloadMutation { val downloadStatus: DownloadStatus, ) - fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture { + fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture> { val (clientMutationId, chapter, to) = input - DownloadManager.reorder(chapter, to) return future { - ReorderChapterDownloadPayload( - clientMutationId, - downloadStatus = - withTimeout(30.seconds) { - DownloadStatus( - DownloadManager.status.first { it.queue.indexOfFirst { it.chapter.id == chapter } <= to }, - ) - }, - ) + asDataFetcherResult { + DownloadManager.reorder(chapter, to) + + ReorderChapterDownloadPayload( + clientMutationId, + downloadStatus = + withTimeout(30.seconds) { + DownloadStatus( + DownloadManager.status.first { it.queue.indexOfFirst { it.chapter.id == chapter } <= to }, + ) + }, + ) + } } } } 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 23520a3a..1e69c8f4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt @@ -1,9 +1,11 @@ package suwayomi.tachidesk.graphql.mutations import eu.kanade.tachiyomi.source.local.LocalSource +import graphql.execution.DataFetcherResult import io.javalin.http.UploadedFile import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.types.ExtensionType import suwayomi.tachidesk.manga.impl.extension.Extension import suwayomi.tachidesk.manga.impl.extension.ExtensionsList @@ -69,41 +71,45 @@ class ExtensionMutation { } } - fun updateExtension(input: UpdateExtensionInput): CompletableFuture { + fun updateExtension(input: UpdateExtensionInput): CompletableFuture> { val (clientMutationId, id, patch) = input return future { - updateExtensions(listOf(id), patch) - }.thenApply { - val extension = - transaction { - ExtensionTable.select { ExtensionTable.pkgName eq id }.firstOrNull() - ?.let { ExtensionType(it) } - } + asDataFetcherResult { + updateExtensions(listOf(id), patch) - UpdateExtensionPayload( - clientMutationId = clientMutationId, - extension = extension, - ) + val extension = + transaction { + ExtensionTable.select { ExtensionTable.pkgName eq id }.firstOrNull() + ?.let { ExtensionType(it) } + } + + UpdateExtensionPayload( + clientMutationId = clientMutationId, + extension = extension, + ) + } } } - fun updateExtensions(input: UpdateExtensionsInput): CompletableFuture { + fun updateExtensions(input: UpdateExtensionsInput): CompletableFuture> { val (clientMutationId, ids, patch) = input return future { - updateExtensions(ids, patch) - }.thenApply { - val extensions = - transaction { - ExtensionTable.select { ExtensionTable.pkgName inList ids } - .map { ExtensionType(it) } - } + asDataFetcherResult { + updateExtensions(ids, patch) - UpdateExtensionsPayload( - clientMutationId = clientMutationId, - extensions = extensions, - ) + val extensions = + transaction { + ExtensionTable.select { ExtensionTable.pkgName inList ids } + .map { ExtensionType(it) } + } + + UpdateExtensionsPayload( + clientMutationId = clientMutationId, + extensions = extensions, + ) + } } } @@ -116,22 +122,24 @@ class ExtensionMutation { val extensions: List, ) - fun fetchExtensions(input: FetchExtensionsInput): CompletableFuture { + fun fetchExtensions(input: FetchExtensionsInput): CompletableFuture> { val (clientMutationId) = input return future { - ExtensionsList.fetchExtensions() - }.thenApply { - val extensions = - transaction { - ExtensionTable.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME } - .map { ExtensionType(it) } - } + asDataFetcherResult { + ExtensionsList.fetchExtensions() - FetchExtensionsPayload( - clientMutationId = clientMutationId, - extensions = extensions, - ) + val extensions = + transaction { + ExtensionTable.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME } + .map { ExtensionType(it) } + } + + FetchExtensionsPayload( + clientMutationId = clientMutationId, + extensions = extensions, + ) + } } } @@ -145,18 +153,22 @@ class ExtensionMutation { val extension: ExtensionType, ) - fun installExternalExtension(input: InstallExternalExtensionInput): CompletableFuture { + fun installExternalExtension( + input: InstallExternalExtensionInput, + ): CompletableFuture> { val (clientMutationId, extensionFile) = input return future { - Extension.installExternalExtension(extensionFile.content, extensionFile.filename) - }.thenApply { - val dbExtension = transaction { ExtensionTable.select { ExtensionTable.apkName eq extensionFile.filename }.first() } + asDataFetcherResult { + Extension.installExternalExtension(extensionFile.content, extensionFile.filename) - InstallExternalExtensionPayload( - clientMutationId, - extension = ExtensionType(dbExtension), - ) + val dbExtension = transaction { ExtensionTable.select { ExtensionTable.apkName eq extensionFile.filename }.first() } + + InstallExternalExtensionPayload( + clientMutationId, + extension = ExtensionType(dbExtension), + ) + } } } } 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 dc6f2689..a16a1a00 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt @@ -1,7 +1,9 @@ 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.types.UpdateState.DOWNLOADING import suwayomi.tachidesk.graphql.types.UpdateState.ERROR import suwayomi.tachidesk.graphql.types.UpdateState.IDLE @@ -22,50 +24,54 @@ class InfoMutation { val updateStatus: WebUIUpdateStatus, ) - fun updateWebUI(input: WebUIUpdateInput): CompletableFuture { + fun updateWebUI(input: WebUIUpdateInput): CompletableFuture> { return future { - withTimeout(30.seconds) { - if (WebInterfaceManager.status.value.state === DOWNLOADING) { - return@withTimeout WebUIUpdatePayload(input.clientMutationId, WebInterfaceManager.status.value) - } + asDataFetcherResult { + 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( + 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( input.clientMutationId, - WebInterfaceManager.getStatus(version, if (didUpdateCheckFail) ERROR else IDLE), + updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING }, ) } - 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 }, - ) } } } - fun resetWebUIUpdateStatus(): CompletableFuture { + fun resetWebUIUpdateStatus(): CompletableFuture> { return future { - withTimeout(30.seconds) { - val isUpdateFinished = WebInterfaceManager.status.value.state != DOWNLOADING - if (!isUpdateFinished) { - throw Exception("Status reset is not allowed during status \"$DOWNLOADING\"") + 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 } } - - WebInterfaceManager.resetStatus() - - WebInterfaceManager.status.first { it.state == IDLE } } } } 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 5bab91a7..40d19b5c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt @@ -1,11 +1,13 @@ package suwayomi.tachidesk.graphql.mutations +import graphql.execution.DataFetcherResult import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update +import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.types.MangaMetaType import suwayomi.tachidesk.graphql.types.MangaType import suwayomi.tachidesk.manga.impl.Library @@ -70,39 +72,43 @@ class MangaMutation { } } - fun updateManga(input: UpdateMangaInput): CompletableFuture { + fun updateManga(input: UpdateMangaInput): CompletableFuture> { val (clientMutationId, id, patch) = input return future { - updateMangas(listOf(id), patch) - }.thenApply { - val manga = - transaction { - MangaType(MangaTable.select { MangaTable.id eq id }.first()) - } + asDataFetcherResult { + updateMangas(listOf(id), patch) - UpdateMangaPayload( - clientMutationId = clientMutationId, - manga = manga, - ) + val manga = + transaction { + MangaType(MangaTable.select { MangaTable.id eq id }.first()) + } + + UpdateMangaPayload( + clientMutationId = clientMutationId, + manga = manga, + ) + } } } - fun updateMangas(input: UpdateMangasInput): CompletableFuture { + fun updateMangas(input: UpdateMangasInput): CompletableFuture> { val (clientMutationId, ids, patch) = input return future { - updateMangas(ids, patch) - }.thenApply { - val mangas = - transaction { - MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) } - } + asDataFetcherResult { + updateMangas(ids, patch) - UpdateMangasPayload( - clientMutationId = clientMutationId, - mangas = mangas, - ) + val mangas = + transaction { + MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) } + } + + UpdateMangasPayload( + clientMutationId = clientMutationId, + mangas = mangas, + ) + } } } @@ -116,20 +122,22 @@ class MangaMutation { val manga: MangaType, ) - fun fetchManga(input: FetchMangaInput): CompletableFuture { + fun fetchManga(input: FetchMangaInput): CompletableFuture> { val (clientMutationId, id) = input return future { - Manga.fetchManga(id) - }.thenApply { - val manga = - transaction { - MangaTable.select { MangaTable.id eq id }.first() - } - FetchMangaPayload( - clientMutationId = clientMutationId, - manga = MangaType(manga), - ) + asDataFetcherResult { + Manga.fetchManga(id) + + val manga = + transaction { + MangaTable.select { MangaTable.id eq id }.first() + } + FetchMangaPayload( + clientMutationId = clientMutationId, + manga = MangaType(manga), + ) + } } } @@ -143,12 +151,14 @@ class MangaMutation { val meta: MangaMetaType, ) - fun setMangaMeta(input: SetMangaMetaInput): SetMangaMetaPayload { + fun setMangaMeta(input: SetMangaMetaInput): DataFetcherResult { val (clientMutationId, meta) = input - Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value) + return asDataFetcherResult { + Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value) - return SetMangaMetaPayload(clientMutationId, meta) + SetMangaMetaPayload(clientMutationId, meta) + } } data class DeleteMangaMetaInput( @@ -163,29 +173,31 @@ class MangaMutation { val manga: MangaType, ) - fun deleteMangaMeta(input: DeleteMangaMetaInput): DeleteMangaMetaPayload { + fun deleteMangaMeta(input: DeleteMangaMetaInput): DataFetcherResult { val (clientMutationId, mangaId, key) = input - val (meta, manga) = - transaction { - val meta = - MangaMetaTable.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) } - .firstOrNull() + return asDataFetcherResult { + val (meta, manga) = + transaction { + val meta = + MangaMetaTable.select { (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.select { MangaTable.id eq mangaId }.first()) - } + val manga = + transaction { + MangaType(MangaTable.select { MangaTable.id eq mangaId }.first()) + } - if (meta != null) { - MangaMetaType(meta) - } else { - null - } to manga - } + if (meta != null) { + MangaMetaType(meta) + } else { + null + } to manga + } - return DeleteMangaMetaPayload(clientMutationId, meta, manga) + DeleteMangaMetaPayload(clientMutationId, meta, manga) + } } } 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 dbe1c96a..65ee38b4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt @@ -1,11 +1,13 @@ package suwayomi.tachidesk.graphql.mutations +import graphql.execution.DataFetcherResult import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.select 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.types.GlobalMetaType class MetaMutation { @@ -19,12 +21,14 @@ class MetaMutation { val meta: GlobalMetaType, ) - fun setGlobalMeta(input: SetGlobalMetaInput): SetGlobalMetaPayload { + fun setGlobalMeta(input: SetGlobalMetaInput): DataFetcherResult { val (clientMutationId, meta) = input - GlobalMeta.modifyMeta(meta.key, meta.value) + return asDataFetcherResult { + GlobalMeta.modifyMeta(meta.key, meta.value) - return SetGlobalMetaPayload(clientMutationId, meta) + SetGlobalMetaPayload(clientMutationId, meta) + } } data class DeleteGlobalMetaInput( @@ -37,24 +41,26 @@ class MetaMutation { val meta: GlobalMetaType?, ) - fun deleteGlobalMeta(input: DeleteGlobalMetaInput): DeleteGlobalMetaPayload { + fun deleteGlobalMeta(input: DeleteGlobalMetaInput): DataFetcherResult { val (clientMutationId, key) = input - val meta = - transaction { - val meta = - GlobalMetaTable.select { GlobalMetaTable.key eq key } - .firstOrNull() + return asDataFetcherResult { + val meta = + transaction { + val meta = + GlobalMetaTable.select { 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 + } } - } - return DeleteGlobalMetaPayload(clientMutationId, meta) + DeleteGlobalMetaPayload(clientMutationId, meta) + } } } 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 a1cb4c37..a0359997 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt @@ -5,11 +5,13 @@ 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.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.types.FilterChange import suwayomi.tachidesk.graphql.types.MangaType import suwayomi.tachidesk.graphql.types.Preference @@ -37,12 +39,14 @@ class SourceMutation { val meta: SourceMetaType, ) - fun setSourceMeta(input: SetSourceMetaInput): SetSourceMetaPayload { + fun setSourceMeta(input: SetSourceMetaInput): DataFetcherResult { val (clientMutationId, meta) = input - Source.modifyMeta(meta.sourceId, meta.key, meta.value) + return asDataFetcherResult { + Source.modifyMeta(meta.sourceId, meta.key, meta.value) - return SetSourceMetaPayload(clientMutationId, meta) + SetSourceMetaPayload(clientMutationId, meta) + } } data class DeleteSourceMetaInput( @@ -57,31 +61,33 @@ class SourceMutation { val source: SourceType?, ) - fun deleteSourceMeta(input: DeleteSourceMetaInput): DeleteSourceMetaPayload { + fun deleteSourceMeta(input: DeleteSourceMetaInput): DataFetcherResult { val (clientMutationId, sourceId, key) = input - val (meta, source) = - transaction { - val meta = - SourceMetaTable.select { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } - .firstOrNull() + return asDataFetcherResult { + val (meta, source) = + transaction { + val meta = + SourceMetaTable.select { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + .firstOrNull() - SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } + SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) } - val source = - transaction { - SourceTable.select { SourceTable.id eq sourceId }.firstOrNull() - ?.let { SourceType(it) } - } + val source = + transaction { + SourceTable.select { SourceTable.id eq sourceId }.firstOrNull() + ?.let { SourceType(it) } + } - if (meta != null) { - SourceMetaType(meta) - } else { - null - } to source - } + if (meta != null) { + SourceMetaType(meta) + } else { + null + } to source + } - return DeleteSourceMetaPayload(clientMutationId, meta, source) + DeleteSourceMetaPayload(clientMutationId, meta, source) + } } enum class FetchSourceMangaType { @@ -105,44 +111,46 @@ class SourceMutation { val hasNextPage: Boolean, ) - fun fetchSourceManga(input: FetchSourceMangaInput): CompletableFuture { + fun fetchSourceManga(input: FetchSourceMangaInput): CompletableFuture> { val (clientMutationId, sourceId, type, page, query, filters) = input return future { - val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!! - val mangasPage = - when (type) { - FetchSourceMangaType.SEARCH -> { - source.getSearchManga( - page = page, - query = query.orEmpty(), - filters = updateFilterList(source, filters), - ) + 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) + } } - FetchSourceMangaType.POPULAR -> { - source.getPopularManga(page) + + val mangaIds = mangasPage.insertOrGet(sourceId) + + val mangas = + transaction { + MangaTable.select { MangaTable.id inList mangaIds } + .map { MangaType(it) } + }.sortedBy { + mangaIds.indexOf(it.id) } - FetchSourceMangaType.LATEST -> { - if (!source.supportsLatest) throw Exception("Source does not support latest") - source.getLatestUpdates(page) - } - } - val mangaIds = mangasPage.insertOrGet(sourceId) - - val mangas = - transaction { - MangaTable.select { MangaTable.id inList mangaIds } - .map { MangaType(it) } - }.sortedBy { - mangaIds.indexOf(it.id) - } - - FetchSourceMangaPayload( - clientMutationId = clientMutationId, - mangas = mangas, - hasNextPage = mangasPage.hasNextPage, - ) + FetchSourceMangaPayload( + clientMutationId = clientMutationId, + mangas = mangas, + hasNextPage = mangasPage.hasNextPage, + ) + } } } @@ -167,27 +175,29 @@ class SourceMutation { val source: SourceType, ) - fun updateSourcePreference(input: UpdateSourcePreferenceInput): UpdateSourcePreferencePayload { + fun updateSourcePreference(input: UpdateSourcePreferenceInput): DataFetcherResult { val (clientMutationId, sourceId, change) = input - 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 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}") + } - return UpdateSourcePreferencePayload( - clientMutationId = clientMutationId, - preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) }, - source = - transaction { - SourceType(SourceTable.select { SourceTable.id eq sourceId }.first())!! - }, - ) + UpdateSourcePreferencePayload( + clientMutationId = clientMutationId, + preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) }, + source = + transaction { + SourceType(SourceTable.select { 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 5103d21b..1e454618 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt @@ -2,9 +2,11 @@ package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.generator.annotations.GraphQLDescription +import graphql.execution.DataFetcherResult import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.types.TrackRecordType import suwayomi.tachidesk.graphql.types.TrackerType import suwayomi.tachidesk.manga.impl.track.Track @@ -203,20 +205,22 @@ class TrackMutation { val trackRecords: List, ) - fun trackProgress(input: TrackProgressInput): CompletableFuture { + fun trackProgress(input: TrackProgressInput): CompletableFuture> { val (clientMutationId, mangaId) = input return future { - Track.trackChapter(mangaId) - val trackRecords = - transaction { - TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId } - .toList() - } - TrackProgressPayload( - clientMutationId, - trackRecords.map { TrackRecordType(it) }, - ) + asDataFetcherResult { + Track.trackChapter(mangaId) + val trackRecords = + transaction { + TrackRecordTable.select { 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 10fed203..377c6f80 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt @@ -1,5 +1,6 @@ package suwayomi.tachidesk.graphql.mutations +import graphql.execution.DataFetcherResult import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeout import org.jetbrains.exposed.sql.select @@ -7,6 +8,7 @@ import org.jetbrains.exposed.sql.transactions.transaction import org.kodein.di.DI import org.kodein.di.conf.global import org.kodein.di.instance +import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.types.UpdateStatus import suwayomi.tachidesk.manga.impl.Category import suwayomi.tachidesk.manga.impl.update.IUpdater @@ -28,7 +30,7 @@ class UpdateMutation { val updateStatus: UpdateStatus, ) - fun updateLibraryManga(input: UpdateLibraryMangaInput): CompletableFuture { + fun updateLibraryManga(input: UpdateLibraryMangaInput): CompletableFuture> { updater.addCategoriesToUpdateQueue( Category.getCategoryList(), clear = true, @@ -36,13 +38,15 @@ class UpdateMutation { ) return future { - UpdateLibraryMangaPayload( - input.clientMutationId, - updateStatus = - withTimeout(30.seconds) { - UpdateStatus(updater.status.first()) - }, - ) + asDataFetcherResult { + UpdateLibraryMangaPayload( + input.clientMutationId, + updateStatus = + withTimeout(30.seconds) { + UpdateStatus(updater.status.first()) + }, + ) + } } } @@ -56,7 +60,7 @@ class UpdateMutation { val updateStatus: UpdateStatus, ) - fun updateCategoryManga(input: UpdateCategoryMangaInput): CompletableFuture { + fun updateCategoryManga(input: UpdateCategoryMangaInput): CompletableFuture> { val categories = transaction { CategoryTable.select { CategoryTable.id inList input.categories }.map { @@ -66,13 +70,15 @@ class UpdateMutation { updater.addCategoriesToUpdateQueue(categories, clear = true, forceAll = true) return future { - UpdateCategoryMangaPayload( - input.clientMutationId, - updateStatus = - withTimeout(30.seconds) { - UpdateStatus(updater.status.first()) - }, - ) + asDataFetcherResult { + UpdateCategoryMangaPayload( + input.clientMutationId, + updateStatus = + withTimeout(30.seconds) { + UpdateStatus(updater.status.first()) + }, + ) + } } }