diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt new file mode 100644 index 000000000..61146aad4 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt @@ -0,0 +1,49 @@ +/* + * 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.dataLoaders + +import com.expediagroup.graphql.dataloader.KotlinDataLoader +import org.dataloader.DataLoader +import org.dataloader.DataLoaderFactory +import org.jetbrains.exposed.sql.StdOutSqlLogger +import org.jetbrains.exposed.sql.addLogger +import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.types.CategoryType +import suwayomi.tachidesk.manga.model.table.CategoryMangaTable +import suwayomi.tachidesk.manga.model.table.CategoryTable +import java.util.concurrent.CompletableFuture + +class CategoryDataLoader : KotlinDataLoader { + override val dataLoaderName = "CategoryDataLoader" + override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> + CompletableFuture.supplyAsync { + transaction { + addLogger(StdOutSqlLogger) + CategoryTable.select { CategoryTable.id inList ids } + .map { CategoryType(it) } + } + } + } +} + +class CategoriesForMangaDataLoader : KotlinDataLoader> { + override val dataLoaderName = "CategoriesForMangaDataLoader" + override fun getDataLoader(): DataLoader> = DataLoaderFactory.newDataLoader> { ids -> + CompletableFuture.supplyAsync { + transaction { + addLogger(StdOutSqlLogger) + val itemsByRef = CategoryMangaTable.innerJoin(CategoryTable).select { CategoryMangaTable.manga inList ids } + .map { Pair(it[CategoryMangaTable.manga].value, CategoryType(it)) } + .groupBy { it.first } + .mapValues { it.value.map { pair -> pair.second } } + ids.map { itemsByRef[it] ?: emptyList() } + } + } + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt index d4804380f..b3e9fb8dc 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt @@ -10,11 +10,13 @@ package suwayomi.tachidesk.graphql.dataLoaders import com.expediagroup.graphql.dataloader.KotlinDataLoader import org.dataloader.DataLoader import org.dataloader.DataLoaderFactory +import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList import org.jetbrains.exposed.sql.StdOutSqlLogger import org.jetbrains.exposed.sql.addLogger import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.graphql.types.MangaType +import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.MangaTable import java.util.concurrent.CompletableFuture @@ -30,3 +32,19 @@ class MangaDataLoader : KotlinDataLoader { } } } + +class MangaForCategoryDataLoader : KotlinDataLoader> { + override val dataLoaderName = "MangaForCategoryDataLoader" + override fun getDataLoader(): DataLoader> = DataLoaderFactory.newDataLoader> { ids -> + CompletableFuture.supplyAsync { + transaction { + addLogger(StdOutSqlLogger) + val itemsByRef = CategoryMangaTable.innerJoin(MangaTable).select { CategoryMangaTable.category inList ids } + .map { Pair(it[CategoryMangaTable.category].value, MangaType(it)) } + .groupBy { it.first } + .mapValues { it.value.map { pair -> pair.second } } + ids.map { itemsByRef[it] ?: emptyList() } + } + } + } +} 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 1ac2b6acf..57ea723f2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt @@ -7,6 +7,7 @@ import org.jetbrains.exposed.sql.StdOutSqlLogger import org.jetbrains.exposed.sql.addLogger import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.types.CategoryMetaItem import suwayomi.tachidesk.graphql.types.ChapterMetaItem import suwayomi.tachidesk.graphql.types.MangaMetaItem import suwayomi.tachidesk.graphql.types.MetaType @@ -20,10 +21,10 @@ class ChapterMetaDataLoader : KotlinDataLoader { CompletableFuture.supplyAsync { transaction { addLogger(StdOutSqlLogger) - val metasByChapterId = ChapterMetaTable.select { ChapterMetaTable.ref inList ids } + val metasByRefId = ChapterMetaTable.select { ChapterMetaTable.ref inList ids } .map { ChapterMetaItem(it) } .groupBy { it.ref } - ids.map { metasByChapterId[it] ?: emptyList() } + ids.map { metasByRefId[it] ?: emptyList() } } } } @@ -35,10 +36,25 @@ class MangaMetaDataLoader : KotlinDataLoader { CompletableFuture.supplyAsync { transaction { addLogger(StdOutSqlLogger) - val metasByChapterId = MangaMetaTable.select { MangaMetaTable.ref inList ids } + val metasByRefId = MangaMetaTable.select { MangaMetaTable.ref inList ids } .map { MangaMetaItem(it) } .groupBy { it.ref } - ids.map { metasByChapterId[it] ?: emptyList() } + ids.map { metasByRefId[it] ?: emptyList() } + } + } + } +} + +class CategoryMetaDataLoader : KotlinDataLoader { + override val dataLoaderName = "CategoryMetaDataLoader" + override fun getDataLoader(): DataLoader = DataLoaderFactory.newDataLoader { ids -> + CompletableFuture.supplyAsync { + transaction { + addLogger(StdOutSqlLogger) + val metasByRefId = MangaMetaTable.select { MangaMetaTable.ref inList ids } + .map { CategoryMetaItem(it) } + .groupBy { it.ref } + ids.map { metasByRefId[it] ?: emptyList() } } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TachideskDataLoaderRegistryFactory.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TachideskDataLoaderRegistryFactory.kt index a0f5cf389..a035f03d8 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TachideskDataLoaderRegistryFactory.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TachideskDataLoaderRegistryFactory.kt @@ -14,5 +14,8 @@ val tachideskDataLoaderRegistryFactory = KotlinDataLoaderRegistryFactory( ChapterDataLoader(), ChaptersForMangaDataLoader(), ChapterMetaDataLoader(), - MangaMetaDataLoader() + MangaMetaDataLoader(), + MangaForCategoryDataLoader(), + CategoryMetaDataLoader(), + CategoriesForMangaDataLoader() ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/impl/TachideskGraphQLSchema.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/impl/TachideskGraphQLSchema.kt index e4d7763d6..257426c25 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/impl/TachideskGraphQLSchema.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/impl/TachideskGraphQLSchema.kt @@ -16,6 +16,7 @@ import graphql.GraphQL import graphql.scalars.ExtendedScalars import graphql.schema.GraphQLType import suwayomi.tachidesk.graphql.mutations.ChapterMutation +import suwayomi.tachidesk.graphql.queries.CategoryQuery import suwayomi.tachidesk.graphql.queries.ChapterQuery import suwayomi.tachidesk.graphql.queries.MangaQuery import kotlin.reflect.KClass @@ -36,7 +37,8 @@ val schema = toSchema( ), queries = listOf( TopLevelObject(MangaQuery()), - TopLevelObject(ChapterQuery()) + TopLevelObject(ChapterQuery()), + TopLevelObject(CategoryQuery()) ), mutations = listOf( TopLevelObject(ChapterMutation()) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt new file mode 100644 index 000000000..15f73722d --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt @@ -0,0 +1,30 @@ +/* + * 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.queries + +import com.expediagroup.graphql.server.extensions.getValueFromDataLoader +import graphql.schema.DataFetchingEnvironment +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.graphql.types.CategoryType +import suwayomi.tachidesk.manga.model.table.CategoryTable +import java.util.concurrent.CompletableFuture + +class CategoryQuery { + fun category(dataFetchingEnvironment: DataFetchingEnvironment, id: Int): CompletableFuture { + return dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id) + } + + fun categories(): List { + val results = transaction { + CategoryTable.selectAll().toList() + } + + return results.map { CategoryType(it) } + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt new file mode 100644 index 000000000..ada7129e5 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/CategoryType.kt @@ -0,0 +1,36 @@ +/* + * 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.types + +import com.expediagroup.graphql.server.extensions.getValueFromDataLoader +import graphql.schema.DataFetchingEnvironment +import org.jetbrains.exposed.sql.ResultRow +import suwayomi.tachidesk.manga.model.table.CategoryTable +import java.util.concurrent.CompletableFuture + +class CategoryType( + val id: Int, + val order: Int, + val name: String, + val default: Boolean +) { + constructor(row: ResultRow) : this( + row[CategoryTable.id].value, + row[CategoryTable.order], + row[CategoryTable.name], + row[CategoryTable.isDefault] + ) + + fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture> { + return dataFetchingEnvironment.getValueFromDataLoader>("MangaForCategoryDataLoader", id) + } + + fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { + return dataFetchingEnvironment.getValueFromDataLoader("CategoryMetaDataLoader", 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 b00dadfa5..40cd22223 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt @@ -71,4 +71,8 @@ class MangaType( fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { return dataFetchingEnvironment.getValueFromDataLoader("MangaMetaDataLoader", id) } + + fun categories(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture> { + return dataFetchingEnvironment.getValueFromDataLoader>("CategoriesForMangaDataLoader", id) + } } 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 d9826d726..a8c192405 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt @@ -2,6 +2,7 @@ package suwayomi.tachidesk.graphql.types import com.expediagroup.graphql.generator.annotations.GraphQLIgnore import org.jetbrains.exposed.sql.ResultRow +import suwayomi.tachidesk.manga.model.table.CategoryMetaTable import suwayomi.tachidesk.manga.model.table.ChapterMetaTable import suwayomi.tachidesk.manga.model.table.MangaMetaTable @@ -21,3 +22,7 @@ class ChapterMetaItem( class MangaMetaItem( private val row: ResultRow ) : MetaItem(row[MangaMetaTable.key], row[MangaMetaTable.value], row[MangaMetaTable.ref].value) + +class CategoryMetaItem( + private val row: ResultRow +) : MetaItem(row[CategoryMetaTable.key], row[CategoryMetaTable.value], row[CategoryMetaTable.ref].value)