diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt index ba3f8b9b..132a399e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt @@ -1,13 +1,17 @@ package suwayomi.tachidesk.graphql.mutations import graphql.execution.DataFetcherResult +import org.jetbrains.exposed.sql.LikePattern +import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList +import org.jetbrains.exposed.sql.SqlExpressionBuilder.like import org.jetbrains.exposed.sql.SqlExpressionBuilder.minus import org.jetbrains.exposed.sql.SqlExpressionBuilder.plus import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.insertAndGetId +import org.jetbrains.exposed.sql.or import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update @@ -16,6 +20,7 @@ import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.CategoryMetaType import suwayomi.tachidesk.graphql.types.CategoryType import suwayomi.tachidesk.graphql.types.MangaType +import suwayomi.tachidesk.graphql.types.MetaInput import suwayomi.tachidesk.manga.impl.Category import suwayomi.tachidesk.manga.impl.CategoryManga import suwayomi.tachidesk.manga.impl.util.lang.isEmpty @@ -88,6 +93,138 @@ class CategoryMutation { DeleteCategoryMetaPayload(clientMutationId, meta, category) } + data class SetCategoryMetasItem( + val categoryIds: List, + val metas: List, + ) + + data class SetCategoryMetasInput( + val clientMutationId: String? = null, + val items: List, + ) + + data class SetCategoryMetasPayload( + val clientMutationId: String?, + val metas: List, + val categories: List, + ) + + @RequireAuth + fun setCategoryMetas(input: SetCategoryMetasInput): DataFetcherResult = + asDataFetcherResult { + val (clientMutationId, items) = input + + val metaByCategoryId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.categoryIds.map { categoryId -> categoryId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + + Category.modifyCategoriesMetas(metaByCategoryId) + + val allCategoryIds = metaByCategoryId.keys + val allMetaKeys = metaByCategoryId.values.flatMap { item -> item.keys }.distinct() + + val (updatedMetas, categories) = + transaction { + val updatedMetas = + CategoryMetaTable + .selectAll() + .where { (CategoryMetaTable.ref inList allCategoryIds) and (CategoryMetaTable.key inList allMetaKeys) } + .map { CategoryMetaType(it) } + + val categories = + CategoryTable + .selectAll() + .where { CategoryTable.id inList allCategoryIds } + .map { CategoryType(it) } + .distinctBy { it.id } + + updatedMetas to categories + } + + SetCategoryMetasPayload(clientMutationId, updatedMetas, categories) + } + + data class DeleteCategoryMetasItem( + val categoryIds: List, + val keys: List? = null, + val prefixes: List? = null, + ) + + data class DeleteCategoryMetasInput( + val clientMutationId: String? = null, + val items: List, + ) + + data class DeleteCategoryMetasPayload( + val clientMutationId: String?, + val metas: List, + val categories: List, + ) + + @RequireAuth + fun deleteCategoryMetas(input: DeleteCategoryMetasInput): DataFetcherResult = + asDataFetcherResult { + val (clientMutationId, items) = input + + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allCategoryIds) = + transaction { + val deletedMetas = mutableListOf() + val categoryIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { CategoryMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (CategoryMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (CategoryMetaTable.ref inList item.categoryIds) and metaKeyCondition + + deletedMetas += + CategoryMetaTable + .selectAll() + .where { condition } + .map { CategoryMetaType(it) } + + CategoryMetaTable.deleteWhere { condition } + categoryIds += item.categoryIds + } + + deletedMetas to categoryIds + } + + val categories = + transaction { + CategoryTable + .selectAll() + .where { CategoryTable.id inList allCategoryIds } + .map { CategoryType(it) } + .distinctBy { it.id } + } + + DeleteCategoryMetasPayload(clientMutationId, allDeletedMetas, categories) + } + data class UpdateCategoryPatch( val name: String? = null, val default: Boolean? = null, 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 81857ffa..9d33fcf3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt @@ -4,9 +4,14 @@ import graphql.execution.DataFetcherResult import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.jetbrains.exposed.dao.id.EntityID +import org.jetbrains.exposed.sql.LikePattern +import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList +import org.jetbrains.exposed.sql.SqlExpressionBuilder.like import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.or import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.statements.BatchUpdateStatement import org.jetbrains.exposed.sql.transactions.transaction @@ -15,6 +20,7 @@ import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.ChapterMetaType import suwayomi.tachidesk.graphql.types.ChapterType +import suwayomi.tachidesk.graphql.types.MetaInput import suwayomi.tachidesk.graphql.types.SyncConflictInfoType import suwayomi.tachidesk.manga.impl.Chapter import suwayomi.tachidesk.manga.impl.chapter.getChapterDownloadReadyById @@ -246,6 +252,138 @@ class ChapterMutation { DeleteChapterMetaPayload(clientMutationId, meta, chapter) } + data class SetChapterMetasItem( + val chapterIds: List, + val metas: List, + ) + + data class SetChapterMetasInput( + val clientMutationId: String? = null, + val items: List, + ) + + data class SetChapterMetasPayload( + val clientMutationId: String?, + val metas: List, + val chapters: List, + ) + + @RequireAuth + fun setChapterMetas(input: SetChapterMetasInput): DataFetcherResult = + asDataFetcherResult { + val (clientMutationId, items) = input + + val metaByChapterId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.chapterIds.map { chapterId -> chapterId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + + Chapter.modifyChaptersMetas(metaByChapterId) + + val allChapterIds = metaByChapterId.keys + val allMetaKeys = metaByChapterId.values.flatMap { it.keys }.distinct() + + val (updatedMetas, chapters) = + transaction { + val updatedMetas = + ChapterMetaTable + .selectAll() + .where { (ChapterMetaTable.ref inList allChapterIds) and (ChapterMetaTable.key inList allMetaKeys) } + .map { ChapterMetaType(it) } + + val chapters = + ChapterTable + .selectAll() + .where { ChapterTable.id inList allChapterIds } + .map { ChapterType(it) } + .distinctBy { it.id } + + updatedMetas to chapters + } + + SetChapterMetasPayload(clientMutationId, updatedMetas, chapters) + } + + data class DeleteChapterMetasItem( + val chapterIds: List, + val keys: List? = null, + val prefixes: List? = null, + ) + + data class DeleteChapterMetasInput( + val clientMutationId: String? = null, + val items: List, + ) + + data class DeleteChapterMetasPayload( + val clientMutationId: String?, + val metas: List, + val chapters: List, + ) + + @RequireAuth + fun deleteChapterMetas(input: DeleteChapterMetasInput): DataFetcherResult = + asDataFetcherResult { + val (clientMutationId, items) = input + + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allChapterIds) = + transaction { + val deletedMetas = mutableListOf() + val chapterIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { ChapterMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (ChapterMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (ChapterMetaTable.ref inList item.chapterIds) and metaKeyCondition + + deletedMetas += + ChapterMetaTable + .selectAll() + .where { condition } + .map { ChapterMetaType(it) } + + ChapterMetaTable.deleteWhere { condition } + chapterIds += item.chapterIds + } + + deletedMetas to chapterIds + } + + val chapters = + transaction { + ChapterTable + .selectAll() + .where { ChapterTable.id inList allChapterIds } + .map { ChapterType(it) } + .distinctBy { it.id } + } + + DeleteChapterMetasPayload(clientMutationId, allDeletedMetas, chapters) + } + data class FetchChapterPagesInput( val clientMutationId: String? = null, val chapterId: Int, 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 a3ad7b5f..175c790f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt @@ -1,9 +1,14 @@ package suwayomi.tachidesk.graphql.mutations import graphql.execution.DataFetcherResult +import org.jetbrains.exposed.sql.LikePattern +import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList +import org.jetbrains.exposed.sql.SqlExpressionBuilder.like import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.or import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update @@ -11,6 +16,7 @@ import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.MangaMetaType import suwayomi.tachidesk.graphql.types.MangaType +import suwayomi.tachidesk.graphql.types.MetaInput import suwayomi.tachidesk.manga.impl.Library import suwayomi.tachidesk.manga.impl.Manga import suwayomi.tachidesk.manga.impl.update.IUpdater @@ -226,4 +232,138 @@ class MangaMutation { DeleteMangaMetaPayload(clientMutationId, meta, manga) } } + + data class SetMangaMetasItem( + val mangaIds: List, + val metas: List, + ) + + data class SetMangaMetasInput( + val clientMutationId: String? = null, + val items: List, + ) + + data class SetMangaMetasPayload( + val clientMutationId: String?, + val metas: List, + val mangas: List, + ) + + @RequireAuth + fun setMangaMetas(input: SetMangaMetasInput): DataFetcherResult { + val (clientMutationId, items) = input + + return asDataFetcherResult { + val metaByMangaId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.mangaIds.map { mangaId -> mangaId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + + Manga.modifyMangasMetas(metaByMangaId) + + val allMangaIds = metaByMangaId.keys + val allMetaKeys = metaByMangaId.values.flatMap { it.keys }.distinct() + + val (updatedMetas, mangas) = + transaction { + val updatedMetas = + MangaMetaTable + .selectAll() + .where { (MangaMetaTable.ref inList allMangaIds) and (MangaMetaTable.key inList allMetaKeys) } + .map { MangaMetaType(it) } + + val mangas = + MangaTable + .selectAll() + .where { MangaTable.id inList allMangaIds } + .map { MangaType(it) } + .distinctBy { it.id } + + updatedMetas to mangas + } + + SetMangaMetasPayload(clientMutationId, updatedMetas, mangas) + } + } + + data class DeleteMangaMetasItem( + val mangaIds: List, + val keys: List? = null, + val prefixes: List? = null, + ) + + data class DeleteMangaMetasInput( + val clientMutationId: String? = null, + val items: List, + ) + + data class DeleteMangaMetasPayload( + val clientMutationId: String?, + val metas: List, + val mangas: List, + ) + + @RequireAuth + fun deleteMangaMetas(input: DeleteMangaMetasInput): DataFetcherResult { + val (clientMutationId, items) = input + + return asDataFetcherResult { + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allMangaIds) = + transaction { + val deletedMetas = mutableListOf() + val mangaIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { MangaMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (MangaMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (MangaMetaTable.ref inList item.mangaIds) and metaKeyCondition + + deletedMetas += + MangaMetaTable + .selectAll() + .where { condition } + .map { MangaMetaType(it) } + + MangaMetaTable.deleteWhere { condition } + mangaIds += item.mangaIds + } + + deletedMetas to mangaIds + } + + val mangas = + transaction { + MangaTable + .selectAll() + .where { MangaTable.id inList allMangaIds } + .map { MangaType(it) } + .distinctBy { it.id } + } + + DeleteMangaMetasPayload(clientMutationId, allDeletedMetas, mangas) + } + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt index 971b0b41..f2926528 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt @@ -1,8 +1,13 @@ package suwayomi.tachidesk.graphql.mutations import graphql.execution.DataFetcherResult +import org.jetbrains.exposed.sql.LikePattern +import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList +import org.jetbrains.exposed.sql.SqlExpressionBuilder.like import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.or import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.global.impl.GlobalMeta @@ -10,6 +15,7 @@ import suwayomi.tachidesk.global.model.table.GlobalMetaTable import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.GlobalMetaType +import suwayomi.tachidesk.graphql.types.MetaInput class MetaMutation { data class SetGlobalMetaInput( @@ -68,4 +74,86 @@ class MetaMutation { DeleteGlobalMetaPayload(clientMutationId, meta) } } + + data class SetGlobalMetasInput( + val clientMutationId: String? = null, + val metas: List, + ) + + data class SetGlobalMetasPayload( + val clientMutationId: String?, + val metas: List, + ) + + @RequireAuth + fun setGlobalMetas(input: SetGlobalMetasInput): DataFetcherResult { + val (clientMutationId, metas) = input + + return asDataFetcherResult { + val metaMap = metas.associate { it.key to it.value } + GlobalMeta.modifyMetas(metaMap) + + val updatedMetas = + transaction { + GlobalMetaTable + .selectAll() + .where { GlobalMetaTable.key inList metaMap.keys } + .map { GlobalMetaType(it) } + } + + SetGlobalMetasPayload(clientMutationId, updatedMetas) + } + } + + data class DeleteGlobalMetasInput( + val clientMutationId: String? = null, + val keys: List? = null, + val prefixes: List? = null, + ) + + data class DeleteGlobalMetasPayload( + val clientMutationId: String?, + val metas: List, + ) + + @RequireAuth + fun deleteGlobalMetas(input: DeleteGlobalMetasInput): DataFetcherResult { + val (clientMutationId, keys, prefixes) = input + + return asDataFetcherResult { + require(!keys.isNullOrEmpty() || !prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided" + } + + val metas = + transaction { + val keyCondition: Op? = keys?.takeIf { it.isNotEmpty() }?.let { GlobalMetaTable.key inList it } + + val prefixCondition: Op? = + prefixes + ?.filter { it.isNotEmpty() } + ?.map { (GlobalMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val finalCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val metas = + GlobalMetaTable + .selectAll() + .where { finalCondition } + .map { GlobalMetaType(it) } + + GlobalMetaTable.deleteWhere { finalCondition } + + metas + } + + DeleteGlobalMetasPayload(clientMutationId, metas) + } + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt index ebadf2a9..c9b619e7 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt @@ -6,15 +6,21 @@ import androidx.preference.ListPreference import androidx.preference.MultiSelectListPreference import androidx.preference.SwitchPreferenceCompat import graphql.execution.DataFetcherResult +import org.jetbrains.exposed.sql.LikePattern +import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList +import org.jetbrains.exposed.sql.SqlExpressionBuilder.like import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.or import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.graphql.asDataFetcherResult import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.FilterChange import suwayomi.tachidesk.graphql.types.MangaType +import suwayomi.tachidesk.graphql.types.MetaInput import suwayomi.tachidesk.graphql.types.Preference import suwayomi.tachidesk.graphql.types.SourceMetaType import suwayomi.tachidesk.graphql.types.SourceType @@ -98,6 +104,140 @@ class SourceMutation { } } + data class SetSourceMetasItem( + val sourceIds: List, + val metas: List, + ) + + data class SetSourceMetasInput( + val clientMutationId: String? = null, + val items: List, + ) + + data class SetSourceMetasPayload( + val clientMutationId: String?, + val metas: List, + val sources: List, + ) + + @RequireAuth + fun setSourceMetas(input: SetSourceMetasInput): DataFetcherResult { + val (clientMutationId, items) = input + + return asDataFetcherResult { + val metaBySourceId = + items + .flatMap { item -> + val metaMap = item.metas.associate { it.key to it.value } + item.sourceIds.map { sourceId -> sourceId to metaMap } + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, maps) -> maps.reduce { acc, map -> acc + map } } + + Source.modifySourceMetas(metaBySourceId) + + val allSourceIds = metaBySourceId.keys + val allMetaKeys = metaBySourceId.values.flatMap { it.keys }.distinct() + + val (updatedMetas, sources) = + transaction { + val updatedMetas = + SourceMetaTable + .selectAll() + .where { (SourceMetaTable.ref inList allSourceIds) and (SourceMetaTable.key inList allMetaKeys) } + .map { SourceMetaType(it) } + + val sources = + SourceTable + .selectAll() + .where { SourceTable.id inList allSourceIds } + .mapNotNull { SourceType(it) } + .distinctBy { it.id } + + updatedMetas to sources + } + + SetSourceMetasPayload(clientMutationId, updatedMetas, sources) + } + } + + data class DeleteSourceMetasItem( + val sourceIds: List, + val keys: List? = null, + val prefixes: List? = null, + ) + + data class DeleteSourceMetasInput( + val clientMutationId: String? = null, + val items: List, + ) + + data class DeleteSourceMetasPayload( + val clientMutationId: String?, + val metas: List, + val sources: List, + ) + + @RequireAuth + fun deleteSourceMetas(input: DeleteSourceMetasInput): DataFetcherResult { + val (clientMutationId, items) = input + + return asDataFetcherResult { + items.forEach { item -> + require(!item.keys.isNullOrEmpty() || !item.prefixes.isNullOrEmpty()) { + "Either 'keys' or 'prefixes' must be provided for each item" + } + } + + val (allDeletedMetas, allSourceIds) = + transaction { + val deletedMetas = mutableListOf() + val sourceIds = mutableSetOf() + + items.forEach { item -> + val keyCondition: Op? = + item.keys?.takeIf { it.isNotEmpty() }?.let { SourceMetaTable.key inList it } + + val prefixCondition: Op? = + item.prefixes + ?.filter { it.isNotEmpty() } + ?.map { (SourceMetaTable.key like LikePattern("$it%")) as Op } + ?.reduceOrNull { acc, op -> acc or op } + + val metaKeyCondition = + if (keyCondition != null && prefixCondition != null) { + keyCondition or prefixCondition + } else { + keyCondition ?: prefixCondition!! + } + + val condition = (SourceMetaTable.ref inList item.sourceIds) and metaKeyCondition + + deletedMetas += + SourceMetaTable + .selectAll() + .where { condition } + .map { SourceMetaType(it) } + + SourceMetaTable.deleteWhere { condition } + sourceIds += item.sourceIds + } + + deletedMetas to sourceIds + } + + val sources = + transaction { + SourceTable + .selectAll() + .where { SourceTable.id inList allSourceIds } + .mapNotNull { SourceType(it) } + .distinctBy { it.id } + } + + DeleteSourceMetasPayload(clientMutationId, allDeletedMetas, sources) + } + } + enum class FetchSourceMangaType { SEARCH, POPULAR, 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 2687dd43..4b1aaef6 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MetaType.kt @@ -20,6 +20,11 @@ interface MetaType : Node { val value: String } +data class MetaInput( + val key: String, + val value: String, +) + class ChapterMetaType( override val key: String, override val value: String,