Library Update Queries and Mutations (#609)

* Add library update GraphQL endpoints

* No need for data classes

* UpdateLibraryManga
This commit is contained in:
Mitchell Syer
2023-08-03 18:08:35 -04:00
committed by GitHub
parent 78a167aacf
commit c3fb08d634
11 changed files with 199 additions and 52 deletions

View File

@@ -80,3 +80,20 @@ class MangaForSourceDataLoader : KotlinDataLoader<Long, MangaNodeList> {
}
}
}
class MangaForIdsDataLoader : KotlinDataLoader<List<Int>, MangaNodeList> {
override val dataLoaderName = "MangaForIdsDataLoader"
override fun getDataLoader(): DataLoader<List<Int>, MangaNodeList> = DataLoaderFactory.newDataLoader { mangaIds ->
future {
transaction {
addLogger(Slf4jSqlDebugLogger)
val ids = mangaIds.flatten().distinct()
val manga = MangaTable.select { MangaTable.id inList ids }
.map { MangaType(it) }
mangaIds.map { mangaIds ->
manga.filter { it.id in mangaIds }.toNodeList()
}
}
}
}
}

View File

@@ -0,0 +1,69 @@
package suwayomi.tachidesk.graphql.mutations
import org.jetbrains.exposed.sql.select
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.types.UpdateStatus
import suwayomi.tachidesk.manga.impl.Category
import suwayomi.tachidesk.manga.impl.update.IUpdater
import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.manga.model.table.toDataClass
class UpdateMutation {
private val updater by DI.global.instance<IUpdater>()
data class UpdateLibraryMangaInput(
val clientMutationId: String? = null
)
data class UpdateLibraryMangaPayload(
val clientMutationId: String?,
val updateStatus: UpdateStatus
)
fun updateLibraryManga(input: UpdateLibraryMangaInput): UpdateLibraryMangaPayload {
updater.addCategoriesToUpdateQueue(
Category.getCategoryList(),
clear = true,
forceAll = false
)
return UpdateLibraryMangaPayload(input.clientMutationId, UpdateStatus(updater.status.value))
}
data class UpdateCategoryMangaInput(
val clientMutationId: String? = null,
val categories: List<Int>
)
data class UpdateCategoryMangaPayload(
val clientMutationId: String?,
val updateStatus: UpdateStatus
)
fun updateCategoryManga(input: UpdateCategoryMangaInput): UpdateCategoryMangaPayload {
val categories = transaction {
CategoryTable.select { CategoryTable.id inList input.categories }.map {
CategoryTable.toDataClass(it)
}
}
updater.addCategoriesToUpdateQueue(categories, clear = true, forceAll = true)
return UpdateCategoryMangaPayload(
clientMutationId = input.clientMutationId,
updateStatus = UpdateStatus(updater.status.value)
)
}
data class UpdateStopInput(
val clientMutationId: String? = null
)
data class UpdateStopPayload(
val clientMutationId: String?
)
fun updateStop(input: UpdateStopInput): UpdateStopPayload {
updater.reset()
return UpdateStopPayload(input.clientMutationId)
}
}

View File

@@ -0,0 +1,24 @@
package suwayomi.tachidesk.graphql.queries
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.graphql.types.UpdateStatus
import suwayomi.tachidesk.graphql.types.UpdateStatusType
import suwayomi.tachidesk.manga.impl.update.IUpdater
import suwayomi.tachidesk.manga.impl.update.JobStatus
class UpdateQuery {
private val updater by DI.global.instance<IUpdater>()
fun updateStatus(): UpdateStatus {
val status = updater.status.value
return UpdateStatus(
isRunning = status.running,
pendingJobs = UpdateStatusType(status.statusMap[JobStatus.PENDING]?.map { it.id }.orEmpty()),
runningJobs = UpdateStatusType(status.statusMap[JobStatus.RUNNING]?.map { it.id }.orEmpty()),
completeJobs = UpdateStatusType(status.statusMap[JobStatus.COMPLETE]?.map { it.id }.orEmpty()),
failedJobs = UpdateStatusType(status.statusMap[JobStatus.FAILED]?.map { it.id }.orEmpty())
)
}
}

View File

@@ -1,42 +0,0 @@
package suwayomi.tachidesk.graphql.queries
import org.jetbrains.exposed.sql.select
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.types.MangaType
import suwayomi.tachidesk.manga.impl.update.IUpdater
import suwayomi.tachidesk.manga.impl.update.JobStatus
import suwayomi.tachidesk.manga.impl.update.UpdaterSocket
import suwayomi.tachidesk.manga.model.table.MangaTable
class UpdaterQuery {
sealed interface UpdaterStatus {
data class UpdaterJob(val status: JobStatus, val manga: MangaType)
data class Running(val jobs: Map<JobStatus, List<MangaType>>) : UpdaterStatus
// data class Idle
}
private val updater by DI.global.instance<IUpdater>()
fun updaterStatus() {
val status = updater.status.value
if (status.running) {
val mangaIds = status.statusMap.values.flatMap { mangas -> mangas.map { it.id } }
val mangaMap = transaction {
MangaTable.select { MangaTable.id inList mangaIds }
.map { MangaType(it) }
.associateBy { it.id }
}
UpdaterStatus.Running(
status.statusMap.mapValues { (_, mangas) ->
mangas.mapNotNull { mangaMap[it.id] }
}
)
}
UpdaterSocket
}
}

