From 2230796504ce938c510c5cdab0a3c7875db99702 Mon Sep 17 00:00:00 2001 From: Mitchell Syer Date: Fri, 26 May 2023 19:39:17 -0400 Subject: [PATCH] Extension mutations (#560) --- .../graphql/mutations/ExtensionMutation.kt | 133 ++++++++++++++++-- .../graphql/queries/ExtensionQuery.kt | 4 + .../manga/impl/extension/ExtensionsList.kt | 5 +- 3 files changed, 133 insertions(+), 9 deletions(-) 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 cfa5a0a1..6211fae7 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt @@ -1,10 +1,127 @@ package suwayomi.tachidesk.graphql.mutations -/** - * TODO Mutations - * - Install - * - Update - * - Uninstall - * - Check for updates (global mutation?) - */ -class ExtensionMutation +import eu.kanade.tachiyomi.source.local.LocalSource +import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.types.ExtensionType +import suwayomi.tachidesk.manga.impl.extension.Extension +import suwayomi.tachidesk.manga.impl.extension.ExtensionsList +import suwayomi.tachidesk.manga.model.table.ExtensionTable +import suwayomi.tachidesk.server.JavalinSetup.future +import java.util.concurrent.CompletableFuture + +class ExtensionMutation { + data class UpdateExtensionPatch( + val install: Boolean? = null, + val update: Boolean? = null, + val uninstall: Boolean? = null + ) + + data class UpdateExtensionPayload( + val clientMutationId: String?, + val extension: ExtensionType + ) + data class UpdateExtensionInput( + val clientMutationId: String? = null, + val id: String, + val patch: UpdateExtensionPatch + ) + + data class UpdateExtensionsPayload( + val clientMutationId: String?, + val extensions: List + ) + data class UpdateExtensionsInput( + val clientMutationId: String? = null, + val ids: List, + val patch: UpdateExtensionPatch + ) + + private suspend fun updateExtensions(ids: List, patch: UpdateExtensionPatch) { + val extensions = transaction { + ExtensionTable.select { ExtensionTable.pkgName inList ids } + .map { ExtensionType(it) } + } + + if (patch.update == true) { + extensions.filter { it.hasUpdate }.forEach { + Extension.updateExtension(it.pkgName) + } + } + + if (patch.install == true) { + extensions.filterNot { it.isInstalled }.forEach { + Extension.installExtension(it.pkgName) + } + } + + if (patch.uninstall == true) { + extensions.filter { it.isInstalled }.forEach { + Extension.uninstallExtension(it.pkgName) + } + } + } + + fun updateExtension(input: UpdateExtensionInput): CompletableFuture { + val (clientMutationId, id, patch) = input + + return future { + updateExtensions(listOf(id), patch) + }.thenApply { + val extension = transaction { + ExtensionType(ExtensionTable.select { ExtensionTable.pkgName eq id }.first()) + } + + UpdateExtensionPayload( + clientMutationId = clientMutationId, + extension = extension + ) + } + } + + 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) } + } + + UpdateExtensionsPayload( + clientMutationId = clientMutationId, + extensions = extensions + ) + } + } + + data class FetchExtensionsInput( + val clientMutationId: String? = null + ) + data class FetchExtensionsPayload( + val clientMutationId: String?, + val extensions: List + ) + + 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) } + } + + FetchExtensionsPayload( + clientMutationId = clientMutationId, + extensions = extensions + ) + } + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt index 1071e3c3..bb49f6b7 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt @@ -8,12 +8,14 @@ package suwayomi.tachidesk.graphql.queries import com.expediagroup.graphql.server.extensions.getValueFromDataLoader +import eu.kanade.tachiyomi.source.local.LocalSource import graphql.schema.DataFetchingEnvironment import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.less +import org.jetbrains.exposed.sql.SqlExpressionBuilder.neq import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction @@ -151,6 +153,8 @@ class ExtensionQuery { val queryResults = transaction { val res = ExtensionTable.selectAll() + res.adjustWhere { ExtensionTable.name neq LocalSource.EXTENSION_NAME } + res.applyOps(condition, filter) if (orderBy != null || (last != null || before != null)) { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt index 17a46d72..0cbf6e8f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt @@ -30,7 +30,7 @@ object ExtensionsList { var lastUpdateCheck: Long = 0 var updateMap = ConcurrentHashMap() - suspend fun getExtensionList(): List { + suspend fun fetchExtensions() { // update if 60 seconds has passed or requested offline and database is empty if (lastUpdateCheck + 60.seconds.inWholeMilliseconds < System.currentTimeMillis()) { logger.debug("Getting extensions list from the internet") @@ -41,7 +41,10 @@ object ExtensionsList { } else { logger.debug("used cached extension list") } + } + suspend fun getExtensionList(): List { + fetchExtensions() return extensionTableAsDataClass() }