mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-03 19:04:39 -05:00
Proper extension store queries
This commit is contained in:
@@ -16,6 +16,26 @@ import suwayomi.tachidesk.manga.model.table.ExtensionStoreTable
|
|||||||
import suwayomi.tachidesk.manga.model.table.ExtensionTable
|
import suwayomi.tachidesk.manga.model.table.ExtensionTable
|
||||||
import suwayomi.tachidesk.server.JavalinSetup.future
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
|
|
||||||
|
class ExtensionStoreDataLoader : KotlinDataLoader<String, ExtensionStoreType> {
|
||||||
|
override val dataLoaderName = "ExtensionStoreDataLoader"
|
||||||
|
|
||||||
|
override fun getDataLoader(graphQLContext: GraphQLContext): DataLoader<String, ExtensionStoreType> =
|
||||||
|
DataLoaderFactory.newDataLoader { ids ->
|
||||||
|
future {
|
||||||
|
transaction {
|
||||||
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
|
val manga =
|
||||||
|
ExtensionStoreTable
|
||||||
|
.selectAll()
|
||||||
|
.where { ExtensionStoreTable.indexUrl inList ids }
|
||||||
|
.map { ExtensionStoreType(it) }
|
||||||
|
.associateBy { it.indexUrl }
|
||||||
|
ids.map { manga[it] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ExtensionStoreForExtension : KotlinDataLoader<String, ExtensionStoreType> {
|
class ExtensionStoreForExtension : KotlinDataLoader<String, ExtensionStoreType> {
|
||||||
override val dataLoaderName = "ExtensionStoreForExtension"
|
override val dataLoaderName = "ExtensionStoreForExtension"
|
||||||
|
|
||||||
|
|||||||
@@ -7,33 +7,185 @@ package suwayomi.tachidesk.graphql.queries
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import org.jetbrains.exposed.v1.core.eq
|
import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated
|
||||||
|
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
|
||||||
|
import graphql.schema.DataFetchingEnvironment
|
||||||
|
import org.jetbrains.exposed.v1.core.Column
|
||||||
|
import org.jetbrains.exposed.v1.core.Op
|
||||||
|
import org.jetbrains.exposed.v1.core.SortOrder
|
||||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||||
import suwayomi.tachidesk.graphql.directives.RequireAuth
|
import suwayomi.tachidesk.graphql.directives.RequireAuth
|
||||||
|
import suwayomi.tachidesk.graphql.queries.filter.Filter
|
||||||
|
import suwayomi.tachidesk.graphql.queries.filter.HasGetOp
|
||||||
|
import suwayomi.tachidesk.graphql.queries.filter.OpAnd
|
||||||
|
import suwayomi.tachidesk.graphql.queries.filter.StringFilter
|
||||||
|
import suwayomi.tachidesk.graphql.queries.filter.andFilterWithCompareString
|
||||||
|
import suwayomi.tachidesk.graphql.queries.filter.applyOps
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.Cursor
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.Order
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.OrderBy
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.PageInfo
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.QueryResults
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.applyBeforeAfter
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.maybeSwap
|
||||||
|
import suwayomi.tachidesk.graphql.types.ExtensionStoreNodeList
|
||||||
import suwayomi.tachidesk.graphql.types.ExtensionStoreType
|
import suwayomi.tachidesk.graphql.types.ExtensionStoreType
|
||||||
import suwayomi.tachidesk.manga.model.table.ExtensionStoreTable
|
import suwayomi.tachidesk.manga.model.table.ExtensionStoreTable
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
class ExtensionStoreQuery {
|
class ExtensionStoreQuery {
|
||||||
@RequireAuth
|
@RequireAuth
|
||||||
fun extensionStore(indexUrl: String): CompletableFuture<ExtensionStoreType?> =
|
fun extensionStore(
|
||||||
CompletableFuture.supplyAsync {
|
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||||
transaction {
|
indexUrl: String,
|
||||||
ExtensionStoreTable
|
): CompletableFuture<ExtensionStoreType> = dataFetchingEnvironment.getValueFromDataLoader("ExtensionStoreDataLoader", indexUrl)
|
||||||
.selectAll()
|
|
||||||
.where { ExtensionStoreTable.indexUrl eq indexUrl }
|
enum class ExtensionStoreOrderBy(
|
||||||
.firstOrNull()
|
override val column: Column<*>,
|
||||||
?.let { ExtensionStoreType(it) }
|
) : OrderBy<ExtensionStoreType> {
|
||||||
|
NAME(ExtensionStoreTable.name),
|
||||||
|
INDEX_URL(ExtensionStoreTable.indexUrl),
|
||||||
|
;
|
||||||
|
|
||||||
|
override fun greater(cursor: Cursor): Op<Boolean> =
|
||||||
|
when (this) {
|
||||||
|
NAME -> greaterNotUnique(ExtensionStoreTable.name, ExtensionStoreTable.id, cursor, String::toString)
|
||||||
|
INDEX_URL -> greaterNotUnique(ExtensionStoreTable.indexUrl, ExtensionStoreTable.id, cursor, String::toString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun less(cursor: Cursor): Op<Boolean> =
|
||||||
|
when (this) {
|
||||||
|
NAME -> lessNotUnique(ExtensionStoreTable.name, ExtensionStoreTable.id, cursor, String::toString)
|
||||||
|
INDEX_URL -> lessNotUnique(ExtensionStoreTable.indexUrl, ExtensionStoreTable.id, cursor, String::toString)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun asCursor(type: ExtensionStoreType): Cursor {
|
||||||
|
val value =
|
||||||
|
when (this) {
|
||||||
|
INDEX_URL -> type.indexUrl
|
||||||
|
NAME -> type.indexUrl + "-" + type.name
|
||||||
|
}
|
||||||
|
return Cursor(value)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ExtensionStoreOrder(
|
||||||
|
override val by: ExtensionStoreOrderBy,
|
||||||
|
override val byType: SortOrder? = null,
|
||||||
|
) : Order<ExtensionStoreOrderBy>
|
||||||
|
|
||||||
|
data class ExtensionStoreCondition(
|
||||||
|
val id: Int? = null,
|
||||||
|
val indexUrl: String? = null,
|
||||||
|
val name: String? = null,
|
||||||
|
) : HasGetOp {
|
||||||
|
override fun getOp(): Op<Boolean>? {
|
||||||
|
val opAnd = OpAnd()
|
||||||
|
opAnd.eq(id, ExtensionStoreTable.id)
|
||||||
|
opAnd.eq(indexUrl, ExtensionStoreTable.indexUrl)
|
||||||
|
opAnd.eq(name, ExtensionStoreTable.name)
|
||||||
|
|
||||||
|
return opAnd.op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ExtensionStoreFilter(
|
||||||
|
val indexUrl: StringFilter? = null,
|
||||||
|
val name: StringFilter? = null,
|
||||||
|
override val and: List<ExtensionStoreFilter>? = null,
|
||||||
|
override val or: List<ExtensionStoreFilter>? = null,
|
||||||
|
override val not: ExtensionStoreFilter? = null,
|
||||||
|
) : Filter<ExtensionStoreFilter> {
|
||||||
|
override fun getOpList(): List<Op<Boolean>> =
|
||||||
|
listOfNotNull(
|
||||||
|
andFilterWithCompareString(ExtensionStoreTable.indexUrl, indexUrl),
|
||||||
|
andFilterWithCompareString(ExtensionStoreTable.name, name),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@RequireAuth
|
@RequireAuth
|
||||||
fun extensionStores(): List<ExtensionStoreType> =
|
fun extensionStores(
|
||||||
transaction {
|
condition: ExtensionStoreCondition? = null,
|
||||||
ExtensionStoreTable
|
filter: ExtensionStoreFilter? = null,
|
||||||
.selectAll()
|
order: List<ExtensionStoreOrder>? = null,
|
||||||
.toList()
|
before: Cursor? = null,
|
||||||
.map { ExtensionStoreType(it) }
|
after: Cursor? = null,
|
||||||
}
|
first: Int? = null,
|
||||||
|
last: Int? = null,
|
||||||
|
offset: Int? = null,
|
||||||
|
): ExtensionStoreNodeList {
|
||||||
|
val queryResults =
|
||||||
|
transaction {
|
||||||
|
val res = ExtensionStoreTable.selectAll()
|
||||||
|
|
||||||
|
res.applyOps(condition, filter)
|
||||||
|
|
||||||
|
if (order != null || (last != null || before != null)) {
|
||||||
|
val baseSort = listOf(ExtensionStoreOrder(ExtensionStoreOrderBy.INDEX_URL, SortOrder.ASC))
|
||||||
|
val actualSort = (order.orEmpty() + baseSort)
|
||||||
|
actualSort.forEach { (orderBy, orderByType) ->
|
||||||
|
val orderByColumn = orderBy.column
|
||||||
|
val orderType = orderByType.maybeSwap(last ?: before)
|
||||||
|
|
||||||
|
res.orderBy(orderByColumn to orderType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val total = res.count()
|
||||||
|
val firstResult = res.firstOrNull()?.get(ExtensionStoreTable.indexUrl)
|
||||||
|
val lastResult = res.lastOrNull()?.get(ExtensionStoreTable.indexUrl)
|
||||||
|
|
||||||
|
res.applyBeforeAfter(
|
||||||
|
before = before,
|
||||||
|
after = after,
|
||||||
|
orderBy = order?.firstOrNull()?.by ?: ExtensionStoreOrderBy.INDEX_URL,
|
||||||
|
orderByType = order?.firstOrNull()?.byType,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (first != null) {
|
||||||
|
res.limit(first).offset(offset?.toLong() ?: 0)
|
||||||
|
} else if (last != null) {
|
||||||
|
res.limit(last)
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryResults(total, firstResult, lastResult, res.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
val getAsCursor: (ExtensionStoreType) -> Cursor = (order?.firstOrNull()?.by ?: ExtensionStoreOrderBy.INDEX_URL)::asCursor
|
||||||
|
|
||||||
|
val resultsAsType = queryResults.results.map { ExtensionStoreType(it) }
|
||||||
|
|
||||||
|
return ExtensionStoreNodeList(
|
||||||
|
resultsAsType,
|
||||||
|
if (resultsAsType.isEmpty()) {
|
||||||
|
emptyList()
|
||||||
|
} else {
|
||||||
|
listOfNotNull(
|
||||||
|
resultsAsType.firstOrNull()?.let {
|
||||||
|
ExtensionStoreNodeList.ExtensionStoreEdge(
|
||||||
|
getAsCursor(it),
|
||||||
|
it,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
resultsAsType.lastOrNull()?.let {
|
||||||
|
ExtensionStoreNodeList.ExtensionStoreEdge(
|
||||||
|
getAsCursor(it),
|
||||||
|
it,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
pageInfo =
|
||||||
|
PageInfo(
|
||||||
|
hasNextPage = queryResults.lastKey != resultsAsType.lastOrNull()?.indexUrl,
|
||||||
|
hasPreviousPage = queryResults.firstKey != resultsAsType.firstOrNull()?.indexUrl,
|
||||||
|
startCursor = resultsAsType.firstOrNull()?.let { getAsCursor(it) },
|
||||||
|
endCursor = resultsAsType.lastOrNull()?.let { getAsCursor(it) },
|
||||||
|
),
|
||||||
|
totalCount = queryResults.total.toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import suwayomi.tachidesk.graphql.dataLoaders.DownloadedChapterCountForMangaData
|
|||||||
import suwayomi.tachidesk.graphql.dataLoaders.ExtensionDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.ExtensionDataLoader
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.ExtensionForExtensionStore
|
import suwayomi.tachidesk.graphql.dataLoaders.ExtensionForExtensionStore
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.ExtensionForSourceDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.ExtensionForSourceDataLoader
|
||||||
|
import suwayomi.tachidesk.graphql.dataLoaders.ExtensionStoreDataLoader
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.ExtensionStoreForExtension
|
import suwayomi.tachidesk.graphql.dataLoaders.ExtensionStoreForExtension
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.FirstUnreadChapterForMangaDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.FirstUnreadChapterForMangaDataLoader
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.GlobalMetaDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.GlobalMetaDataLoader
|
||||||
@@ -81,6 +82,7 @@ class TachideskDataLoaderRegistryFactory {
|
|||||||
ExtensionDataLoader(),
|
ExtensionDataLoader(),
|
||||||
ExtensionForSourceDataLoader(),
|
ExtensionForSourceDataLoader(),
|
||||||
ExtensionForExtensionStore(),
|
ExtensionForExtensionStore(),
|
||||||
|
ExtensionStoreDataLoader(),
|
||||||
ExtensionStoreForExtension(),
|
ExtensionStoreForExtension(),
|
||||||
TrackerDataLoader(),
|
TrackerDataLoader(),
|
||||||
TrackerStatusesDataLoader(),
|
TrackerStatusesDataLoader(),
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ package suwayomi.tachidesk.graphql.types
|
|||||||
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
|
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
|
||||||
import graphql.schema.DataFetchingEnvironment
|
import graphql.schema.DataFetchingEnvironment
|
||||||
import org.jetbrains.exposed.v1.core.ResultRow
|
import org.jetbrains.exposed.v1.core.ResultRow
|
||||||
|
import suwayomi.tachidesk.graphql.server.primitives.Cursor
|
||||||
|
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.PageInfo
|
||||||
import suwayomi.tachidesk.manga.model.table.ExtensionStoreTable
|
import suwayomi.tachidesk.manga.model.table.ExtensionStoreTable
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
@@ -36,3 +40,45 @@ class ExtensionStoreType(
|
|||||||
fun extension(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ExtensionNodeList> =
|
fun extension(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ExtensionNodeList> =
|
||||||
dataFetchingEnvironment.getValueFromDataLoader<String, ExtensionNodeList>("ExtensionForExtensionStore", indexUrl)
|
dataFetchingEnvironment.getValueFromDataLoader<String, ExtensionNodeList>("ExtensionForExtensionStore", indexUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ExtensionStoreNodeList(
|
||||||
|
override val nodes: List<ExtensionStoreType>,
|
||||||
|
override val edges: List<ExtensionStoreEdge>,
|
||||||
|
override val pageInfo: PageInfo,
|
||||||
|
override val totalCount: Int,
|
||||||
|
) : NodeList() {
|
||||||
|
data class ExtensionStoreEdge(
|
||||||
|
override val cursor: Cursor,
|
||||||
|
override val node: ExtensionStoreType,
|
||||||
|
) : Edge()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun List<ExtensionStoreType>.toNodeList(): ExtensionStoreNodeList =
|
||||||
|
ExtensionStoreNodeList(
|
||||||
|
nodes = this,
|
||||||
|
edges = getEdges(),
|
||||||
|
pageInfo =
|
||||||
|
PageInfo(
|
||||||
|
hasNextPage = false,
|
||||||
|
hasPreviousPage = false,
|
||||||
|
startCursor = Cursor(0.toString()),
|
||||||
|
endCursor = Cursor(lastIndex.toString()),
|
||||||
|
),
|
||||||
|
totalCount = size,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun List<ExtensionStoreType>.getEdges(): List<ExtensionStoreEdge> {
|
||||||
|
if (isEmpty()) return emptyList()
|
||||||
|
return listOf(
|
||||||
|
ExtensionStoreEdge(
|
||||||
|
cursor = Cursor("0"),
|
||||||
|
node = first(),
|
||||||
|
),
|
||||||
|
ExtensionStoreEdge(
|
||||||
|
cursor = Cursor(lastIndex.toString()),
|
||||||
|
node = last(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user