Support partial mutation responses (#954)
In case e.g. a mutation was made which looked like this
myMutation {
mutationA { ... }
mutationB { ... }
mutationC { ... }
}
and mutation A and B succeeded while mutation C failed, the response only included the error of C and the successful mutation data response of A and B was missing
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package suwayomi.tachidesk.graphql
|
||||
|
||||
import com.expediagroup.graphql.server.extensions.toGraphQLError
|
||||
import graphql.execution.DataFetcherResult
|
||||
import mu.KotlinLogging
|
||||
|
||||
val logger = KotlinLogging.logger { }
|
||||
|
||||
inline fun <T> asDataFetcherResult(block: () -> T): DataFetcherResult<T?> {
|
||||
val result =
|
||||
runCatching {
|
||||
block()
|
||||
}
|
||||
|
||||
if (result.isFailure) {
|
||||
logger.error(result.exceptionOrNull()) { "asDataFetcherResult: failed due to" }
|
||||
return DataFetcherResult.newResult<T?>()
|
||||
.error(result.exceptionOrNull()?.toGraphQLError())
|
||||
.build()
|
||||
}
|
||||
|
||||
return DataFetcherResult.newResult<T?>()
|
||||
.data(result.getOrNull())
|
||||
.build()
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import graphql.execution.DataFetcherResult
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.minus
|
||||
@@ -12,6 +13,7 @@ import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.jetbrains.exposed.sql.update
|
||||
import suwayomi.tachidesk.graphql.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.CategoryMetaType
|
||||
import suwayomi.tachidesk.graphql.types.CategoryType
|
||||
import suwayomi.tachidesk.graphql.types.MangaType
|
||||
@@ -36,12 +38,14 @@ class CategoryMutation {
|
||||
val meta: CategoryMetaType,
|
||||
)
|
||||
|
||||
fun setCategoryMeta(input: SetCategoryMetaInput): SetCategoryMetaPayload {
|
||||
val (clientMutationId, meta) = input
|
||||
fun setCategoryMeta(input: SetCategoryMetaInput): DataFetcherResult<SetCategoryMetaPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, meta) = input
|
||||
|
||||
Category.modifyMeta(meta.categoryId, meta.key, meta.value)
|
||||
Category.modifyMeta(meta.categoryId, meta.key, meta.value)
|
||||
|
||||
return SetCategoryMetaPayload(clientMutationId, meta)
|
||||
SetCategoryMetaPayload(clientMutationId, meta)
|
||||
}
|
||||
}
|
||||
|
||||
data class DeleteCategoryMetaInput(
|
||||
@@ -56,30 +60,32 @@ class CategoryMutation {
|
||||
val category: CategoryType,
|
||||
)
|
||||
|
||||
fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DeleteCategoryMetaPayload {
|
||||
val (clientMutationId, categoryId, key) = input
|
||||
fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DataFetcherResult<DeleteCategoryMetaPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, categoryId, key) = input
|
||||
|
||||
val (meta, category) =
|
||||
transaction {
|
||||
val meta =
|
||||
CategoryMetaTable.select { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
|
||||
.firstOrNull()
|
||||
val (meta, category) =
|
||||
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) }
|
||||
CategoryMetaTable.deleteWhere { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
|
||||
|
||||
val category =
|
||||
transaction {
|
||||
CategoryType(CategoryTable.select { CategoryTable.id eq categoryId }.first())
|
||||
}
|
||||
val category =
|
||||
transaction {
|
||||
CategoryType(CategoryTable.select { CategoryTable.id eq categoryId }.first())
|
||||
}
|
||||
|
||||
if (meta != null) {
|
||||
CategoryMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
} to category
|
||||
}
|
||||
if (meta != null) {
|
||||
CategoryMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
} to category
|
||||
}
|
||||
|
||||
return DeleteCategoryMetaPayload(clientMutationId, meta, category)
|
||||
DeleteCategoryMetaPayload(clientMutationId, meta, category)
|
||||
}
|
||||
}
|
||||
|
||||
data class UpdateCategoryPatch(
|
||||
@@ -147,36 +153,40 @@ class CategoryMutation {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateCategory(input: UpdateCategoryInput): UpdateCategoryPayload {
|
||||
val (clientMutationId, id, patch) = input
|
||||
fun updateCategory(input: UpdateCategoryInput): DataFetcherResult<UpdateCategoryPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, id, patch) = input
|
||||
|
||||
updateCategories(listOf(id), patch)
|
||||
updateCategories(listOf(id), patch)
|
||||
|
||||
val category =
|
||||
transaction {
|
||||
CategoryType(CategoryTable.select { CategoryTable.id eq id }.first())
|
||||
}
|
||||
val category =
|
||||
transaction {
|
||||
CategoryType(CategoryTable.select { CategoryTable.id eq id }.first())
|
||||
}
|
||||
|
||||
return UpdateCategoryPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
category = category,
|
||||
)
|
||||
UpdateCategoryPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
category = category,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateCategories(input: UpdateCategoriesInput): UpdateCategoriesPayload {
|
||||
val (clientMutationId, ids, patch) = input
|
||||
fun updateCategories(input: UpdateCategoriesInput): DataFetcherResult<UpdateCategoriesPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, ids, patch) = input
|
||||
|
||||
updateCategories(ids, patch)
|
||||
updateCategories(ids, patch)
|
||||
|
||||
val categories =
|
||||
transaction {
|
||||
CategoryTable.select { CategoryTable.id inList ids }.map { CategoryType(it) }
|
||||
}
|
||||
val categories =
|
||||
transaction {
|
||||
CategoryTable.select { CategoryTable.id inList ids }.map { CategoryType(it) }
|
||||
}
|
||||
|
||||
return UpdateCategoriesPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
categories = categories,
|
||||
)
|
||||
UpdateCategoriesPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
categories = categories,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class UpdateCategoryOrderPayload(
|
||||
@@ -190,46 +200,48 @@ class CategoryMutation {
|
||||
val position: Int,
|
||||
)
|
||||
|
||||
fun updateCategoryOrder(input: UpdateCategoryOrderInput): UpdateCategoryOrderPayload {
|
||||
val (clientMutationId, categoryId, position) = input
|
||||
require(position > 0) {
|
||||
"'order' must not be <= 0"
|
||||
}
|
||||
|
||||
transaction {
|
||||
val currentOrder =
|
||||
CategoryTable
|
||||
.select { CategoryTable.id eq categoryId }
|
||||
.first()[CategoryTable.order]
|
||||
|
||||
if (currentOrder != position) {
|
||||
if (position < currentOrder) {
|
||||
CategoryTable.update({ CategoryTable.order greaterEq position }) {
|
||||
it[CategoryTable.order] = CategoryTable.order + 1
|
||||
}
|
||||
} else {
|
||||
CategoryTable.update({ CategoryTable.order lessEq position }) {
|
||||
it[CategoryTable.order] = CategoryTable.order - 1
|
||||
}
|
||||
}
|
||||
|
||||
CategoryTable.update({ CategoryTable.id eq categoryId }) {
|
||||
it[CategoryTable.order] = position
|
||||
}
|
||||
fun updateCategoryOrder(input: UpdateCategoryOrderInput): DataFetcherResult<UpdateCategoryOrderPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, categoryId, position) = input
|
||||
require(position > 0) {
|
||||
"'order' must not be <= 0"
|
||||
}
|
||||
}
|
||||
|
||||
Category.normalizeCategories()
|
||||
|
||||
val categories =
|
||||
transaction {
|
||||
CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) }
|
||||
val currentOrder =
|
||||
CategoryTable
|
||||
.select { CategoryTable.id eq categoryId }
|
||||
.first()[CategoryTable.order]
|
||||
|
||||
if (currentOrder != position) {
|
||||
if (position < currentOrder) {
|
||||
CategoryTable.update({ CategoryTable.order greaterEq position }) {
|
||||
it[CategoryTable.order] = CategoryTable.order + 1
|
||||
}
|
||||
} else {
|
||||
CategoryTable.update({ CategoryTable.order lessEq position }) {
|
||||
it[CategoryTable.order] = CategoryTable.order - 1
|
||||
}
|
||||
}
|
||||
|
||||
CategoryTable.update({ CategoryTable.id eq categoryId }) {
|
||||
it[CategoryTable.order] = position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UpdateCategoryOrderPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
categories = categories,
|
||||
)
|
||||
Category.normalizeCategories()
|
||||
|
||||
val categories =
|
||||
transaction {
|
||||
CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) }
|
||||
}
|
||||
|
||||
UpdateCategoryOrderPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
categories = categories,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class CreateCategoryInput(
|
||||
@@ -246,51 +258,53 @@ class CategoryMutation {
|
||||
val category: CategoryType,
|
||||
)
|
||||
|
||||
fun createCategory(input: CreateCategoryInput): CreateCategoryPayload {
|
||||
val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input
|
||||
transaction {
|
||||
require(CategoryTable.select { CategoryTable.name eq input.name }.isEmpty()) {
|
||||
"'name' must be unique"
|
||||
}
|
||||
}
|
||||
require(!name.equals(Category.DEFAULT_CATEGORY_NAME, ignoreCase = true)) {
|
||||
"'name' must not be ${Category.DEFAULT_CATEGORY_NAME}"
|
||||
}
|
||||
if (order != null) {
|
||||
require(order > 0) {
|
||||
"'order' must not be <= 0"
|
||||
}
|
||||
}
|
||||
|
||||
val category =
|
||||
fun createCategory(input: CreateCategoryInput): DataFetcherResult<CreateCategoryPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input
|
||||
transaction {
|
||||
if (order != null) {
|
||||
CategoryTable.update({ CategoryTable.order greaterEq order }) {
|
||||
it[CategoryTable.order] = CategoryTable.order + 1
|
||||
require(CategoryTable.select { CategoryTable.name eq input.name }.isEmpty()) {
|
||||
"'name' must be unique"
|
||||
}
|
||||
}
|
||||
require(!name.equals(Category.DEFAULT_CATEGORY_NAME, ignoreCase = true)) {
|
||||
"'name' must not be ${Category.DEFAULT_CATEGORY_NAME}"
|
||||
}
|
||||
if (order != null) {
|
||||
require(order > 0) {
|
||||
"'order' must not be <= 0"
|
||||
}
|
||||
}
|
||||
|
||||
val category =
|
||||
transaction {
|
||||
if (order != null) {
|
||||
CategoryTable.update({ CategoryTable.order greaterEq order }) {
|
||||
it[CategoryTable.order] = CategoryTable.order + 1
|
||||
}
|
||||
}
|
||||
|
||||
val id =
|
||||
CategoryTable.insertAndGetId {
|
||||
it[CategoryTable.name] = input.name
|
||||
it[CategoryTable.order] = order ?: Int.MAX_VALUE
|
||||
if (default != null) {
|
||||
it[CategoryTable.isDefault] = default
|
||||
}
|
||||
if (includeInUpdate != null) {
|
||||
it[CategoryTable.includeInUpdate] = includeInUpdate.value
|
||||
}
|
||||
if (includeInDownload != null) {
|
||||
it[CategoryTable.includeInDownload] = includeInDownload.value
|
||||
}
|
||||
}
|
||||
|
||||
Category.normalizeCategories()
|
||||
|
||||
CategoryType(CategoryTable.select { CategoryTable.id eq id }.first())
|
||||
}
|
||||
|
||||
val id =
|
||||
CategoryTable.insertAndGetId {
|
||||
it[CategoryTable.name] = input.name
|
||||
it[CategoryTable.order] = order ?: Int.MAX_VALUE
|
||||
if (default != null) {
|
||||
it[CategoryTable.isDefault] = default
|
||||
}
|
||||
if (includeInUpdate != null) {
|
||||
it[CategoryTable.includeInUpdate] = includeInUpdate.value
|
||||
}
|
||||
if (includeInDownload != null) {
|
||||
it[CategoryTable.includeInDownload] = includeInDownload.value
|
||||
}
|
||||
}
|
||||
|
||||
Category.normalizeCategories()
|
||||
|
||||
CategoryType(CategoryTable.select { CategoryTable.id eq id }.first())
|
||||
}
|
||||
|
||||
return CreateCategoryPayload(clientMutationId, category)
|
||||
CreateCategoryPayload(clientMutationId, category)
|
||||
}
|
||||
}
|
||||
|
||||
data class DeleteCategoryInput(
|
||||
@@ -304,41 +318,43 @@ class CategoryMutation {
|
||||
val mangas: List<MangaType>,
|
||||
)
|
||||
|
||||
fun deleteCategory(input: DeleteCategoryInput): DeleteCategoryPayload {
|
||||
val (clientMutationId, categoryId) = input
|
||||
if (categoryId == 0) { // Don't delete default category
|
||||
return DeleteCategoryPayload(
|
||||
clientMutationId,
|
||||
null,
|
||||
emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
val (category, mangas) =
|
||||
transaction {
|
||||
val category =
|
||||
CategoryTable.select { CategoryTable.id eq categoryId }
|
||||
.firstOrNull()
|
||||
|
||||
val mangas =
|
||||
transaction {
|
||||
MangaTable.innerJoin(CategoryMangaTable)
|
||||
.select { CategoryMangaTable.category eq categoryId }
|
||||
.map { MangaType(it) }
|
||||
}
|
||||
|
||||
CategoryTable.deleteWhere { CategoryTable.id eq categoryId }
|
||||
|
||||
Category.normalizeCategories()
|
||||
|
||||
if (category != null) {
|
||||
CategoryType(category)
|
||||
} else {
|
||||
null
|
||||
} to mangas
|
||||
fun deleteCategory(input: DeleteCategoryInput): DataFetcherResult<DeleteCategoryPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, categoryId) = input
|
||||
if (categoryId == 0) { // Don't delete default category
|
||||
return@asDataFetcherResult DeleteCategoryPayload(
|
||||
clientMutationId,
|
||||
null,
|
||||
emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
return DeleteCategoryPayload(clientMutationId, category, mangas)
|
||||
val (category, mangas) =
|
||||
transaction {
|
||||
val category =
|
||||
CategoryTable.select { CategoryTable.id eq categoryId }
|
||||
.firstOrNull()
|
||||
|
||||
val mangas =
|
||||
transaction {
|
||||
MangaTable.innerJoin(CategoryMangaTable)
|
||||
.select { CategoryMangaTable.category eq categoryId }
|
||||
.map { MangaType(it) }
|
||||
}
|
||||
|
||||
CategoryTable.deleteWhere { CategoryTable.id eq categoryId }
|
||||
|
||||
Category.normalizeCategories()
|
||||
|
||||
if (category != null) {
|
||||
CategoryType(category)
|
||||
} else {
|
||||
null
|
||||
} to mangas
|
||||
}
|
||||
|
||||
DeleteCategoryPayload(clientMutationId, category, mangas)
|
||||
}
|
||||
}
|
||||
|
||||
data class UpdateMangaCategoriesPatch(
|
||||
@@ -406,35 +422,39 @@ class CategoryMutation {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateMangaCategories(input: UpdateMangaCategoriesInput): UpdateMangaCategoriesPayload {
|
||||
val (clientMutationId, id, patch) = input
|
||||
fun updateMangaCategories(input: UpdateMangaCategoriesInput): DataFetcherResult<UpdateMangaCategoriesPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, id, patch) = input
|
||||
|
||||
updateMangas(listOf(id), patch)
|
||||
updateMangas(listOf(id), patch)
|
||||
|
||||
val manga =
|
||||
transaction {
|
||||
MangaType(MangaTable.select { MangaTable.id eq id }.first())
|
||||
}
|
||||
val manga =
|
||||
transaction {
|
||||
MangaType(MangaTable.select { MangaTable.id eq id }.first())
|
||||
}
|
||||
|
||||
return UpdateMangaCategoriesPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
manga = manga,
|
||||
)
|
||||
UpdateMangaCategoriesPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
manga = manga,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateMangasCategories(input: UpdateMangasCategoriesInput): UpdateMangasCategoriesPayload {
|
||||
val (clientMutationId, ids, patch) = input
|
||||
fun updateMangasCategories(input: UpdateMangasCategoriesInput): DataFetcherResult<UpdateMangasCategoriesPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, ids, patch) = input
|
||||
|
||||
updateMangas(ids, patch)
|
||||
updateMangas(ids, patch)
|
||||
|
||||
val mangas =
|
||||
transaction {
|
||||
MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) }
|
||||
}
|
||||
val mangas =
|
||||
transaction {
|
||||
MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) }
|
||||
}
|
||||
|
||||
return UpdateMangasCategoriesPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
mangas = mangas,
|
||||
)
|
||||
UpdateMangasCategoriesPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
mangas = mangas,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import graphql.execution.DataFetcherResult
|
||||
import org.jetbrains.exposed.dao.id.EntityID
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.and
|
||||
@@ -7,6 +8,7 @@ import org.jetbrains.exposed.sql.deleteWhere
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.statements.BatchUpdateStatement
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import suwayomi.tachidesk.graphql.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.ChapterMetaType
|
||||
import suwayomi.tachidesk.graphql.types.ChapterType
|
||||
import suwayomi.tachidesk.manga.impl.Chapter
|
||||
@@ -89,36 +91,40 @@ class ChapterMutation {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateChapter(input: UpdateChapterInput): UpdateChapterPayload {
|
||||
val (clientMutationId, id, patch) = input
|
||||
fun updateChapter(input: UpdateChapterInput): DataFetcherResult<UpdateChapterPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, id, patch) = input
|
||||
|
||||
updateChapters(listOf(id), patch)
|
||||
updateChapters(listOf(id), patch)
|
||||
|
||||
val chapter =
|
||||
transaction {
|
||||
ChapterType(ChapterTable.select { ChapterTable.id eq id }.first())
|
||||
}
|
||||
val chapter =
|
||||
transaction {
|
||||
ChapterType(ChapterTable.select { ChapterTable.id eq id }.first())
|
||||
}
|
||||
|
||||
return UpdateChapterPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapter = chapter,
|
||||
)
|
||||
UpdateChapterPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapter = chapter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateChapters(input: UpdateChaptersInput): UpdateChaptersPayload {
|
||||
val (clientMutationId, ids, patch) = input
|
||||
fun updateChapters(input: UpdateChaptersInput): DataFetcherResult<UpdateChaptersPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, ids, patch) = input
|
||||
|
||||
updateChapters(ids, patch)
|
||||
updateChapters(ids, patch)
|
||||
|
||||
val chapters =
|
||||
transaction {
|
||||
ChapterTable.select { ChapterTable.id inList ids }.map { ChapterType(it) }
|
||||
}
|
||||
val chapters =
|
||||
transaction {
|
||||
ChapterTable.select { ChapterTable.id inList ids }.map { ChapterType(it) }
|
||||
}
|
||||
|
||||
return UpdateChaptersPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapters = chapters,
|
||||
)
|
||||
UpdateChaptersPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapters = chapters,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class FetchChaptersInput(
|
||||
@@ -131,23 +137,25 @@ class ChapterMutation {
|
||||
val chapters: List<ChapterType>,
|
||||
)
|
||||
|
||||
fun fetchChapters(input: FetchChaptersInput): CompletableFuture<FetchChaptersPayload> {
|
||||
fun fetchChapters(input: FetchChaptersInput): CompletableFuture<DataFetcherResult<FetchChaptersPayload?>> {
|
||||
val (clientMutationId, mangaId) = input
|
||||
|
||||
return future {
|
||||
Chapter.fetchChapterList(mangaId)
|
||||
}.thenApply {
|
||||
val chapters =
|
||||
transaction {
|
||||
ChapterTable.select { ChapterTable.manga eq mangaId }
|
||||
.orderBy(ChapterTable.sourceOrder)
|
||||
.map { ChapterType(it) }
|
||||
}
|
||||
asDataFetcherResult {
|
||||
Chapter.fetchChapterList(mangaId)
|
||||
|
||||
FetchChaptersPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapters = chapters,
|
||||
)
|
||||
val chapters =
|
||||
transaction {
|
||||
ChapterTable.select { ChapterTable.manga eq mangaId }
|
||||
.orderBy(ChapterTable.sourceOrder)
|
||||
.map { ChapterType(it) }
|
||||
}
|
||||
|
||||
FetchChaptersPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapters = chapters,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,12 +169,14 @@ class ChapterMutation {
|
||||
val meta: ChapterMetaType,
|
||||
)
|
||||
|
||||
fun setChapterMeta(input: SetChapterMetaInput): SetChapterMetaPayload {
|
||||
val (clientMutationId, meta) = input
|
||||
fun setChapterMeta(input: SetChapterMetaInput): DataFetcherResult<SetChapterMetaPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, meta) = input
|
||||
|
||||
Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value)
|
||||
Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value)
|
||||
|
||||
return SetChapterMetaPayload(clientMutationId, meta)
|
||||
SetChapterMetaPayload(clientMutationId, meta)
|
||||
}
|
||||
}
|
||||
|
||||
data class DeleteChapterMetaInput(
|
||||
@@ -181,30 +191,32 @@ class ChapterMutation {
|
||||
val chapter: ChapterType,
|
||||
)
|
||||
|
||||
fun deleteChapterMeta(input: DeleteChapterMetaInput): DeleteChapterMetaPayload {
|
||||
val (clientMutationId, chapterId, key) = input
|
||||
fun deleteChapterMeta(input: DeleteChapterMetaInput): DataFetcherResult<DeleteChapterMetaPayload?> {
|
||||
return asDataFetcherResult {
|
||||
val (clientMutationId, chapterId, key) = input
|
||||
|
||||
val (meta, chapter) =
|
||||
transaction {
|
||||
val meta =
|
||||
ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
|
||||
.firstOrNull()
|
||||
val (meta, chapter) =
|
||||
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) }
|
||||
ChapterMetaTable.deleteWhere { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
|
||||
|
||||
val chapter =
|
||||
transaction {
|
||||
ChapterType(ChapterTable.select { ChapterTable.id eq chapterId }.first())
|
||||
}
|
||||
val chapter =
|
||||
transaction {
|
||||
ChapterType(ChapterTable.select { ChapterTable.id eq chapterId }.first())
|
||||
}
|
||||
|
||||
if (meta != null) {
|
||||
ChapterMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
} to chapter
|
||||
}
|
||||
if (meta != null) {
|
||||
ChapterMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
} to chapter
|
||||
}
|
||||
|
||||
return DeleteChapterMetaPayload(clientMutationId, meta, chapter)
|
||||
DeleteChapterMetaPayload(clientMutationId, meta, chapter)
|
||||
}
|
||||
}
|
||||
|
||||
data class FetchChapterPagesInput(
|
||||
@@ -218,20 +230,22 @@ class ChapterMutation {
|
||||
val chapter: ChapterType,
|
||||
)
|
||||
|
||||
fun fetchChapterPages(input: FetchChapterPagesInput): CompletableFuture<FetchChapterPagesPayload> {
|
||||
fun fetchChapterPages(input: FetchChapterPagesInput): CompletableFuture<DataFetcherResult<FetchChapterPagesPayload?>> {
|
||||
val (clientMutationId, chapterId) = input
|
||||
|
||||
return future {
|
||||
getChapterDownloadReadyById(chapterId)
|
||||
}.thenApply { chapter ->
|
||||
FetchChapterPagesPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
pages =
|
||||
List(chapter.pageCount) { index ->
|
||||
"/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/$index"
|
||||
},
|
||||
chapter = ChapterType(chapter),
|
||||
)
|
||||
asDataFetcherResult {
|
||||
val chapter = getChapterDownloadReadyById(chapterId)
|
||||
|
||||
FetchChapterPagesPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
pages =
|
||||
List(chapter.pageCount) { index ->
|
||||
"/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/$index"
|
||||
},
|
||||
chapter = ChapterType(chapter),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import graphql.execution.DataFetcherResult
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import suwayomi.tachidesk.graphql.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.ChapterType
|
||||
import suwayomi.tachidesk.graphql.types.DownloadStatus
|
||||
import suwayomi.tachidesk.manga.impl.Chapter
|
||||
@@ -25,19 +27,21 @@ class DownloadMutation {
|
||||
val chapters: List<ChapterType>,
|
||||
)
|
||||
|
||||
fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DeleteDownloadedChaptersPayload {
|
||||
fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DataFetcherResult<DeleteDownloadedChaptersPayload?> {
|
||||
val (clientMutationId, chapters) = input
|
||||
|
||||
Chapter.deleteChapters(chapters)
|
||||
return asDataFetcherResult {
|
||||
Chapter.deleteChapters(chapters)
|
||||
|
||||
return DeleteDownloadedChaptersPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapters =
|
||||
transaction {
|
||||
ChapterTable.select { ChapterTable.id inList chapters }
|
||||
.map { ChapterType(it) }
|
||||
},
|
||||
)
|
||||
DeleteDownloadedChaptersPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapters =
|
||||
transaction {
|
||||
ChapterTable.select { ChapterTable.id inList chapters }
|
||||
.map { ChapterType(it) }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class DeleteDownloadedChapterInput(
|
||||
@@ -50,18 +54,20 @@ class DownloadMutation {
|
||||
val chapters: ChapterType,
|
||||
)
|
||||
|
||||
fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DeleteDownloadedChapterPayload {
|
||||
fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DataFetcherResult<DeleteDownloadedChapterPayload?> {
|
||||
val (clientMutationId, chapter) = input
|
||||
|
||||
Chapter.deleteChapters(listOf(chapter))
|
||||
return asDataFetcherResult {
|
||||
Chapter.deleteChapters(listOf(chapter))
|
||||
|
||||
return DeleteDownloadedChapterPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapters =
|
||||
transaction {
|
||||
ChapterType(ChapterTable.select { ChapterTable.id eq chapter }.first())
|
||||
},
|
||||
)
|
||||
DeleteDownloadedChapterPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
chapters =
|
||||
transaction {
|
||||
ChapterType(ChapterTable.select { ChapterTable.id eq chapter }.first())
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class EnqueueChapterDownloadsInput(
|
||||
@@ -74,19 +80,23 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun enqueueChapterDownloads(input: EnqueueChapterDownloadsInput): CompletableFuture<EnqueueChapterDownloadsPayload> {
|
||||
fun enqueueChapterDownloads(
|
||||
input: EnqueueChapterDownloadsInput,
|
||||
): CompletableFuture<DataFetcherResult<EnqueueChapterDownloadsPayload?>> {
|
||||
val (clientMutationId, chapters) = input
|
||||
|
||||
DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters))
|
||||
|
||||
return future {
|
||||
EnqueueChapterDownloadsPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id in chapters } })
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters))
|
||||
|
||||
EnqueueChapterDownloadsPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id in chapters } })
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,19 +110,21 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture<EnqueueChapterDownloadPayload> {
|
||||
fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture<DataFetcherResult<EnqueueChapterDownloadPayload?>> {
|
||||
val (clientMutationId, chapter) = input
|
||||
|
||||
DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter)))
|
||||
|
||||
return future {
|
||||
EnqueueChapterDownloadPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id == chapter } })
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter)))
|
||||
|
||||
EnqueueChapterDownloadPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id == chapter } })
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,19 +138,23 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun dequeueChapterDownloads(input: DequeueChapterDownloadsInput): CompletableFuture<DequeueChapterDownloadsPayload> {
|
||||
fun dequeueChapterDownloads(
|
||||
input: DequeueChapterDownloadsInput,
|
||||
): CompletableFuture<DataFetcherResult<DequeueChapterDownloadsPayload?>> {
|
||||
val (clientMutationId, chapters) = input
|
||||
|
||||
DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters))
|
||||
|
||||
return future {
|
||||
DequeueChapterDownloadsPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id in chapters } })
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters))
|
||||
|
||||
DequeueChapterDownloadsPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id in chapters } })
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,19 +168,21 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture<DequeueChapterDownloadPayload> {
|
||||
fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture<DataFetcherResult<DequeueChapterDownloadPayload?>> {
|
||||
val (clientMutationId, chapter) = input
|
||||
|
||||
DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter)))
|
||||
|
||||
return future {
|
||||
DequeueChapterDownloadPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id == chapter } })
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter)))
|
||||
|
||||
DequeueChapterDownloadPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id == chapter } })
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,19 +195,21 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun startDownloader(input: StartDownloaderInput): CompletableFuture<StartDownloaderPayload> {
|
||||
DownloadManager.start()
|
||||
|
||||
fun startDownloader(input: StartDownloaderInput): CompletableFuture<DataFetcherResult<StartDownloaderPayload?>> {
|
||||
return future {
|
||||
StartDownloaderPayload(
|
||||
input.clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(
|
||||
DownloadManager.status.first { it.status == Status.Started },
|
||||
)
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
DownloadManager.start()
|
||||
|
||||
StartDownloaderPayload(
|
||||
input.clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(
|
||||
DownloadManager.status.first { it.status == Status.Started },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,18 +222,21 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<StopDownloaderPayload> {
|
||||
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<DataFetcherResult<StopDownloaderPayload?>> {
|
||||
return future {
|
||||
DownloadManager.stop()
|
||||
StopDownloaderPayload(
|
||||
input.clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(
|
||||
DownloadManager.status.first { it.status == Status.Stopped },
|
||||
)
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
DownloadManager.stop()
|
||||
|
||||
StopDownloaderPayload(
|
||||
input.clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(
|
||||
DownloadManager.status.first { it.status == Status.Stopped },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,18 +249,21 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<ClearDownloaderPayload> {
|
||||
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<DataFetcherResult<ClearDownloaderPayload?>> {
|
||||
return future {
|
||||
DownloadManager.clear()
|
||||
ClearDownloaderPayload(
|
||||
input.clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(
|
||||
DownloadManager.status.first { it.status == Status.Stopped && it.queue.isEmpty() },
|
||||
)
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
DownloadManager.clear()
|
||||
|
||||
ClearDownloaderPayload(
|
||||
input.clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(
|
||||
DownloadManager.status.first { it.status == Status.Stopped && it.queue.isEmpty() },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,20 +278,23 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture<ReorderChapterDownloadPayload> {
|
||||
fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture<DataFetcherResult<ReorderChapterDownloadPayload?>> {
|
||||
val (clientMutationId, chapter, to) = input
|
||||
DownloadManager.reorder(chapter, to)
|
||||
|
||||
return future {
|
||||
ReorderChapterDownloadPayload(
|
||||
clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(
|
||||
DownloadManager.status.first { it.queue.indexOfFirst { it.chapter.id == chapter } <= to },
|
||||
)
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
DownloadManager.reorder(chapter, to)
|
||||
|
||||
ReorderChapterDownloadPayload(
|
||||
clientMutationId,
|
||||
downloadStatus =
|
||||
withTimeout(30.seconds) {
|
||||
DownloadStatus(
|
||||
DownloadManager.status.first { it.queue.indexOfFirst { it.chapter.id == chapter } <= to },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import eu.kanade.tachiyomi.source.local.LocalSource
|
||||
import graphql.execution.DataFetcherResult
|
||||
import io.javalin.http.UploadedFile
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import suwayomi.tachidesk.graphql.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.ExtensionType
|
||||
import suwayomi.tachidesk.manga.impl.extension.Extension
|
||||
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList
|
||||
@@ -69,41 +71,45 @@ class ExtensionMutation {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateExtension(input: UpdateExtensionInput): CompletableFuture<UpdateExtensionPayload> {
|
||||
fun updateExtension(input: UpdateExtensionInput): CompletableFuture<DataFetcherResult<UpdateExtensionPayload?>> {
|
||||
val (clientMutationId, id, patch) = input
|
||||
|
||||
return future {
|
||||
updateExtensions(listOf(id), patch)
|
||||
}.thenApply {
|
||||
val extension =
|
||||
transaction {
|
||||
ExtensionTable.select { ExtensionTable.pkgName eq id }.firstOrNull()
|
||||
?.let { ExtensionType(it) }
|
||||
}
|
||||
asDataFetcherResult {
|
||||
updateExtensions(listOf(id), patch)
|
||||
|
||||
UpdateExtensionPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
extension = extension,
|
||||
)
|
||||
val extension =
|
||||
transaction {
|
||||
ExtensionTable.select { ExtensionTable.pkgName eq id }.firstOrNull()
|
||||
?.let { ExtensionType(it) }
|
||||
}
|
||||
|
||||
UpdateExtensionPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
extension = extension,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateExtensions(input: UpdateExtensionsInput): CompletableFuture<UpdateExtensionsPayload> {
|
||||
fun updateExtensions(input: UpdateExtensionsInput): CompletableFuture<DataFetcherResult<UpdateExtensionsPayload?>> {
|
||||
val (clientMutationId, ids, patch) = input
|
||||
|
||||
return future {
|
||||
updateExtensions(ids, patch)
|
||||
}.thenApply {
|
||||
val extensions =
|
||||
transaction {
|
||||
ExtensionTable.select { ExtensionTable.pkgName inList ids }
|
||||
.map { ExtensionType(it) }
|
||||
}
|
||||
asDataFetcherResult {
|
||||
updateExtensions(ids, patch)
|
||||
|
||||
UpdateExtensionsPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
extensions = extensions,
|
||||
)
|
||||
val extensions =
|
||||
transaction {
|
||||
ExtensionTable.select { ExtensionTable.pkgName inList ids }
|
||||
.map { ExtensionType(it) }
|
||||
}
|
||||
|
||||
UpdateExtensionsPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
extensions = extensions,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,22 +122,24 @@ class ExtensionMutation {
|
||||
val extensions: List<ExtensionType>,
|
||||
)
|
||||
|
||||
fun fetchExtensions(input: FetchExtensionsInput): CompletableFuture<FetchExtensionsPayload> {
|
||||
fun fetchExtensions(input: FetchExtensionsInput): CompletableFuture<DataFetcherResult<FetchExtensionsPayload?>> {
|
||||
val (clientMutationId) = input
|
||||
|
||||
return future {
|
||||
ExtensionsList.fetchExtensions()
|
||||
}.thenApply {
|
||||
val extensions =
|
||||
transaction {
|
||||
ExtensionTable.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME }
|
||||
.map { ExtensionType(it) }
|
||||
}
|
||||
asDataFetcherResult {
|
||||
ExtensionsList.fetchExtensions()
|
||||
|
||||
FetchExtensionsPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
extensions = extensions,
|
||||
)
|
||||
val extensions =
|
||||
transaction {
|
||||
ExtensionTable.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME }
|
||||
.map { ExtensionType(it) }
|
||||
}
|
||||
|
||||
FetchExtensionsPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
extensions = extensions,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,18 +153,22 @@ class ExtensionMutation {
|
||||
val extension: ExtensionType,
|
||||
)
|
||||
|
||||
fun installExternalExtension(input: InstallExternalExtensionInput): CompletableFuture<InstallExternalExtensionPayload> {
|
||||
fun installExternalExtension(
|
||||
input: InstallExternalExtensionInput,
|
||||
): CompletableFuture<DataFetcherResult<InstallExternalExtensionPayload?>> {
|
||||
val (clientMutationId, extensionFile) = input
|
||||
|
||||
return future {
|
||||
Extension.installExternalExtension(extensionFile.content, extensionFile.filename)
|
||||
}.thenApply {
|
||||
val dbExtension = transaction { ExtensionTable.select { ExtensionTable.apkName eq extensionFile.filename }.first() }
|
||||
asDataFetcherResult {
|
||||
Extension.installExternalExtension(extensionFile.content, extensionFile.filename)
|
||||
|
||||
InstallExternalExtensionPayload(
|
||||
clientMutationId,
|
||||
extension = ExtensionType(dbExtension),
|
||||
)
|
||||
val dbExtension = transaction { ExtensionTable.select { ExtensionTable.apkName eq extensionFile.filename }.first() }
|
||||
|
||||
InstallExternalExtensionPayload(
|
||||
clientMutationId,
|
||||
extension = ExtensionType(dbExtension),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import graphql.execution.DataFetcherResult
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import suwayomi.tachidesk.graphql.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.UpdateState.DOWNLOADING
|
||||
import suwayomi.tachidesk.graphql.types.UpdateState.ERROR
|
||||
import suwayomi.tachidesk.graphql.types.UpdateState.IDLE
|
||||
@@ -22,50 +24,54 @@ class InfoMutation {
|
||||
val updateStatus: WebUIUpdateStatus,
|
||||
)
|
||||
|
||||
fun updateWebUI(input: WebUIUpdateInput): CompletableFuture<WebUIUpdatePayload> {
|
||||
fun updateWebUI(input: WebUIUpdateInput): CompletableFuture<DataFetcherResult<WebUIUpdatePayload?>> {
|
||||
return future {
|
||||
withTimeout(30.seconds) {
|
||||
if (WebInterfaceManager.status.value.state === DOWNLOADING) {
|
||||
return@withTimeout WebUIUpdatePayload(input.clientMutationId, WebInterfaceManager.status.value)
|
||||
}
|
||||
asDataFetcherResult {
|
||||
withTimeout(30.seconds) {
|
||||
if (WebInterfaceManager.status.value.state === DOWNLOADING) {
|
||||
return@withTimeout WebUIUpdatePayload(input.clientMutationId, WebInterfaceManager.status.value)
|
||||
}
|
||||
|
||||
val flavor = WebUIFlavor.current
|
||||
val flavor = WebUIFlavor.current
|
||||
|
||||
val (version, updateAvailable) = WebInterfaceManager.isUpdateAvailable(flavor)
|
||||
val (version, updateAvailable) = WebInterfaceManager.isUpdateAvailable(flavor)
|
||||
|
||||
if (!updateAvailable) {
|
||||
val didUpdateCheckFail = version.isEmpty()
|
||||
if (!updateAvailable) {
|
||||
val didUpdateCheckFail = version.isEmpty()
|
||||
|
||||
return@withTimeout WebUIUpdatePayload(
|
||||
return@withTimeout WebUIUpdatePayload(
|
||||
input.clientMutationId,
|
||||
WebInterfaceManager.getStatus(version, if (didUpdateCheckFail) ERROR else IDLE),
|
||||
)
|
||||
}
|
||||
try {
|
||||
WebInterfaceManager.startDownloadInScope(flavor, version)
|
||||
} catch (e: Exception) {
|
||||
// ignore since we use the status anyway
|
||||
}
|
||||
|
||||
WebUIUpdatePayload(
|
||||
input.clientMutationId,
|
||||
WebInterfaceManager.getStatus(version, if (didUpdateCheckFail) ERROR else IDLE),
|
||||
updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING },
|
||||
)
|
||||
}
|
||||
try {
|
||||
WebInterfaceManager.startDownloadInScope(flavor, version)
|
||||
} catch (e: Exception) {
|
||||
// ignore since we use the status anyway
|
||||
}
|
||||
|
||||
WebUIUpdatePayload(
|
||||
input.clientMutationId,
|
||||
updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resetWebUIUpdateStatus(): CompletableFuture<WebUIUpdateStatus> {
|
||||
fun resetWebUIUpdateStatus(): CompletableFuture<DataFetcherResult<WebUIUpdateStatus?>> {
|
||||
return future {
|
||||
withTimeout(30.seconds) {
|
||||
val isUpdateFinished = WebInterfaceManager.status.value.state != DOWNLOADING
|
||||
if (!isUpdateFinished) {
|
||||
throw Exception("Status reset is not allowed during status \"$DOWNLOADING\"")
|
||||
asDataFetcherResult {
|
||||
withTimeout(30.seconds) {
|
||||
val isUpdateFinished = WebInterfaceManager.status.value.state != DOWNLOADING
|
||||
if (!isUpdateFinished) {
|
||||
throw Exception("Status reset is not allowed during status \"$DOWNLOADING\"")
|
||||
}
|
||||
|
||||
WebInterfaceManager.resetStatus()
|
||||
|
||||
WebInterfaceManager.status.first { it.state == IDLE }
|
||||
}
|
||||
|
||||
WebInterfaceManager.resetStatus()
|
||||
|
||||
WebInterfaceManager.status.first { it.state == IDLE }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import graphql.execution.DataFetcherResult
|
||||
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.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.MangaMetaType
|
||||
import suwayomi.tachidesk.graphql.types.MangaType
|
||||
import suwayomi.tachidesk.manga.impl.Library
|
||||
@@ -70,39 +72,43 @@ class MangaMutation {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateManga(input: UpdateMangaInput): CompletableFuture<UpdateMangaPayload> {
|
||||
fun updateManga(input: UpdateMangaInput): CompletableFuture<DataFetcherResult<UpdateMangaPayload?>> {
|
||||
val (clientMutationId, id, patch) = input
|
||||
|
||||
return future {
|
||||
updateMangas(listOf(id), patch)
|
||||
}.thenApply {
|
||||
val manga =
|
||||
transaction {
|
||||
MangaType(MangaTable.select { MangaTable.id eq id }.first())
|
||||
}
|
||||
asDataFetcherResult {
|
||||
updateMangas(listOf(id), patch)
|
||||
|
||||
UpdateMangaPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
manga = manga,
|
||||
)
|
||||
val manga =
|
||||
transaction {
|
||||
MangaType(MangaTable.select { MangaTable.id eq id }.first())
|
||||
}
|
||||
|
||||
UpdateMangaPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
manga = manga,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateMangas(input: UpdateMangasInput): CompletableFuture<UpdateMangasPayload> {
|
||||
fun updateMangas(input: UpdateMangasInput): CompletableFuture<DataFetcherResult<UpdateMangasPayload?>> {
|
||||
val (clientMutationId, ids, patch) = input
|
||||
|
||||
return future {
|
||||
updateMangas(ids, patch)
|
||||
}.thenApply {
|
||||
val mangas =
|
||||
transaction {
|
||||
MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) }
|
||||
}
|
||||
asDataFetcherResult {
|
||||
updateMangas(ids, patch)
|
||||
|
||||
UpdateMangasPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
mangas = mangas,
|
||||
)
|
||||
val mangas =
|
||||
transaction {
|
||||
MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) }
|
||||
}
|
||||
|
||||
UpdateMangasPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
mangas = mangas,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,20 +122,22 @@ class MangaMutation {
|
||||
val manga: MangaType,
|
||||
)
|
||||
|
||||
fun fetchManga(input: FetchMangaInput): CompletableFuture<FetchMangaPayload> {
|
||||
fun fetchManga(input: FetchMangaInput): CompletableFuture<DataFetcherResult<FetchMangaPayload?>> {
|
||||
val (clientMutationId, id) = input
|
||||
|
||||
return future {
|
||||
Manga.fetchManga(id)
|
||||
}.thenApply {
|
||||
val manga =
|
||||
transaction {
|
||||
MangaTable.select { MangaTable.id eq id }.first()
|
||||
}
|
||||
FetchMangaPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
manga = MangaType(manga),
|
||||
)
|
||||
asDataFetcherResult {
|
||||
Manga.fetchManga(id)
|
||||
|
||||
val manga =
|
||||
transaction {
|
||||
MangaTable.select { MangaTable.id eq id }.first()
|
||||
}
|
||||
FetchMangaPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
manga = MangaType(manga),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,12 +151,14 @@ class MangaMutation {
|
||||
val meta: MangaMetaType,
|
||||
)
|
||||
|
||||
fun setMangaMeta(input: SetMangaMetaInput): SetMangaMetaPayload {
|
||||
fun setMangaMeta(input: SetMangaMetaInput): DataFetcherResult<SetMangaMetaPayload?> {
|
||||
val (clientMutationId, meta) = input
|
||||
|
||||
Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value)
|
||||
return asDataFetcherResult {
|
||||
Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value)
|
||||
|
||||
return SetMangaMetaPayload(clientMutationId, meta)
|
||||
SetMangaMetaPayload(clientMutationId, meta)
|
||||
}
|
||||
}
|
||||
|
||||
data class DeleteMangaMetaInput(
|
||||
@@ -163,29 +173,31 @@ class MangaMutation {
|
||||
val manga: MangaType,
|
||||
)
|
||||
|
||||
fun deleteMangaMeta(input: DeleteMangaMetaInput): DeleteMangaMetaPayload {
|
||||
fun deleteMangaMeta(input: DeleteMangaMetaInput): DataFetcherResult<DeleteMangaMetaPayload?> {
|
||||
val (clientMutationId, mangaId, key) = input
|
||||
|
||||
val (meta, manga) =
|
||||
transaction {
|
||||
val meta =
|
||||
MangaMetaTable.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
|
||||
.firstOrNull()
|
||||
return asDataFetcherResult {
|
||||
val (meta, manga) =
|
||||
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) }
|
||||
MangaMetaTable.deleteWhere { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
|
||||
|
||||
val manga =
|
||||
transaction {
|
||||
MangaType(MangaTable.select { MangaTable.id eq mangaId }.first())
|
||||
}
|
||||
val manga =
|
||||
transaction {
|
||||
MangaType(MangaTable.select { MangaTable.id eq mangaId }.first())
|
||||
}
|
||||
|
||||
if (meta != null) {
|
||||
MangaMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
} to manga
|
||||
}
|
||||
if (meta != null) {
|
||||
MangaMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
} to manga
|
||||
}
|
||||
|
||||
return DeleteMangaMetaPayload(clientMutationId, meta, manga)
|
||||
DeleteMangaMetaPayload(clientMutationId, meta, manga)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import graphql.execution.DataFetcherResult
|
||||
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.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.GlobalMetaType
|
||||
|
||||
class MetaMutation {
|
||||
@@ -19,12 +21,14 @@ class MetaMutation {
|
||||
val meta: GlobalMetaType,
|
||||
)
|
||||
|
||||
fun setGlobalMeta(input: SetGlobalMetaInput): SetGlobalMetaPayload {
|
||||
fun setGlobalMeta(input: SetGlobalMetaInput): DataFetcherResult<SetGlobalMetaPayload?> {
|
||||
val (clientMutationId, meta) = input
|
||||
|
||||
GlobalMeta.modifyMeta(meta.key, meta.value)
|
||||
return asDataFetcherResult {
|
||||
GlobalMeta.modifyMeta(meta.key, meta.value)
|
||||
|
||||
return SetGlobalMetaPayload(clientMutationId, meta)
|
||||
SetGlobalMetaPayload(clientMutationId, meta)
|
||||
}
|
||||
}
|
||||
|
||||
data class DeleteGlobalMetaInput(
|
||||
@@ -37,24 +41,26 @@ class MetaMutation {
|
||||
val meta: GlobalMetaType?,
|
||||
)
|
||||
|
||||
fun deleteGlobalMeta(input: DeleteGlobalMetaInput): DeleteGlobalMetaPayload {
|
||||
fun deleteGlobalMeta(input: DeleteGlobalMetaInput): DataFetcherResult<DeleteGlobalMetaPayload?> {
|
||||
val (clientMutationId, key) = input
|
||||
|
||||
val meta =
|
||||
transaction {
|
||||
val meta =
|
||||
GlobalMetaTable.select { GlobalMetaTable.key eq key }
|
||||
.firstOrNull()
|
||||
return asDataFetcherResult {
|
||||
val meta =
|
||||
transaction {
|
||||
val meta =
|
||||
GlobalMetaTable.select { GlobalMetaTable.key eq key }
|
||||
.firstOrNull()
|
||||
|
||||
GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key }
|
||||
GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key }
|
||||
|
||||
if (meta != null) {
|
||||
GlobalMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
if (meta != null) {
|
||||
GlobalMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DeleteGlobalMetaPayload(clientMutationId, meta)
|
||||
DeleteGlobalMetaPayload(clientMutationId, meta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.MultiSelectListPreference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import graphql.execution.DataFetcherResult
|
||||
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.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.FilterChange
|
||||
import suwayomi.tachidesk.graphql.types.MangaType
|
||||
import suwayomi.tachidesk.graphql.types.Preference
|
||||
@@ -37,12 +39,14 @@ class SourceMutation {
|
||||
val meta: SourceMetaType,
|
||||
)
|
||||
|
||||
fun setSourceMeta(input: SetSourceMetaInput): SetSourceMetaPayload {
|
||||
fun setSourceMeta(input: SetSourceMetaInput): DataFetcherResult<SetSourceMetaPayload?> {
|
||||
val (clientMutationId, meta) = input
|
||||
|
||||
Source.modifyMeta(meta.sourceId, meta.key, meta.value)
|
||||
return asDataFetcherResult {
|
||||
Source.modifyMeta(meta.sourceId, meta.key, meta.value)
|
||||
|
||||
return SetSourceMetaPayload(clientMutationId, meta)
|
||||
SetSourceMetaPayload(clientMutationId, meta)
|
||||
}
|
||||
}
|
||||
|
||||
data class DeleteSourceMetaInput(
|
||||
@@ -57,31 +61,33 @@ class SourceMutation {
|
||||
val source: SourceType?,
|
||||
)
|
||||
|
||||
fun deleteSourceMeta(input: DeleteSourceMetaInput): DeleteSourceMetaPayload {
|
||||
fun deleteSourceMeta(input: DeleteSourceMetaInput): DataFetcherResult<DeleteSourceMetaPayload?> {
|
||||
val (clientMutationId, sourceId, key) = input
|
||||
|
||||
val (meta, source) =
|
||||
transaction {
|
||||
val meta =
|
||||
SourceMetaTable.select { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
|
||||
.firstOrNull()
|
||||
return asDataFetcherResult {
|
||||
val (meta, source) =
|
||||
transaction {
|
||||
val meta =
|
||||
SourceMetaTable.select { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
|
||||
.firstOrNull()
|
||||
|
||||
SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
|
||||
SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
|
||||
|
||||
val source =
|
||||
transaction {
|
||||
SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
|
||||
?.let { SourceType(it) }
|
||||
}
|
||||
val source =
|
||||
transaction {
|
||||
SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
|
||||
?.let { SourceType(it) }
|
||||
}
|
||||
|
||||
if (meta != null) {
|
||||
SourceMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
} to source
|
||||
}
|
||||
if (meta != null) {
|
||||
SourceMetaType(meta)
|
||||
} else {
|
||||
null
|
||||
} to source
|
||||
}
|
||||
|
||||
return DeleteSourceMetaPayload(clientMutationId, meta, source)
|
||||
DeleteSourceMetaPayload(clientMutationId, meta, source)
|
||||
}
|
||||
}
|
||||
|
||||
enum class FetchSourceMangaType {
|
||||
@@ -105,44 +111,46 @@ class SourceMutation {
|
||||
val hasNextPage: Boolean,
|
||||
)
|
||||
|
||||
fun fetchSourceManga(input: FetchSourceMangaInput): CompletableFuture<FetchSourceMangaPayload> {
|
||||
fun fetchSourceManga(input: FetchSourceMangaInput): CompletableFuture<DataFetcherResult<FetchSourceMangaPayload?>> {
|
||||
val (clientMutationId, sourceId, type, page, query, filters) = input
|
||||
|
||||
return future {
|
||||
val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!!
|
||||
val mangasPage =
|
||||
when (type) {
|
||||
FetchSourceMangaType.SEARCH -> {
|
||||
source.getSearchManga(
|
||||
page = page,
|
||||
query = query.orEmpty(),
|
||||
filters = updateFilterList(source, filters),
|
||||
)
|
||||
asDataFetcherResult {
|
||||
val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!!
|
||||
val mangasPage =
|
||||
when (type) {
|
||||
FetchSourceMangaType.SEARCH -> {
|
||||
source.getSearchManga(
|
||||
page = page,
|
||||
query = query.orEmpty(),
|
||||
filters = updateFilterList(source, filters),
|
||||
)
|
||||
}
|
||||
FetchSourceMangaType.POPULAR -> {
|
||||
source.getPopularManga(page)
|
||||
}
|
||||
FetchSourceMangaType.LATEST -> {
|
||||
if (!source.supportsLatest) throw Exception("Source does not support latest")
|
||||
source.getLatestUpdates(page)
|
||||
}
|
||||
}
|
||||
FetchSourceMangaType.POPULAR -> {
|
||||
source.getPopularManga(page)
|
||||
|
||||
val mangaIds = mangasPage.insertOrGet(sourceId)
|
||||
|
||||
val mangas =
|
||||
transaction {
|
||||
MangaTable.select { MangaTable.id inList mangaIds }
|
||||
.map { MangaType(it) }
|
||||
}.sortedBy {
|
||||
mangaIds.indexOf(it.id)
|
||||
}
|
||||
FetchSourceMangaType.LATEST -> {
|
||||
if (!source.supportsLatest) throw Exception("Source does not support latest")
|
||||
source.getLatestUpdates(page)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
FetchSourceMangaPayload(
|
||||
clientMutationId = clientMutationId,
|
||||
mangas = mangas,
|
||||
hasNextPage = mangasPage.hasNextPage,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,27 +175,29 @@ class SourceMutation {
|
||||
val source: SourceType,
|
||||
)
|
||||
|
||||
fun updateSourcePreference(input: UpdateSourcePreferenceInput): UpdateSourcePreferencePayload {
|
||||
fun updateSourcePreference(input: UpdateSourcePreferenceInput): DataFetcherResult<UpdateSourcePreferencePayload?> {
|
||||
val (clientMutationId, sourceId, change) = input
|
||||
|
||||
Source.setSourcePreference(sourceId, change.position, "") { preference ->
|
||||
when (preference) {
|
||||
is SwitchPreferenceCompat -> change.switchState
|
||||
is CheckBoxPreference -> change.checkBoxState
|
||||
is EditTextPreference -> change.editTextState
|
||||
is ListPreference -> change.listState
|
||||
is MultiSelectListPreference -> change.multiSelectState?.toSet()
|
||||
else -> throw RuntimeException("sealed class cannot have more subtypes!")
|
||||
} ?: throw Exception("Expected change to ${preference::class.simpleName}")
|
||||
}
|
||||
return asDataFetcherResult {
|
||||
Source.setSourcePreference(sourceId, change.position, "") { preference ->
|
||||
when (preference) {
|
||||
is SwitchPreferenceCompat -> change.switchState
|
||||
is CheckBoxPreference -> change.checkBoxState
|
||||
is EditTextPreference -> change.editTextState
|
||||
is ListPreference -> change.listState
|
||||
is MultiSelectListPreference -> change.multiSelectState?.toSet()
|
||||
else -> throw RuntimeException("sealed class cannot have more subtypes!")
|
||||
} ?: throw Exception("Expected change to ${preference::class.simpleName}")
|
||||
}
|
||||
|
||||
return UpdateSourcePreferencePayload(
|
||||
clientMutationId = clientMutationId,
|
||||
preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) },
|
||||
source =
|
||||
transaction {
|
||||
SourceType(SourceTable.select { SourceTable.id eq sourceId }.first())!!
|
||||
},
|
||||
)
|
||||
UpdateSourcePreferencePayload(
|
||||
clientMutationId = clientMutationId,
|
||||
preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) },
|
||||
source =
|
||||
transaction {
|
||||
SourceType(SourceTable.select { SourceTable.id eq sourceId }.first())!!
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated
|
||||
import com.expediagroup.graphql.generator.annotations.GraphQLDescription
|
||||
import graphql.execution.DataFetcherResult
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import suwayomi.tachidesk.graphql.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.TrackRecordType
|
||||
import suwayomi.tachidesk.graphql.types.TrackerType
|
||||
import suwayomi.tachidesk.manga.impl.track.Track
|
||||
@@ -203,20 +205,22 @@ class TrackMutation {
|
||||
val trackRecords: List<TrackRecordType>,
|
||||
)
|
||||
|
||||
fun trackProgress(input: TrackProgressInput): CompletableFuture<TrackProgressPayload> {
|
||||
fun trackProgress(input: TrackProgressInput): CompletableFuture<DataFetcherResult<TrackProgressPayload?>> {
|
||||
val (clientMutationId, mangaId) = input
|
||||
|
||||
return future {
|
||||
Track.trackChapter(mangaId)
|
||||
val trackRecords =
|
||||
transaction {
|
||||
TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId }
|
||||
.toList()
|
||||
}
|
||||
TrackProgressPayload(
|
||||
clientMutationId,
|
||||
trackRecords.map { TrackRecordType(it) },
|
||||
)
|
||||
asDataFetcherResult {
|
||||
Track.trackChapter(mangaId)
|
||||
val trackRecords =
|
||||
transaction {
|
||||
TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId }
|
||||
.toList()
|
||||
}
|
||||
TrackProgressPayload(
|
||||
clientMutationId,
|
||||
trackRecords.map { TrackRecordType(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import graphql.execution.DataFetcherResult
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.jetbrains.exposed.sql.select
|
||||
@@ -7,6 +8,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import suwayomi.tachidesk.graphql.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.types.UpdateStatus
|
||||
import suwayomi.tachidesk.manga.impl.Category
|
||||
import suwayomi.tachidesk.manga.impl.update.IUpdater
|
||||
@@ -28,7 +30,7 @@ class UpdateMutation {
|
||||
val updateStatus: UpdateStatus,
|
||||
)
|
||||
|
||||
fun updateLibraryManga(input: UpdateLibraryMangaInput): CompletableFuture<UpdateLibraryMangaPayload> {
|
||||
fun updateLibraryManga(input: UpdateLibraryMangaInput): CompletableFuture<DataFetcherResult<UpdateLibraryMangaPayload?>> {
|
||||
updater.addCategoriesToUpdateQueue(
|
||||
Category.getCategoryList(),
|
||||
clear = true,
|
||||
@@ -36,13 +38,15 @@ class UpdateMutation {
|
||||
)
|
||||
|
||||
return future {
|
||||
UpdateLibraryMangaPayload(
|
||||
input.clientMutationId,
|
||||
updateStatus =
|
||||
withTimeout(30.seconds) {
|
||||
UpdateStatus(updater.status.first())
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
UpdateLibraryMangaPayload(
|
||||
input.clientMutationId,
|
||||
updateStatus =
|
||||
withTimeout(30.seconds) {
|
||||
UpdateStatus(updater.status.first())
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +60,7 @@ class UpdateMutation {
|
||||
val updateStatus: UpdateStatus,
|
||||
)
|
||||
|
||||
fun updateCategoryManga(input: UpdateCategoryMangaInput): CompletableFuture<UpdateCategoryMangaPayload> {
|
||||
fun updateCategoryManga(input: UpdateCategoryMangaInput): CompletableFuture<DataFetcherResult<UpdateCategoryMangaPayload?>> {
|
||||
val categories =
|
||||
transaction {
|
||||
CategoryTable.select { CategoryTable.id inList input.categories }.map {
|
||||
@@ -66,13 +70,15 @@ class UpdateMutation {
|
||||
updater.addCategoriesToUpdateQueue(categories, clear = true, forceAll = true)
|
||||
|
||||
return future {
|
||||
UpdateCategoryMangaPayload(
|
||||
input.clientMutationId,
|
||||
updateStatus =
|
||||
withTimeout(30.seconds) {
|
||||
UpdateStatus(updater.status.first())
|
||||
},
|
||||
)
|
||||
asDataFetcherResult {
|
||||
UpdateCategoryMangaPayload(
|
||||
input.clientMutationId,
|
||||
updateStatus =
|
||||
withTimeout(30.seconds) {
|
||||
UpdateStatus(updater.status.first())
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user