mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 11:24:35 -05:00
Complete source mutations (#567)
This commit is contained in:
@@ -1,8 +1,112 @@
|
|||||||
package suwayomi.tachidesk.graphql.mutations
|
package suwayomi.tachidesk.graphql.mutations
|
||||||
|
|
||||||
/**
|
import org.jetbrains.exposed.sql.select
|
||||||
* TODO Mutations
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
* - Browse with filters
|
import suwayomi.tachidesk.graphql.types.MangaType
|
||||||
* - Configure settings
|
import suwayomi.tachidesk.graphql.types.PreferenceObject
|
||||||
*/
|
import suwayomi.tachidesk.manga.impl.MangaList.insertOrGet
|
||||||
class SourceMutation
|
import suwayomi.tachidesk.manga.impl.Search
|
||||||
|
import suwayomi.tachidesk.manga.impl.Source
|
||||||
|
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
||||||
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource
|
||||||
|
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||||
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
class SourceMutation {
|
||||||
|
|
||||||
|
enum class FetchSourceMangaType {
|
||||||
|
SEARCH,
|
||||||
|
POPULAR,
|
||||||
|
LATEST
|
||||||
|
}
|
||||||
|
data class FilterChange(
|
||||||
|
val position: Int,
|
||||||
|
val state: String
|
||||||
|
)
|
||||||
|
data class FetchSourceMangaInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val source: Long,
|
||||||
|
val type: FetchSourceMangaType,
|
||||||
|
val page: Int,
|
||||||
|
val query: String? = null,
|
||||||
|
val filters: List<FilterChange>? = null
|
||||||
|
)
|
||||||
|
data class FetchSourceMangaPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val mangas: List<MangaType>,
|
||||||
|
val hasNextPage: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fetchSourceManga(
|
||||||
|
input: FetchSourceMangaInput
|
||||||
|
): CompletableFuture<FetchSourceMangaPayload> {
|
||||||
|
val (clientMutationId, sourceId, type, page, query, filters) = input
|
||||||
|
|
||||||
|
return future {
|
||||||
|
val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!!
|
||||||
|
val mangasPage = when (type) {
|
||||||
|
FetchSourceMangaType.SEARCH -> {
|
||||||
|
source.fetchSearchManga(
|
||||||
|
page = page,
|
||||||
|
query = query.orEmpty(),
|
||||||
|
filters = Search.buildFilterList(
|
||||||
|
sourceId = sourceId,
|
||||||
|
changes = filters?.map { Search.FilterChange(it.position, it.state) }
|
||||||
|
.orEmpty()
|
||||||
|
)
|
||||||
|
).awaitSingle()
|
||||||
|
}
|
||||||
|
FetchSourceMangaType.POPULAR -> {
|
||||||
|
source.fetchPopularManga(page).awaitSingle()
|
||||||
|
}
|
||||||
|
FetchSourceMangaType.LATEST -> {
|
||||||
|
if (!source.supportsLatest) throw Exception("Source does not support latest")
|
||||||
|
source.fetchLatestUpdates(page).awaitSingle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val mangaIds = mangasPage.insertOrGet(sourceId)
|
||||||
|
|
||||||
|
val mangas = transaction {
|
||||||
|
MangaTable.select { MangaTable.id inList mangaIds }
|
||||||
|
.map { MangaType(it) }
|
||||||
|
}.sortedBy {
|
||||||
|
mangaIds.indexOf(it.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
FetchSourceMangaPayload(
|
||||||
|
clientMutationId = clientMutationId,
|
||||||
|
mangas = mangas,
|
||||||
|
hasNextPage = mangasPage.hasNextPage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SourcePreferenceChange(
|
||||||
|
val position: Int,
|
||||||
|
val state: String
|
||||||
|
)
|
||||||
|
data class UpdateSourcePreferenceInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val source: Long,
|
||||||
|
val change: SourcePreferenceChange
|
||||||
|
)
|
||||||
|
data class UpdateSourcePreferencePayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val preferences: List<PreferenceObject>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun updateSourcePreference(
|
||||||
|
input: UpdateSourcePreferenceInput
|
||||||
|
): UpdateSourcePreferencePayload {
|
||||||
|
val (clientMutationId, sourceId, change) = input
|
||||||
|
|
||||||
|
Source.setSourcePreference(sourceId, Source.SourcePreferenceChange(change.position, change.state))
|
||||||
|
|
||||||
|
return UpdateSourcePreferencePayload(
|
||||||
|
clientMutationId = clientMutationId,
|
||||||
|
preferences = Source.getSourcePreferences(sourceId).map { PreferenceObject(it.type, it.props) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.expediagroup.graphql.generator.SchemaGeneratorConfig
|
|||||||
import com.expediagroup.graphql.generator.TopLevelObject
|
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.scalars.ExtendedScalars
|
||||||
import graphql.schema.GraphQLType
|
import graphql.schema.GraphQLType
|
||||||
import suwayomi.tachidesk.graphql.mutations.CategoryMutation
|
import suwayomi.tachidesk.graphql.mutations.CategoryMutation
|
||||||
import suwayomi.tachidesk.graphql.mutations.ChapterMutation
|
import suwayomi.tachidesk.graphql.mutations.ChapterMutation
|
||||||
@@ -35,6 +36,7 @@ class CustomSchemaGeneratorHooks : FlowSubscriptionSchemaGeneratorHooks() {
|
|||||||
override fun willGenerateGraphQLType(type: KType): GraphQLType? = when (type.classifier as? KClass<*>) {
|
override fun willGenerateGraphQLType(type: KType): GraphQLType? = when (type.classifier as? KClass<*>) {
|
||||||
Long::class -> GraphQLLongAsString // encode to string for JS
|
Long::class -> GraphQLLongAsString // encode to string for JS
|
||||||
Cursor::class -> GraphQLCursor
|
Cursor::class -> GraphQLCursor
|
||||||
|
Any::class -> ExtendedScalars.Json
|
||||||
else -> super.willGenerateGraphQLType(type)
|
else -> super.willGenerateGraphQLType(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import suwayomi.tachidesk.graphql.server.primitives.Edge
|
|||||||
import suwayomi.tachidesk.graphql.server.primitives.Node
|
import suwayomi.tachidesk.graphql.server.primitives.Node
|
||||||
import suwayomi.tachidesk.graphql.server.primitives.NodeList
|
import suwayomi.tachidesk.graphql.server.primitives.NodeList
|
||||||
import suwayomi.tachidesk.graphql.server.primitives.PageInfo
|
import suwayomi.tachidesk.graphql.server.primitives.PageInfo
|
||||||
|
import suwayomi.tachidesk.manga.impl.Search
|
||||||
|
import suwayomi.tachidesk.manga.impl.Source
|
||||||
import suwayomi.tachidesk.manga.impl.extension.Extension
|
import suwayomi.tachidesk.manga.impl.extension.Extension
|
||||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
|
||||||
@@ -64,6 +66,14 @@ class SourceType(
|
|||||||
fun extension(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ExtensionType> {
|
fun extension(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ExtensionType> {
|
||||||
return dataFetchingEnvironment.getValueFromDataLoader<Long, ExtensionType>("ExtensionForSourceDataLoader", id)
|
return dataFetchingEnvironment.getValueFromDataLoader<Long, ExtensionType>("ExtensionForSourceDataLoader", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun preferences(): List<PreferenceObject> {
|
||||||
|
return Source.getSourcePreferences(id).map { PreferenceObject(it.type, it.props) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filters(): List<FilterObject> {
|
||||||
|
return Search.getFilterList(id, false).map { FilterObject(it.type, it.filter) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun SourceType(row: ResultRow): SourceType? {
|
fun SourceType(row: ResultRow): SourceType? {
|
||||||
@@ -122,3 +132,13 @@ data class SourceNodeList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class PreferenceObject(
|
||||||
|
val type: String,
|
||||||
|
val props: Any
|
||||||
|
)
|
||||||
|
|
||||||
|
data class FilterObject(
|
||||||
|
val type: String,
|
||||||
|
val filter: Any
|
||||||
|
)
|
||||||
|
|||||||
@@ -44,6 +44,34 @@ object MangaList {
|
|||||||
return mangasPage.processEntries(sourceId)
|
return mangasPage.processEntries(sourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun MangasPage.insertOrGet(sourceId: Long): List<Int> {
|
||||||
|
return transaction {
|
||||||
|
mangas.map { manga ->
|
||||||
|
val mangaEntry = MangaTable.select {
|
||||||
|
(MangaTable.url eq manga.url) and (MangaTable.sourceReference eq sourceId)
|
||||||
|
}.firstOrNull()
|
||||||
|
if (mangaEntry == null) { // create manga entry
|
||||||
|
MangaTable.insertAndGetId {
|
||||||
|
it[url] = manga.url
|
||||||
|
it[title] = manga.title
|
||||||
|
|
||||||
|
it[artist] = manga.artist
|
||||||
|
it[author] = manga.author
|
||||||
|
it[description] = manga.description
|
||||||
|
it[genre] = manga.genre
|
||||||
|
it[status] = manga.status
|
||||||
|
it[thumbnail_url] = manga.thumbnail_url
|
||||||
|
it[updateStrategy] = manga.update_strategy.name
|
||||||
|
|
||||||
|
it[sourceReference] = sourceId
|
||||||
|
}.value
|
||||||
|
} else {
|
||||||
|
mangaEntry[MangaTable.id].value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
||||||
val mangasPage = this
|
val mangasPage = this
|
||||||
val mangaList = transaction {
|
val mangaList = transaction {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ object Search {
|
|||||||
return filterList
|
return filterList
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildFilterList(sourceId: Long, changes: List<FilterChange>): FilterList {
|
fun buildFilterList(sourceId: Long, changes: List<FilterChange>): FilterList {
|
||||||
val source = getCatalogueSourceOrStub(sourceId)
|
val source = getCatalogueSourceOrStub(sourceId)
|
||||||
val filterList = source.getFilterList()
|
val filterList = source.getFilterList()
|
||||||
return updateFilterList(filterList, changes)
|
return updateFilterList(filterList, changes)
|
||||||
|
|||||||
Reference in New Issue
Block a user