Rewrite meta and add meta mutations (#556)

This commit is contained in:
Mitchell Syer
2023-05-26 06:15:16 -04:00
committed by GitHub
parent 04a671382a
commit 3f91663ecf
20 changed files with 384 additions and 122 deletions

View File

@@ -21,9 +21,9 @@ import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.manga.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.server.JavalinSetup.future import suwayomi.tachidesk.server.JavalinSetup.future
class CategoryDataLoader : KotlinDataLoader<Int, CategoryType?> { class CategoryDataLoader : KotlinDataLoader<Int, CategoryType> {
override val dataLoaderName = "CategoryDataLoader" override val dataLoaderName = "CategoryDataLoader"
override fun getDataLoader(): DataLoader<Int, CategoryType?> = DataLoaderFactory.newDataLoader { ids -> override fun getDataLoader(): DataLoader<Int, CategoryType> = DataLoaderFactory.newDataLoader { ids ->
future { future {
transaction { transaction {
addLogger(Slf4jSqlDebugLogger) addLogger(Slf4jSqlDebugLogger)

View File

@@ -8,25 +8,23 @@ import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.global.model.table.GlobalMetaTable import suwayomi.tachidesk.global.model.table.GlobalMetaTable
import suwayomi.tachidesk.graphql.types.CategoryMetaItem import suwayomi.tachidesk.graphql.types.CategoryMetaType
import suwayomi.tachidesk.graphql.types.ChapterMetaItem import suwayomi.tachidesk.graphql.types.ChapterMetaType
import suwayomi.tachidesk.graphql.types.GlobalMetaItem import suwayomi.tachidesk.graphql.types.GlobalMetaType
import suwayomi.tachidesk.graphql.types.MangaMetaItem import suwayomi.tachidesk.graphql.types.MangaMetaType
import suwayomi.tachidesk.graphql.types.MetaItem import suwayomi.tachidesk.manga.model.table.CategoryMetaTable
import suwayomi.tachidesk.graphql.types.MetaNodeList
import suwayomi.tachidesk.graphql.types.MetaNodeList.Companion.toNodeList
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
import suwayomi.tachidesk.manga.model.table.MangaMetaTable import suwayomi.tachidesk.manga.model.table.MangaMetaTable
import suwayomi.tachidesk.server.JavalinSetup.future import suwayomi.tachidesk.server.JavalinSetup.future
class GlobalMetaDataLoader : KotlinDataLoader<String, MetaItem?> { class GlobalMetaDataLoader : KotlinDataLoader<String, GlobalMetaType?> {
override val dataLoaderName = "GlobalMetaDataLoader" override val dataLoaderName = "GlobalMetaDataLoader"
override fun getDataLoader(): DataLoader<String, MetaItem?> = DataLoaderFactory.newDataLoader<String, MetaItem?> { ids -> override fun getDataLoader(): DataLoader<String, GlobalMetaType?> = DataLoaderFactory.newDataLoader<String, GlobalMetaType?> { ids ->
future { future {
transaction { transaction {
addLogger(Slf4jSqlDebugLogger) addLogger(Slf4jSqlDebugLogger)
val metasByRefId = GlobalMetaTable.select { GlobalMetaTable.key inList ids } val metasByRefId = GlobalMetaTable.select { GlobalMetaTable.key inList ids }
.map { GlobalMetaItem(it) } .map { GlobalMetaType(it) }
.associateBy { it.key } .associateBy { it.key }
ids.map { metasByRefId[it] } ids.map { metasByRefId[it] }
} }
@@ -34,46 +32,46 @@ class GlobalMetaDataLoader : KotlinDataLoader<String, MetaItem?> {
} }
} }
class ChapterMetaDataLoader : KotlinDataLoader<Int, MetaNodeList> { class ChapterMetaDataLoader : KotlinDataLoader<Int, List<ChapterMetaType>> {
override val dataLoaderName = "ChapterMetaDataLoader" override val dataLoaderName = "ChapterMetaDataLoader"
override fun getDataLoader(): DataLoader<Int, MetaNodeList> = DataLoaderFactory.newDataLoader<Int, MetaNodeList> { ids -> override fun getDataLoader(): DataLoader<Int, List<ChapterMetaType>> = DataLoaderFactory.newDataLoader<Int, List<ChapterMetaType>> { ids ->
future { future {
transaction { transaction {
addLogger(Slf4jSqlDebugLogger) addLogger(Slf4jSqlDebugLogger)
val metasByRefId = ChapterMetaTable.select { ChapterMetaTable.ref inList ids } val metasByRefId = ChapterMetaTable.select { ChapterMetaTable.ref inList ids }
.map { ChapterMetaItem(it) } .map { ChapterMetaType(it) }
.groupBy { it.ref } .groupBy { it.chapterId }
ids.map { (metasByRefId[it] ?: emptyList()).toNodeList() } ids.map { metasByRefId[it].orEmpty() }
} }
} }
} }
} }
class MangaMetaDataLoader : KotlinDataLoader<Int, MetaNodeList> { class MangaMetaDataLoader : KotlinDataLoader<Int, List<MangaMetaType>> {
override val dataLoaderName = "MangaMetaDataLoader" override val dataLoaderName = "MangaMetaDataLoader"
override fun getDataLoader(): DataLoader<Int, MetaNodeList> = DataLoaderFactory.newDataLoader<Int, MetaNodeList> { ids -> override fun getDataLoader(): DataLoader<Int, List<MangaMetaType>> = DataLoaderFactory.newDataLoader<Int, List<MangaMetaType>> { ids ->
future { future {
transaction { transaction {
addLogger(Slf4jSqlDebugLogger) addLogger(Slf4jSqlDebugLogger)
val metasByRefId = MangaMetaTable.select { MangaMetaTable.ref inList ids } val metasByRefId = MangaMetaTable.select { MangaMetaTable.ref inList ids }
.map { MangaMetaItem(it) } .map { MangaMetaType(it) }
.groupBy { it.ref } .groupBy { it.mangaId }
ids.map { (metasByRefId[it] ?: emptyList()).toNodeList() } ids.map { metasByRefId[it].orEmpty() }
} }
} }
} }
} }
class CategoryMetaDataLoader : KotlinDataLoader<Int, MetaNodeList> { class CategoryMetaDataLoader : KotlinDataLoader<Int, List<CategoryMetaType>> {
override val dataLoaderName = "CategoryMetaDataLoader" override val dataLoaderName = "CategoryMetaDataLoader"
override fun getDataLoader(): DataLoader<Int, MetaNodeList> = DataLoaderFactory.newDataLoader<Int, MetaNodeList> { ids -> override fun getDataLoader(): DataLoader<Int, List<CategoryMetaType>> = DataLoaderFactory.newDataLoader<Int, List<CategoryMetaType>> { ids ->
future { future {
transaction { transaction {
addLogger(Slf4jSqlDebugLogger) addLogger(Slf4jSqlDebugLogger)
val metasByRefId = MangaMetaTable.select { MangaMetaTable.ref inList ids } val metasByRefId = CategoryMetaTable.select { CategoryMetaTable.ref inList ids }
.map { CategoryMetaItem(it) } .map { CategoryMetaType(it) }
.groupBy { it.ref } .groupBy { it.categoryId }
ids.map { (metasByRefId[it] ?: emptyList()).toNodeList() } ids.map { metasByRefId[it].orEmpty() }
} }
} }
} }

View File

@@ -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)
}
}

View File

@@ -3,11 +3,16 @@ package suwayomi.tachidesk.graphql.mutations
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
import com.expediagroup.graphql.server.extensions.getValuesFromDataLoader import com.expediagroup.graphql.server.extensions.getValuesFromDataLoader
import graphql.schema.DataFetchingEnvironment 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.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.graphql.types.ChapterMetaType
import suwayomi.tachidesk.graphql.types.ChapterType import suwayomi.tachidesk.graphql.types.ChapterType
import suwayomi.tachidesk.manga.impl.Chapter import suwayomi.tachidesk.manga.impl.Chapter
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.server.JavalinSetup.future import suwayomi.tachidesk.server.JavalinSetup.future
import java.time.Instant import java.time.Instant
@@ -15,7 +20,6 @@ import java.util.concurrent.CompletableFuture
/** /**
* TODO Mutations * TODO Mutations
* - Check for updates?
* - Download * - Download
* - Delete 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)
}
} }

View File

@@ -0,0 +1,10 @@
package suwayomi.tachidesk.graphql.mutations
/**
* TODO Mutations
* - Install
* - Update
* - Uninstall
* - Check for updates (global mutation?)
*/
class ExtensionMutation

View File

@@ -3,12 +3,16 @@ package suwayomi.tachidesk.graphql.mutations
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
import com.expediagroup.graphql.server.extensions.getValuesFromDataLoader import com.expediagroup.graphql.server.extensions.getValuesFromDataLoader
import graphql.schema.DataFetchingEnvironment 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.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update 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.graphql.types.MangaType
import suwayomi.tachidesk.manga.impl.Manga import suwayomi.tachidesk.manga.impl.Manga
import suwayomi.tachidesk.manga.model.table.MangaMetaTable
import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.server.JavalinSetup.future import suwayomi.tachidesk.server.JavalinSetup.future
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
@@ -17,11 +21,8 @@ import java.util.concurrent.CompletableFuture
* TODO Mutations * TODO Mutations
* - Add to category * - Add to category
* - Remove from category * - Remove from category
* - Check for updates
* - Download x(all = -1) chapters * - Download x(all = -1) chapters
* - Delete read/all downloaded chapters * - Delete read/all downloaded chapters
* - Add/update meta
* - Delete meta
*/ */
class MangaMutation { class MangaMutation {
data class UpdateMangaPatch( 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)
}
} }

View File

@@ -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)
}
}

View File

@@ -0,0 +1,8 @@
package suwayomi.tachidesk.graphql.mutations
/**
* TODO Mutations
* - Browse with filters
* - Configure settings
*/
class SourceMutation

View File

@@ -39,18 +39,6 @@ import suwayomi.tachidesk.graphql.types.CategoryType
import suwayomi.tachidesk.manga.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.CategoryTable
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
/**
* TODO Queries
*
* TODO Mutations
* - Name
* - Order
* - Default
* - Create
* - Delete
* - Add/update meta
* - Delete meta
*/
class CategoryQuery { class CategoryQuery {
fun category(dataFetchingEnvironment: DataFetchingEnvironment, id: Int): CompletableFuture<CategoryType?> { fun category(dataFetchingEnvironment: DataFetchingEnvironment, id: Int): CompletableFuture<CategoryType?> {
return dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id) return dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id)

View File

@@ -38,15 +38,6 @@ import suwayomi.tachidesk.graphql.types.ExtensionType
import suwayomi.tachidesk.manga.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.ExtensionTable
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
/**
* TODO Queries
*
* TODO Mutations
* - Install
* - Update
* - Uninstall
* - Check for updates (global mutation?)
*/
class ExtensionQuery { class ExtensionQuery {
fun extension(dataFetchingEnvironment: DataFetchingEnvironment, pkgName: String): CompletableFuture<ExtensionType?> { fun extension(dataFetchingEnvironment: DataFetchingEnvironment, pkgName: String): CompletableFuture<ExtensionType?> {
return dataFetchingEnvironment.getValueFromDataLoader("ExtensionDataLoader", pkgName) return dataFetchingEnvironment.getValueFromDataLoader("ExtensionDataLoader", pkgName)

View File

@@ -42,9 +42,6 @@ import suwayomi.tachidesk.manga.model.table.MangaStatus
import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.manga.model.table.MangaTable
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
/**
* TODO Queries
*/
class MangaQuery { class MangaQuery {
fun manga(dataFetchingEnvironment: DataFetchingEnvironment, id: Int): CompletableFuture<MangaType?> { fun manga(dataFetchingEnvironment: DataFetchingEnvironment, id: Int): CompletableFuture<MangaType?> {
return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id) return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id)

View File

@@ -31,25 +31,16 @@ import suwayomi.tachidesk.graphql.server.primitives.QueryResults
import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique
import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique
import suwayomi.tachidesk.graphql.server.primitives.maybeSwap import suwayomi.tachidesk.graphql.server.primitives.maybeSwap
import suwayomi.tachidesk.graphql.types.GlobalMetaItem import suwayomi.tachidesk.graphql.types.GlobalMetaNodeList
import suwayomi.tachidesk.graphql.types.MetaItem import suwayomi.tachidesk.graphql.types.GlobalMetaType
import suwayomi.tachidesk.graphql.types.MetaNodeList
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
/**
* TODO Queries
*
* TODO Mutations
* - Add/update meta
* - Delete meta
*
*/
class MetaQuery { class MetaQuery {
fun meta(dataFetchingEnvironment: DataFetchingEnvironment, key: String): CompletableFuture<MetaItem?> { fun meta(dataFetchingEnvironment: DataFetchingEnvironment, key: String): CompletableFuture<GlobalMetaType?> {
return dataFetchingEnvironment.getValueFromDataLoader<String, MetaItem?>("GlobalMetaDataLoader", key) return dataFetchingEnvironment.getValueFromDataLoader<String, GlobalMetaType?>("GlobalMetaDataLoader", key)
} }
enum class MetaOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<MetaItem> { enum class MetaOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<GlobalMetaType> {
KEY(GlobalMetaTable.key), KEY(GlobalMetaTable.key),
VALUE(GlobalMetaTable.value); 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) { val value = when (this) {
KEY -> type.key KEY -> type.key
VALUE -> type.key + "\\-" + type.value VALUE -> type.key + "\\-" + type.value
@@ -114,7 +105,7 @@ class MetaQuery {
first: Int? = null, first: Int? = null,
last: Int? = null, last: Int? = null,
offset: Int? = null offset: Int? = null
): MetaNodeList { ): GlobalMetaNodeList {
val queryResults = transaction { val queryResults = transaction {
val res = GlobalMetaTable.selectAll() val res = GlobalMetaTable.selectAll()
@@ -157,24 +148,24 @@ class MetaQuery {
QueryResults(total, firstResult, lastResult, res.toList()) 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, resultsAsType,
if (resultsAsType.isEmpty()) { if (resultsAsType.isEmpty()) {
emptyList() emptyList()
} else { } else {
listOfNotNull( listOfNotNull(
resultsAsType.firstOrNull()?.let { resultsAsType.firstOrNull()?.let {
MetaNodeList.MetaEdge( GlobalMetaNodeList.MetaEdge(
getAsCursor(it), getAsCursor(it),
it it
) )
}, },
resultsAsType.lastOrNull()?.let { resultsAsType.lastOrNull()?.let {
MetaNodeList.MetaEdge( GlobalMetaNodeList.MetaEdge(
getAsCursor(it), getAsCursor(it),
it it
) )

View File

@@ -39,14 +39,6 @@ import suwayomi.tachidesk.graphql.types.SourceType
import suwayomi.tachidesk.manga.model.table.SourceTable import suwayomi.tachidesk.manga.model.table.SourceTable
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
/**
* TODO Queries
*
* TODO Mutations
* - Browse with filters
* - Configure settings
*
*/
class SourceQuery { class SourceQuery {
fun source(dataFetchingEnvironment: DataFetchingEnvironment, id: Long): CompletableFuture<SourceType?> { fun source(dataFetchingEnvironment: DataFetchingEnvironment, id: Long): CompletableFuture<SourceType?> {
return dataFetchingEnvironment.getValueFromDataLoader<Long, SourceType?>("SourceDataLoader", id) return dataFetchingEnvironment.getValueFromDataLoader<Long, SourceType?>("SourceDataLoader", id)

View File

@@ -9,6 +9,7 @@ package suwayomi.tachidesk.graphql.server
import com.expediagroup.graphql.dataloader.KotlinDataLoaderRegistryFactory import com.expediagroup.graphql.dataloader.KotlinDataLoaderRegistryFactory
import suwayomi.tachidesk.graphql.dataLoaders.CategoriesForMangaDataLoader import suwayomi.tachidesk.graphql.dataLoaders.CategoriesForMangaDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.CategoryDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.CategoryMetaDataLoader import suwayomi.tachidesk.graphql.dataLoaders.CategoryMetaDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.ChapterDataLoader import suwayomi.tachidesk.graphql.dataLoaders.ChapterDataLoader
import suwayomi.tachidesk.graphql.dataLoaders.ChapterMetaDataLoader import suwayomi.tachidesk.graphql.dataLoaders.ChapterMetaDataLoader
@@ -33,6 +34,7 @@ class TachideskDataLoaderRegistryFactory {
ChapterMetaDataLoader(), ChapterMetaDataLoader(),
MangaMetaDataLoader(), MangaMetaDataLoader(),
MangaForCategoryDataLoader(), MangaForCategoryDataLoader(),
CategoryDataLoader(),
CategoryMetaDataLoader(), CategoryMetaDataLoader(),
CategoriesForMangaDataLoader(), CategoriesForMangaDataLoader(),
SourceDataLoader(), SourceDataLoader(),

View File

@@ -12,8 +12,12 @@ import com.expediagroup.graphql.generator.TopLevelObject
import com.expediagroup.graphql.generator.hooks.FlowSubscriptionSchemaGeneratorHooks import com.expediagroup.graphql.generator.hooks.FlowSubscriptionSchemaGeneratorHooks
import com.expediagroup.graphql.generator.toSchema import com.expediagroup.graphql.generator.toSchema
import graphql.schema.GraphQLType import graphql.schema.GraphQLType
import suwayomi.tachidesk.graphql.mutations.CategoryMutation
import suwayomi.tachidesk.graphql.mutations.ChapterMutation import suwayomi.tachidesk.graphql.mutations.ChapterMutation
import suwayomi.tachidesk.graphql.mutations.ExtensionMutation
import suwayomi.tachidesk.graphql.mutations.MangaMutation 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.CategoryQuery
import suwayomi.tachidesk.graphql.queries.ChapterQuery import suwayomi.tachidesk.graphql.queries.ChapterQuery
import suwayomi.tachidesk.graphql.queries.ExtensionQuery import suwayomi.tachidesk.graphql.queries.ExtensionQuery
@@ -42,16 +46,20 @@ val schema = toSchema(
hooks = CustomSchemaGeneratorHooks() hooks = CustomSchemaGeneratorHooks()
), ),
queries = listOf( queries = listOf(
TopLevelObject(MangaQuery()),
TopLevelObject(ChapterQuery()),
TopLevelObject(CategoryQuery()), TopLevelObject(CategoryQuery()),
TopLevelObject(SourceQuery()), TopLevelObject(ChapterQuery()),
TopLevelObject(ExtensionQuery()), TopLevelObject(ExtensionQuery()),
TopLevelObject(MetaQuery()) TopLevelObject(MangaQuery()),
TopLevelObject(MetaQuery()),
TopLevelObject(SourceQuery())
), ),
mutations = listOf( mutations = listOf(
TopLevelObject(CategoryMutation()),
TopLevelObject(ChapterMutation()), TopLevelObject(ChapterMutation()),
TopLevelObject(MangaMutation()) TopLevelObject(ExtensionMutation()),
TopLevelObject(MangaMutation()),
TopLevelObject(MetaMutation()),
TopLevelObject(SourceMutation())
), ),
subscriptions = listOf( subscriptions = listOf(
TopLevelObject(DownloadSubscription()) TopLevelObject(DownloadSubscription())

View File

@@ -35,8 +35,8 @@ class CategoryType(
return dataFetchingEnvironment.getValueFromDataLoader<Int, MangaNodeList>("MangaForCategoryDataLoader", id) return dataFetchingEnvironment.getValueFromDataLoader<Int, MangaNodeList>("MangaForCategoryDataLoader", id)
} }
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MetaNodeList> { fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<CategoryMetaType>> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, MetaNodeList>("CategoryMetaDataLoader", id) return dataFetchingEnvironment.getValueFromDataLoader<Int, List<CategoryMetaType>>("CategoryMetaDataLoader", id)
} }
} }

View File

@@ -81,8 +81,8 @@ class ChapterType(
return dataFetchingEnvironment.getValueFromDataLoader<Int, MangaType>("MangaDataLoader", mangaId) return dataFetchingEnvironment.getValueFromDataLoader<Int, MangaType>("MangaDataLoader", mangaId)
} }
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MetaNodeList> { fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<ChapterMetaType>> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, MetaNodeList>("ChapterMetaDataLoader", id) return dataFetchingEnvironment.getValueFromDataLoader<Int, List<ChapterMetaType>>("ChapterMetaDataLoader", id)
} }
} }

View File

@@ -93,8 +93,8 @@ class MangaType(
return Instant.now().epochSecond.minus(chaptersLastFetchedAt!!) return Instant.now().epochSecond.minus(chaptersLastFetchedAt!!)
} }
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MetaNodeList> { fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<MangaMetaType>> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, MetaNodeList>("MangaMetaDataLoader", id) return dataFetchingEnvironment.getValueFromDataLoader<Int, List<MangaMetaType>>("MangaMetaDataLoader", id)
} }
fun categories(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<CategoryNodeList> { fun categories(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<CategoryNodeList> {

View File

@@ -1,6 +1,7 @@
package suwayomi.tachidesk.graphql.types 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 org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.global.model.table.GlobalMetaTable import suwayomi.tachidesk.global.model.table.GlobalMetaTable
import suwayomi.tachidesk.graphql.server.primitives.Cursor 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.CategoryMetaTable
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
import suwayomi.tachidesk.manga.model.table.MangaMetaTable import suwayomi.tachidesk.manga.model.table.MangaMetaTable
import java.util.concurrent.CompletableFuture
open class MetaItem( interface MetaType : Node {
val key: String, val key: String
val value: String, val value: String
@GraphQLIgnore }
val ref: Int?
) : Node
class ChapterMetaItem( class ChapterMetaType(
private val row: ResultRow override val key: String,
) : MetaItem(row[ChapterMetaTable.key], row[ChapterMetaTable.value], row[ChapterMetaTable.ref].value) 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( fun chapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType> {
private val row: ResultRow return dataFetchingEnvironment.getValueFromDataLoader<Int, ChapterType>("ChapterDataLoader", chapterId)
) : MetaItem(row[MangaMetaTable.key], row[MangaMetaTable.value], row[MangaMetaTable.ref].value) }
}
class CategoryMetaItem( class MangaMetaType(
private val row: ResultRow override val key: String,
) : MetaItem(row[CategoryMetaTable.key], row[CategoryMetaTable.value], row[CategoryMetaTable.ref].value) 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( fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaType> {
private val row: ResultRow return dataFetchingEnvironment.getValueFromDataLoader<Int, MangaType>("MangaDataLoader", mangaId)
) : MetaItem(row[GlobalMetaTable.key], row[GlobalMetaTable.value], null) }
}
data class MetaNodeList( class CategoryMetaType(
override val nodes: List<MetaItem>, 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<CategoryType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, CategoryType>("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<GlobalMetaType>,
override val edges: List<MetaEdge>, override val edges: List<MetaEdge>,
override val pageInfo: PageInfo, override val pageInfo: PageInfo,
override val totalCount: Int override val totalCount: Int
) : NodeList() { ) : NodeList() {
data class MetaEdge( data class MetaEdge(
override val cursor: Cursor, override val cursor: Cursor,
override val node: MetaItem override val node: GlobalMetaType
) : Edge() ) : Edge()
companion object { companion object {
fun List<MetaItem>.toNodeList(): MetaNodeList { fun List<GlobalMetaType>.toNodeList(): GlobalMetaNodeList {
return MetaNodeList( return GlobalMetaNodeList(
nodes = this, nodes = this,
edges = getEdges(), edges = getEdges(),
pageInfo = PageInfo( pageInfo = PageInfo(
@@ -61,7 +103,7 @@ data class MetaNodeList(
) )
} }
private fun List<MetaItem>.getEdges(): List<MetaEdge> { private fun List<GlobalMetaType>.getEdges(): List<MetaEdge> {
if (isEmpty()) return emptyList() if (isEmpty()) return emptyList()
return listOf( return listOf(
MetaEdge( MetaEdge(

View File

@@ -307,6 +307,12 @@ object Chapter {
val chapterId = val chapterId =
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) } ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
.first()[ChapterTable.id].value .first()[ChapterTable.id].value
modifyChapterMeta(chapterId, key, value)
}
}
fun modifyChapterMeta(chapterId: Int, key: String, value: String) {
transaction {
val meta = val meta =
ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
.firstOrNull() .firstOrNull()