@file:Suppress("RedundantNullableReturnType", "unused") package suwayomi.tachidesk.graphql.mutations import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated import com.expediagroup.graphql.generator.annotations.GraphQLDescription import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.v1.jdbc.selectAll import org.jetbrains.exposed.v1.jdbc.transactions.transaction import suwayomi.tachidesk.graphql.directives.RequireAuth import suwayomi.tachidesk.graphql.types.TrackRecordType import suwayomi.tachidesk.graphql.types.TrackerType import suwayomi.tachidesk.manga.impl.track.Track import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager import suwayomi.tachidesk.manga.model.table.TrackRecordTable import suwayomi.tachidesk.server.JavalinSetup.future import java.util.concurrent.CompletableFuture class TrackMutation { data class LoginTrackerOAuthInput( val clientMutationId: String? = null, val trackerId: Int, val callbackUrl: String, ) data class LoginTrackerOAuthPayload( val clientMutationId: String?, val isLoggedIn: Boolean, val tracker: TrackerType, ) @RequireAuth fun loginTrackerOAuth(input: LoginTrackerOAuthInput): CompletableFuture { val tracker = requireNotNull(TrackerManager.getTracker(input.trackerId)) { "Could not find tracker" } return future { tracker.authCallback(input.callbackUrl) val trackerType = TrackerType(tracker) LoginTrackerOAuthPayload( input.clientMutationId, trackerType.isLoggedIn, trackerType, ) } } data class LoginTrackerCredentialsInput( val clientMutationId: String? = null, val trackerId: Int, val username: String, val password: String, ) data class LoginTrackerCredentialsPayload( val clientMutationId: String?, val isLoggedIn: Boolean, val tracker: TrackerType, ) @RequireAuth fun loginTrackerCredentials(input: LoginTrackerCredentialsInput): CompletableFuture { val tracker = requireNotNull(TrackerManager.getTracker(input.trackerId)) { "Could not find tracker" } return future { tracker.login(input.username, input.password) val trackerType = TrackerType(tracker) LoginTrackerCredentialsPayload( input.clientMutationId, trackerType.isLoggedIn, trackerType, ) } } data class LogoutTrackerInput( val clientMutationId: String? = null, val trackerId: Int, ) data class LogoutTrackerPayload( val clientMutationId: String?, val isLoggedIn: Boolean, val tracker: TrackerType, ) @RequireAuth fun logoutTracker(input: LogoutTrackerInput): CompletableFuture { val tracker = requireNotNull(TrackerManager.getTracker(input.trackerId)) { "Could not find tracker" } require(tracker.isLoggedIn) { "Cannot logout of a tracker that is not logged-in" } return future { tracker.logout() val trackerType = TrackerType(tracker) LogoutTrackerPayload( input.clientMutationId, trackerType.isLoggedIn, trackerType, ) } } data class BindTrackInput( val clientMutationId: String? = null, val mangaId: Int, val trackerId: Int, val remoteId: Long, @GraphQLDescription("This will only work if the tracker of the track record supports private tracking") val private: Boolean? = null, ) data class BindTrackPayload( val clientMutationId: String?, val trackRecord: TrackRecordType, ) @RequireAuth fun bindTrack(input: BindTrackInput): CompletableFuture { val (clientMutationId, mangaId, trackerId, remoteId, private) = input return future { Track.bind( mangaId, trackerId, remoteId, private ?: false, ) val trackRecord = transaction { TrackRecordTable .selectAll() .where { TrackRecordTable.mangaId eq mangaId and (TrackRecordTable.trackerId eq trackerId) }.first() } BindTrackPayload( clientMutationId, TrackRecordType(trackRecord), ) } } data class FetchTrackInput( val clientMutationId: String? = null, val recordId: Int, ) data class FetchTrackPayload( val clientMutationId: String?, val trackRecord: TrackRecordType, ) @RequireAuth fun fetchTrack(input: FetchTrackInput): CompletableFuture { val (clientMutationId, recordId) = input return future { Track.refresh(recordId) val trackRecord = transaction { TrackRecordTable .selectAll() .where { TrackRecordTable.id eq recordId }.first() } FetchTrackPayload( clientMutationId, TrackRecordType(trackRecord), ) } } data class UnbindTrackInput( val clientMutationId: String? = null, val recordId: Int, @GraphQLDescription("This will only work if the tracker of the track record supports deleting tracks") val deleteRemoteTrack: Boolean? = null, ) data class UnbindTrackPayload( val clientMutationId: String?, val trackRecord: TrackRecordType?, ) @RequireAuth fun unbindTrack(input: UnbindTrackInput): CompletableFuture { val (clientMutationId, recordId, deleteRemoteTrack) = input return future { Track.unbind(recordId, deleteRemoteTrack) val trackRecord = transaction { TrackRecordTable .selectAll() .where { TrackRecordTable.id eq recordId }.firstOrNull() } UnbindTrackPayload( clientMutationId, trackRecord?.let { TrackRecordType(it) }, ) } } data class TrackProgressInput( val clientMutationId: String? = null, val mangaId: Int, ) data class TrackProgressPayload( val clientMutationId: String?, val trackRecords: List, ) @RequireAuth fun trackProgress(input: TrackProgressInput): CompletableFuture { val (clientMutationId, mangaId) = input return future { Track.trackChapter(mangaId) val trackRecords = transaction { TrackRecordTable .selectAll() .where { TrackRecordTable.mangaId eq mangaId } .toList() } TrackProgressPayload( clientMutationId, trackRecords.map { TrackRecordType(it) }, ) } } data class UpdateTrackInput( val clientMutationId: String? = null, val recordId: Int, val status: Int? = null, val lastChapterRead: Double? = null, val scoreString: String? = null, @GraphQLDescription("This will only work if the tracker of the track record supports reading dates") val startDate: Long? = null, @GraphQLDescription("This will only work if the tracker of the track record supports reading dates") val finishDate: Long? = null, @GraphQLDescription("This will only work if the tracker of the track record supports private tracking") val private: Boolean? = null, @GraphQLDeprecated("Replaced with \"unbindTrack\" mutation", replaceWith = ReplaceWith("unbindTrack")) val unbind: Boolean? = null, ) data class UpdateTrackPayload( val clientMutationId: String?, val trackRecord: TrackRecordType?, ) @RequireAuth fun updateTrack(input: UpdateTrackInput): CompletableFuture = future { Track.update( Track.UpdateInput( input.recordId, input.status, input.lastChapterRead, input.scoreString, input.startDate, input.finishDate, input.unbind, input.private, ), ) val trackRecord = transaction { TrackRecordTable .selectAll() .where { TrackRecordTable.id eq input.recordId }.firstOrNull() } UpdateTrackPayload( input.clientMutationId, trackRecord?.let { TrackRecordType(it) }, ) } }