View File

@@ -19,6 +19,7 @@ import suwayomi.tachidesk.graphql.dataLoaders.ExtensionForSourceDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.GlobalMetaDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.MangaDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.MangaForCategoryDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.MangaForIdsDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.MangaForSourceDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.MangaMetaDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.SourceDataLoader
@@ -36,6 +37,7 @@ class TachideskDataLoaderRegistryFactory {
MangaMetaDataLoader(),
MangaForCategoryDataLoader(),
MangaForSourceDataLoader(),
MangaForIdsDataLoader(),
CategoryDataLoader(),
CategoryMetaDataLoader(),
CategoriesForMangaDataLoader(),

View File

@@ -20,6 +20,7 @@ import suwayomi.tachidesk.graphql.mutations.ExtensionMutation
import suwayomi.tachidesk.graphql.mutations.MangaMutation
import suwayomi.tachidesk.graphql.mutations.MetaMutation
import suwayomi.tachidesk.graphql.mutations.SourceMutation
import suwayomi.tachidesk.graphql.mutations.UpdateMutation
import suwayomi.tachidesk.graphql.queries.BackupQuery
import suwayomi.tachidesk.graphql.queries.CategoryQuery
import suwayomi.tachidesk.graphql.queries.ChapterQuery
@@ -27,11 +28,13 @@ import suwayomi.tachidesk.graphql.queries.ExtensionQuery
import suwayomi.tachidesk.graphql.queries.MangaQuery
import suwayomi.tachidesk.graphql.queries.MetaQuery
import suwayomi.tachidesk.graphql.queries.SourceQuery
import suwayomi.tachidesk.graphql.queries.UpdateQuery
import suwayomi.tachidesk.graphql.server.primitives.Cursor
import suwayomi.tachidesk.graphql.server.primitives.GraphQLCursor
import suwayomi.tachidesk.graphql.server.primitives.GraphQLLongAsString
import suwayomi.tachidesk.graphql.server.primitives.GraphQLUpload
import suwayomi.tachidesk.graphql.subscriptions.DownloadSubscription
import suwayomi.tachidesk.graphql.subscriptions.UpdateSubscription
import kotlin.reflect.KClass
import kotlin.reflect.KType
@@ -57,7 +60,8 @@ val schema = toSchema(
TopLevelObject(ExtensionQuery()),
TopLevelObject(MangaQuery()),
TopLevelObject(MetaQuery()),
TopLevelObject(SourceQuery())
TopLevelObject(SourceQuery()),
TopLevelObject(UpdateQuery())
),
mutations = listOf(
TopLevelObject(BackupMutation()),
@@ -66,9 +70,11 @@ val schema = toSchema(
TopLevelObject(ExtensionMutation()),
TopLevelObject(MangaMutation()),
TopLevelObject(MetaMutation()),
TopLevelObject(SourceMutation())
TopLevelObject(SourceMutation()),
TopLevelObject(UpdateMutation())
),
subscriptions = listOf(
TopLevelObject(DownloadSubscription())
TopLevelObject(DownloadSubscription()),
TopLevelObject(UpdateSubscription())
)
)

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
package suwayomi.tachidesk.graphql.subscriptions
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.graphql.types.UpdateStatus
import suwayomi.tachidesk.manga.impl.update.IUpdater
class UpdateSubscription {
private val updater by DI.global.instance<IUpdater>()
fun updateStatusChanged(): Flow<UpdateStatus> {
return updater.status.map { updateStatus ->
UpdateStatus(updateStatus)
}
}
}

View File

@@ -0,0 +1,33 @@
package suwayomi.tachidesk.graphql.types
import com.expediagroup.graphql.generator.annotations.GraphQLIgnore
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
import graphql.schema.DataFetchingEnvironment
import suwayomi.tachidesk.manga.impl.update.JobStatus
import suwayomi.tachidesk.manga.impl.update.UpdateStatus
import java.util.concurrent.CompletableFuture
class UpdateStatus(
val isRunning: Boolean,
val pendingJobs: UpdateStatusType,
val runningJobs: UpdateStatusType,
val completeJobs: UpdateStatusType,
val failedJobs: UpdateStatusType
) {
constructor(status: UpdateStatus) : this(
isRunning = status.running,
pendingJobs = UpdateStatusType(status.statusMap[JobStatus.PENDING]?.map { it.id }.orEmpty()),
runningJobs = UpdateStatusType(status.statusMap[JobStatus.RUNNING]?.map { it.id }.orEmpty()),
completeJobs = UpdateStatusType(status.statusMap[JobStatus.COMPLETE]?.map { it.id }.orEmpty()),
failedJobs = UpdateStatusType(status.statusMap[JobStatus.FAILED]?.map { it.id }.orEmpty())
)
}
class UpdateStatusType(
@get:GraphQLIgnore
val mangaIds: List<Int>
) {
fun mangas(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaNodeList> {
return dataFetchingEnvironment.getValueFromDataLoader<List<Int>, MangaNodeList>("MangaForIdsDataLoader", mangaIds)
}
}