From 3f91663ecf7f4e34b0ba87010e26c3b597adcc81 Mon Sep 17 00:00:00 2001 From: Mitchell Syer Date: Fri, 26 May 2023 06:15:16 -0400 Subject: [PATCH] Rewrite meta and add meta mutations (#556) --- .../graphql/dataLoaders/CategoryDataLoader.kt | 4 +- .../graphql/dataLoaders/MetaDataLoader.kt | 50 +++++----- .../graphql/mutations/CategoryMutation.kt | 68 ++++++++++++++ .../graphql/mutations/ChapterMutation.kt | 54 ++++++++++- .../graphql/mutations/ExtensionMutation.kt | 10 ++ .../graphql/mutations/MangaMutation.kt | 57 +++++++++++- .../graphql/mutations/MetaMutation.kt | 60 ++++++++++++ .../graphql/mutations/SourceMutation.kt | 8 ++ .../graphql/queries/CategoryQuery.kt | 12 --- .../graphql/queries/ExtensionQuery.kt | 9 -- .../tachidesk/graphql/queries/MangaQuery.kt | 3 - .../tachidesk/graphql/queries/MetaQuery.kt | 33 +++---- .../tachidesk/graphql/queries/SourceQuery.kt | 8 -- .../TachideskDataLoaderRegistryFactory.kt | 2 + .../graphql/server/TachideskGraphQLSchema.kt | 18 +++- .../tachidesk/graphql/types/CategoryType.kt | 4 +- .../tachidesk/graphql/types/ChapterType.kt | 4 +- .../tachidesk/graphql/types/MangaType.kt | 4 +- .../tachidesk/graphql/types/MetaType.kt | 92 ++++++++++++++----- .../suwayomi/tachidesk/manga/impl/Chapter.kt | 6 ++ 20 files changed, 384 insertions(+), 122 deletions(-) create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt index a4c5df83..e1d0da0b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt @@ -21,9 +21,9 @@ import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryTable import suwayomi.tachidesk.server.JavalinSetup.future -class CategoryDataLoader : KotlinDataLoader { +class CategoryDataLoader : KotlinDataLoader { override val dataLoaderName = "CategoryDataLoader" - override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) 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 6a7aca0a..662967dc 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt @@ -8,25 +8,23 @@ import org.jetbrains.exposed.sql.addLogger import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.global.model.table.GlobalMetaTable -import suwayomi.tachidesk.graphql.types.CategoryMetaItem -import suwayomi.tachidesk.graphql.types.ChapterMetaItem -import suwayomi.tachidesk.graphql.types.GlobalMetaItem -import suwayomi.tachidesk.graphql.types.MangaMetaItem -import suwayomi.tachidesk.graphql.types.MetaItem -import suwayomi.tachidesk.graphql.types.MetaNodeList -import suwayomi.tachidesk.graphql.types.MetaNodeList.Companion.toNodeList +import suwayomi.tachidesk.graphql.types.CategoryMetaType +import suwayomi.tachidesk.graphql.types.ChapterMetaType +import suwayomi.tachidesk.graphql.types.GlobalMetaType +import suwayomi.tachidesk.graphql.types.MangaMetaType +import suwayomi.tachidesk.manga.model.table.CategoryMetaTable import suwayomi.tachidesk.manga.model.table.ChapterMetaTable import suwayomi.tachidesk.manga.model.table.MangaMetaTable import suwayomi.tachidesk.server.JavalinSetup.future -class GlobalMetaDataLoader : KotlinDataLoader { +class GlobalMetaDataLoader : KotlinDataLoader { override val dataLoaderName = "GlobalMetaDataLoader" - override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) val metasByRefId = GlobalMetaTable.select { GlobalMetaTable.key inList ids } - .map { GlobalMetaItem(it) } + .map { GlobalMetaType(it) } .associateBy { it.key } ids.map { metasByRefId[it] } } @@ -34,46 +32,46 @@ class GlobalMetaDataLoader : KotlinDataLoader { } } -class ChapterMetaDataLoader : KotlinDataLoader { +class ChapterMetaDataLoader : KotlinDataLoader> { override val dataLoaderName = "ChapterMetaDataLoader" - override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(): DataLoader> = DataLoaderFactory.newDataLoader> { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) val metasByRefId = ChapterMetaTable.select { ChapterMetaTable.ref inList ids } - .map { ChapterMetaItem(it) } - .groupBy { it.ref } - ids.map { (metasByRefId[it] ?: emptyList()).toNodeList() } + .map { ChapterMetaType(it) } + .groupBy { it.chapterId } + ids.map { metasByRefId[it].orEmpty() } } } } } -class MangaMetaDataLoader : KotlinDataLoader { +class MangaMetaDataLoader : KotlinDataLoader> { override val dataLoaderName = "MangaMetaDataLoader" - override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(): DataLoader> = DataLoaderFactory.newDataLoader> { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) val metasByRefId = MangaMetaTable.select { MangaMetaTable.ref inList ids } - .map { MangaMetaItem(it) } - .groupBy { it.ref } - ids.map { (metasByRefId[it] ?: emptyList()).toNodeList() } + .map { MangaMetaType(it) } + .groupBy { it.mangaId } + ids.map { metasByRefId[it].orEmpty() } } } } } -class CategoryMetaDataLoader : KotlinDataLoader { +class CategoryMetaDataLoader : KotlinDataLoader> { override val dataLoaderName = "CategoryMetaDataLoader" - override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> + override fun getDataLoader(): DataLoader> = DataLoaderFactory.newDataLoader> { ids -> future { transaction { addLogger(Slf4jSqlDebugLogger) - val metasByRefId = MangaMetaTable.select { MangaMetaTable.ref inList ids } - .map { CategoryMetaItem(it) } - .groupBy { it.ref } - ids.map { (metasByRefId[it] ?: emptyList()).toNodeList() } + val metasByRefId = CategoryMetaTable.select { CategoryMetaTable.ref inList ids } + .map { CategoryMetaType(it) } + .groupBy { it.categoryId } + ids.map { metasByRefId[it].orEmpty() } } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt new file mode 100644 index 00000000..a79e3a03 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt @@ -0,0 +1,68 @@ +package suwayomi.tachidesk.graphql.mutations + +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.types.CategoryMetaType +import suwayomi.tachidesk.manga.impl.Category +import suwayomi.tachidesk.manga.model.table.CategoryMetaTable + +/** + * TODO Mutations + * - Name + * - Order + * - Default + * - Create + * - Delete + */ +class CategoryMutation { + data class SetCategoryMetaInput( + val clientMutationId: String? = null, + val meta: CategoryMetaType + ) + data class SetCategoryMetaPayload( + val clientMutationId: String?, + val meta: CategoryMetaType + ) + fun setCategoryMeta( + input: SetCategoryMetaInput + ): SetCategoryMetaPayload { + val (clientMutationId, meta) = input + + Category.modifyMeta(meta.categoryId, meta.key, meta.value) + + return SetCategoryMetaPayload(clientMutationId, meta) + } + + data class DeleteCategoryMetaInput( + val clientMutationId: String? = null, + val categoryId: Int, + val key: String + ) + data class DeleteCategoryMetaPayload( + val clientMutationId: String?, + val meta: CategoryMetaType? + ) + fun deleteCategoryMeta( + input: DeleteCategoryMetaInput + ): DeleteCategoryMetaPayload { + val (clientMutationId, categoryId, key) = input + + val meta = 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) } + + if (meta != null) { + CategoryMetaType(meta) + } else { + null + } + } + + return DeleteCategoryMetaPayload(clientMutationId, meta) + } +} 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 718aec58..75387c7e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt @@ -3,11 +3,16 @@ package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import com.expediagroup.graphql.server.extensions.getValuesFromDataLoader import graphql.schema.DataFetchingEnvironment +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.types.ChapterMetaType import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.manga.impl.Chapter +import suwayomi.tachidesk.manga.model.table.ChapterMetaTable import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.server.JavalinSetup.future import java.time.Instant @@ -15,7 +20,6 @@ import java.util.concurrent.CompletableFuture /** * TODO Mutations - * - Check for updates? * - Download * - Delete download */ @@ -124,4 +128,52 @@ class ChapterMutation { ) } } + + data class SetChapterMetaInput( + val clientMutationId: String? = null, + val meta: ChapterMetaType + ) + data class SetChapterMetaPayload( + val clientMutationId: String?, + val meta: ChapterMetaType + ) + fun setChapterMeta( + input: SetChapterMetaInput + ): SetChapterMetaPayload { + val (clientMutationId, meta) = input + + Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value) + + return SetChapterMetaPayload(clientMutationId, meta) + } + + data class DeleteChapterMetaInput( + val clientMutationId: String? = null, + val chapterId: Int, + val key: String + ) + data class DeleteChapterMetaPayload( + val clientMutationId: String?, + val meta: ChapterMetaType? + ) + fun deleteChapterMeta( + input: DeleteChapterMetaInput + ): DeleteChapterMetaPayload { + val (clientMutationId, chapterId, key) = input + + val meta = 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) } + + if (meta != null) { + ChapterMetaType(meta) + } else { + null + } + } + + return DeleteChapterMetaPayload(clientMutationId, meta) + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt new file mode 100644 index 00000000..cfa5a0a1 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt @@ -0,0 +1,10 @@ +package suwayomi.tachidesk.graphql.mutations + +/** + * TODO Mutations + * - Install + * - Update + * - Uninstall + * - Check for updates (global mutation?) + */ +class ExtensionMutation 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 fb2dc531..71618f73 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt @@ -3,12 +3,16 @@ package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import com.expediagroup.graphql.server.extensions.getValuesFromDataLoader import graphql.schema.DataFetchingEnvironment +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.queries.MangaQuery +import suwayomi.tachidesk.graphql.types.MangaMetaType import suwayomi.tachidesk.graphql.types.MangaType import suwayomi.tachidesk.manga.impl.Manga +import suwayomi.tachidesk.manga.model.table.MangaMetaTable import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.server.JavalinSetup.future import java.util.concurrent.CompletableFuture @@ -17,11 +21,8 @@ import java.util.concurrent.CompletableFuture * TODO Mutations * - Add to category * - Remove from category - * - Check for updates * - Download x(all = -1) chapters * - Delete read/all downloaded chapters - * - Add/update meta - * - Delete meta */ class MangaMutation { data class UpdateMangaPatch( @@ -112,4 +113,52 @@ class MangaMutation { ) } } + + data class SetMangaMetaInput( + val clientMutationId: String? = null, + val meta: MangaMetaType + ) + data class SetMangaMetaPayload( + val clientMutationId: String?, + val meta: MangaMetaType + ) + fun setMangaMeta( + input: SetMangaMetaInput + ): SetMangaMetaPayload { + val (clientMutationId, meta) = input + + Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value) + + return SetMangaMetaPayload(clientMutationId, meta) + } + + data class DeleteMangaMetaInput( + val clientMutationId: String? = null, + val mangaId: Int, + val key: String + ) + data class DeleteMangaMetaPayload( + val clientMutationId: String?, + val meta: MangaMetaType? + ) + fun deleteMangaMeta( + input: DeleteMangaMetaInput + ): DeleteMangaMetaPayload { + val (clientMutationId, mangaId, key) = input + + val meta = 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) } + + if (meta != null) { + MangaMetaType(meta) + } else { + null + } + } + + return DeleteMangaMetaPayload(clientMutationId, meta) + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt new file mode 100644 index 00000000..8a55081b --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt @@ -0,0 +1,60 @@ +package suwayomi.tachidesk.graphql.mutations + +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.types.GlobalMetaType +import suwayomi.tachidesk.manga.model.table.MangaMetaTable + +class MetaMutation { + + data class SetGlobalMetaInput( + val clientMutationId: String? = null, + val meta: GlobalMetaType + ) + data class SetGlobalMetaPayload( + val clientMutationId: String?, + val meta: GlobalMetaType + ) + fun setGlobalMeta( + input: SetGlobalMetaInput + ): SetGlobalMetaPayload { + val (clientMutationId, meta) = input + + GlobalMeta.modifyMeta(meta.key, meta.value) + + return SetGlobalMetaPayload(clientMutationId, meta) + } + + data class DeleteGlobalMetaInput( + val clientMutationId: String? = null, + val key: String + ) + data class DeleteGlobalMetaPayload( + val clientMutationId: String?, + val meta: GlobalMetaType? + ) + fun deleteGlobalMeta( + input: DeleteGlobalMetaInput + ): DeleteGlobalMetaPayload { + val (clientMutationId, key) = input + + val meta = transaction { + val meta = GlobalMetaTable.select { MangaMetaTable.key eq key } + .firstOrNull() + + GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key } + + if (meta != null) { + GlobalMetaType(meta) + } else { + null + } + } + + return 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 new file mode 100644 index 00000000..0c8e1182 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt @@ -0,0 +1,8 @@ +package suwayomi.tachidesk.graphql.mutations + +/** + * TODO Mutations + * - Browse with filters + * - Configure settings + */ +class SourceMutation diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt index 94d5cfd7..d1c64d20 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt @@ -39,18 +39,6 @@ import suwayomi.tachidesk.graphql.types.CategoryType import suwayomi.tachidesk.manga.model.table.CategoryTable import java.util.concurrent.CompletableFuture -/** - * TODO Queries - * - * TODO Mutations - * - Name - * - Order - * - Default - * - Create - * - Delete - * - Add/update meta - * - Delete meta - */ class CategoryQuery { fun category(dataFetchingEnvironment: DataFetchingEnvironment, id: Int): CompletableFuture { return dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id) 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 4a87a983..1071e3c3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt @@ -38,15 +38,6 @@ import suwayomi.tachidesk.graphql.types.ExtensionType import suwayomi.tachidesk.manga.model.table.ExtensionTable import java.util.concurrent.CompletableFuture -/** - * TODO Queries - * - * TODO Mutations - * - Install - * - Update - * - Uninstall - * - Check for updates (global mutation?) - */ class ExtensionQuery { fun extension(dataFetchingEnvironment: DataFetchingEnvironment, pkgName: String): CompletableFuture { return dataFetchingEnvironment.getValueFromDataLoader("ExtensionDataLoader", pkgName) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt index d3aeb10e..21d9eb1d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt @@ -42,9 +42,6 @@ import suwayomi.tachidesk.manga.model.table.MangaStatus import suwayomi.tachidesk.manga.model.table.MangaTable import java.util.concurrent.CompletableFuture -/** - * TODO Queries - */ class MangaQuery { fun manga(dataFetchingEnvironment: DataFetchingEnvironment, id: Int): CompletableFuture { return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt index f934ce2e..8c54d58b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt @@ -31,25 +31,16 @@ import suwayomi.tachidesk.graphql.server.primitives.QueryResults import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.maybeSwap -import suwayomi.tachidesk.graphql.types.GlobalMetaItem -import suwayomi.tachidesk.graphql.types.MetaItem -import suwayomi.tachidesk.graphql.types.MetaNodeList +import suwayomi.tachidesk.graphql.types.GlobalMetaNodeList +import suwayomi.tachidesk.graphql.types.GlobalMetaType import java.util.concurrent.CompletableFuture -/** - * TODO Queries - * - * TODO Mutations - * - Add/update meta - * - Delete meta - * - */ class MetaQuery { - fun meta(dataFetchingEnvironment: DataFetchingEnvironment, key: String): CompletableFuture { - return dataFetchingEnvironment.getValueFromDataLoader("GlobalMetaDataLoader", key) + fun meta(dataFetchingEnvironment: DataFetchingEnvironment, key: String): CompletableFuture { + return dataFetchingEnvironment.getValueFromDataLoader("GlobalMetaDataLoader", key) } - enum class MetaOrderBy(override val column: Column>) : OrderBy { + enum class MetaOrderBy(override val column: Column>) : OrderBy { KEY(GlobalMetaTable.key), VALUE(GlobalMetaTable.value); @@ -67,7 +58,7 @@ class MetaQuery { } } - override fun asCursor(type: MetaItem): Cursor { + override fun asCursor(type: GlobalMetaType): Cursor { val value = when (this) { KEY -> type.key VALUE -> type.key + "\\-" + type.value @@ -114,7 +105,7 @@ class MetaQuery { first: Int? = null, last: Int? = null, offset: Int? = null - ): MetaNodeList { + ): GlobalMetaNodeList { val queryResults = transaction { val res = GlobalMetaTable.selectAll() @@ -157,24 +148,24 @@ class MetaQuery { QueryResults(total, firstResult, lastResult, res.toList()) } - val getAsCursor: (MetaItem) -> Cursor = (orderBy ?: MetaOrderBy.KEY)::asCursor + val getAsCursor: (GlobalMetaType) -> Cursor = (orderBy ?: MetaOrderBy.KEY)::asCursor - val resultsAsType = queryResults.results.map { GlobalMetaItem(it) } + val resultsAsType = queryResults.results.map { GlobalMetaType(it) } - return MetaNodeList( + return GlobalMetaNodeList( resultsAsType, if (resultsAsType.isEmpty()) { emptyList() } else { listOfNotNull( resultsAsType.firstOrNull()?.let { - MetaNodeList.MetaEdge( + GlobalMetaNodeList.MetaEdge( getAsCursor(it), it ) }, resultsAsType.lastOrNull()?.let { - MetaNodeList.MetaEdge( + GlobalMetaNodeList.MetaEdge( getAsCursor(it), it ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt index 84114389..dfd9e852 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt @@ -39,14 +39,6 @@ import suwayomi.tachidesk.graphql.types.SourceType import suwayomi.tachidesk.manga.model.table.SourceTable import java.util.concurrent.CompletableFuture -/** - * TODO Queries - * - * TODO Mutations - * - Browse with filters - * - Configure settings - * - */ class SourceQuery { fun source(dataFetchingEnvironment: DataFetchingEnvironment, id: Long): CompletableFuture { return dataFetchingEnvironment.getValueFromDataLoader("SourceDataLoader", id) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskDataLoaderRegistryFactory.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskDataLoaderRegistryFactory.kt index 033e23d6..3621edf2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskDataLoaderRegistryFactory.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskDataLoaderRegistryFactory.kt @@ -9,6 +9,7 @@ package suwayomi.tachidesk.graphql.server import com.expediagroup.graphql.dataloader.KotlinDataLoaderRegistryFactory import suwayomi.tachidesk.graphql.dataLoaders.CategoriesForMangaDataLoader +import suwayomi.tachidesk.graphql.dataLoaders.CategoryDataLoader import suwayomi.tachidesk.graphql.dataLoaders.CategoryMetaDataLoader import suwayomi.tachidesk.graphql.dataLoaders.ChapterDataLoader import suwayomi.tachidesk.graphql.dataLoaders.ChapterMetaDataLoader @@ -33,6 +34,7 @@ class TachideskDataLoaderRegistryFactory { ChapterMetaDataLoader(), MangaMetaDataLoader(), MangaForCategoryDataLoader(), + CategoryDataLoader(), CategoryMetaDataLoader(), CategoriesForMangaDataLoader(), SourceDataLoader(), diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt index b8e1cb3e..e42a0d45 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt @@ -12,8 +12,12 @@ import com.expediagroup.graphql.generator.TopLevelObject import com.expediagroup.graphql.generator.hooks.FlowSubscriptionSchemaGeneratorHooks import com.expediagroup.graphql.generator.toSchema import graphql.schema.GraphQLType +import suwayomi.tachidesk.graphql.mutations.CategoryMutation import suwayomi.tachidesk.graphql.mutations.ChapterMutation +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.queries.CategoryQuery import suwayomi.tachidesk.graphql.queries.ChapterQuery import suwayomi.tachidesk.graphql.queries.ExtensionQuery @@ -42,16 +46,20 @@ val schema = toSchema( hooks = CustomSchemaGeneratorHooks() ), queries = listOf( - TopLevelObject(MangaQuery()), - TopLevelObject(ChapterQuery()), TopLevelObject(CategoryQuery()), - TopLevelObject(SourceQuery()), + TopLevelObject(ChapterQuery()), TopLevelObject(ExtensionQuery()), - TopLevelObject(MetaQuery()) + TopLevelObject(MangaQuery()), + TopLevelObject(MetaQuery()), + TopLevelObject(SourceQuery()) ), mutations = listOf( + TopLevelObject(CategoryMutation()), TopLevelObject(ChapterMutation()), - TopLevelObject(MangaMutation()) + TopLevelObject(ExtensionMutation()), + TopLevelObject(MangaMutation()), + TopLevelObject(MetaMutation()), + TopLevelObject(SourceMutation()) ), subscriptions = listOf( TopLevelObject(DownloadSubscription()) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt index d6e3cd07..c579ed04 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt @@ -35,8 +35,8 @@ class CategoryType( return dataFetchingEnvironment.getValueFromDataLoader("MangaForCategoryDataLoader", id) } - fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { - return dataFetchingEnvironment.getValueFromDataLoader("CategoryMetaDataLoader", id) + fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture> { + return dataFetchingEnvironment.getValueFromDataLoader>("CategoryMetaDataLoader", id) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ChapterType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ChapterType.kt index 321f8cf5..9010bcaa 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ChapterType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/ChapterType.kt @@ -81,8 +81,8 @@ class ChapterType( return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", mangaId) } - fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { - return dataFetchingEnvironment.getValueFromDataLoader("ChapterMetaDataLoader", id) + fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture> { + return dataFetchingEnvironment.getValueFromDataLoader>("ChapterMetaDataLoader", id) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt index 57b2ac6c..bcb07433 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt @@ -93,8 +93,8 @@ class MangaType( return Instant.now().epochSecond.minus(chaptersLastFetchedAt!!) } - fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { - return dataFetchingEnvironment.getValueFromDataLoader("MangaMetaDataLoader", id) + fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture> { + return dataFetchingEnvironment.getValueFromDataLoader>("MangaMetaDataLoader", id) } fun categories(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt index 5c5ba1c0..6d5fee8d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt @@ -1,6 +1,7 @@ package suwayomi.tachidesk.graphql.types -import com.expediagroup.graphql.generator.annotations.GraphQLIgnore +import com.expediagroup.graphql.server.extensions.getValueFromDataLoader +import graphql.schema.DataFetchingEnvironment import org.jetbrains.exposed.sql.ResultRow import suwayomi.tachidesk.global.model.table.GlobalMetaTable import suwayomi.tachidesk.graphql.server.primitives.Cursor @@ -11,44 +12,85 @@ import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.manga.model.table.CategoryMetaTable import suwayomi.tachidesk.manga.model.table.ChapterMetaTable import suwayomi.tachidesk.manga.model.table.MangaMetaTable +import java.util.concurrent.CompletableFuture -open class MetaItem( - val key: String, - val value: String, - @GraphQLIgnore - val ref: Int? -) : Node +interface MetaType : Node { + val key: String + val value: String +} -class ChapterMetaItem( - private val row: ResultRow -) : MetaItem(row[ChapterMetaTable.key], row[ChapterMetaTable.value], row[ChapterMetaTable.ref].value) +class ChapterMetaType( + override val key: String, + override val value: String, + val chapterId: Int +) : MetaType { + constructor(row: ResultRow) : this( + key = row[ChapterMetaTable.key], + value = row[ChapterMetaTable.value], + chapterId = row[ChapterMetaTable.ref].value + ) -class MangaMetaItem( - private val row: ResultRow -) : MetaItem(row[MangaMetaTable.key], row[MangaMetaTable.value], row[MangaMetaTable.ref].value) + fun chapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { + return dataFetchingEnvironment.getValueFromDataLoader("ChapterDataLoader", chapterId) + } +} -class CategoryMetaItem( - private val row: ResultRow -) : MetaItem(row[CategoryMetaTable.key], row[CategoryMetaTable.value], row[CategoryMetaTable.ref].value) +class MangaMetaType( + override val key: String, + override val value: String, + val mangaId: Int +) : MetaType { + constructor(row: ResultRow) : this( + key = row[MangaMetaTable.key], + value = row[MangaMetaTable.value], + mangaId = row[MangaMetaTable.ref].value + ) -class GlobalMetaItem( - private val row: ResultRow -) : MetaItem(row[GlobalMetaTable.key], row[GlobalMetaTable.value], null) + fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { + return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", mangaId) + } +} -data class MetaNodeList( - override val nodes: List, +class CategoryMetaType( + override val key: String, + override val value: String, + val categoryId: Int +) : MetaType { + constructor(row: ResultRow) : this( + key = row[CategoryMetaTable.key], + value = row[CategoryMetaTable.value], + categoryId = row[CategoryMetaTable.ref].value + ) + + fun category(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { + return dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", categoryId) + } +} + +class GlobalMetaType( + override val key: String, + override val value: String +) : MetaType { + constructor(row: ResultRow) : this( + key = row[GlobalMetaTable.key], + value = row[GlobalMetaTable.value] + ) +} + +data class GlobalMetaNodeList( + override val nodes: List, override val edges: List, override val pageInfo: PageInfo, override val totalCount: Int ) : NodeList() { data class MetaEdge( override val cursor: Cursor, - override val node: MetaItem + override val node: GlobalMetaType ) : Edge() companion object { - fun List.toNodeList(): MetaNodeList { - return MetaNodeList( + fun List.toNodeList(): GlobalMetaNodeList { + return GlobalMetaNodeList( nodes = this, edges = getEdges(), pageInfo = PageInfo( @@ -61,7 +103,7 @@ data class MetaNodeList( ) } - private fun List.getEdges(): List { + private fun List.getEdges(): List { if (isEmpty()) return emptyList() return listOf( MetaEdge( diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt index 37b5fc71..17985703 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt @@ -307,6 +307,12 @@ object Chapter { val chapterId = ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) } .first()[ChapterTable.id].value + modifyChapterMeta(chapterId, key, value) + } + } + + fun modifyChapterMeta(chapterId: Int, key: String, value: String) { + transaction { val meta = ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } .firstOrNull()