|
|
|
@ -2,15 +2,14 @@ package com.jeluchu.features.anime.services
|
|
|
|
|
|
|
|
|
|
import com.jeluchu.core.enums.TimeUnit
|
|
|
|
|
import com.jeluchu.core.enums.parseAnimeType
|
|
|
|
|
import com.jeluchu.core.extensions.needsUpdate
|
|
|
|
|
import com.jeluchu.core.extensions.update
|
|
|
|
|
import com.jeluchu.core.extensions.*
|
|
|
|
|
import com.jeluchu.core.messages.ErrorMessages
|
|
|
|
|
import com.jeluchu.core.models.ErrorResponse
|
|
|
|
|
import com.jeluchu.core.models.PaginationResponse
|
|
|
|
|
import com.jeluchu.core.utils.Collections
|
|
|
|
|
import com.jeluchu.core.utils.TimerKey
|
|
|
|
|
import com.jeluchu.features.anime.mappers.documentToAnimeDirectoryEntity
|
|
|
|
|
import com.jeluchu.features.anime.mappers.documentToAnimeTypeEntity
|
|
|
|
|
import com.mongodb.client.MongoCollection
|
|
|
|
|
import com.mongodb.client.MongoDatabase
|
|
|
|
|
import com.mongodb.client.model.Filters
|
|
|
|
|
import io.ktor.http.*
|
|
|
|
@ -19,143 +18,124 @@ import io.ktor.server.routing.*
|
|
|
|
|
import kotlinx.serialization.encodeToString
|
|
|
|
|
import kotlinx.serialization.json.Json
|
|
|
|
|
import org.bson.Document
|
|
|
|
|
import org.bson.conversions.Bson
|
|
|
|
|
|
|
|
|
|
class DirectoryService(
|
|
|
|
|
private val database: MongoDatabase
|
|
|
|
|
private val database: MongoDatabase,
|
|
|
|
|
private val timers: MongoCollection<Document> = database.getCollection(Collections.TIMERS),
|
|
|
|
|
private val directory: MongoCollection<Document> = database.getCollection(Collections.ANIME_DETAILS)
|
|
|
|
|
) {
|
|
|
|
|
private val timers = database.getCollection(Collections.TIMERS)
|
|
|
|
|
private val directory = database.getCollection(Collections.ANIME_DETAILS)
|
|
|
|
|
|
|
|
|
|
suspend fun getAnimeByType(call: RoutingCall) {
|
|
|
|
|
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
|
|
|
|
|
val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 10
|
|
|
|
|
val param = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidAnimeType.message)
|
|
|
|
|
val param = call.getStringSafeParam("type").uppercase()
|
|
|
|
|
val page = call.getIntSafeQueryParam("page", 1)
|
|
|
|
|
val size = call.getIntSafeQueryParam("size", 10)
|
|
|
|
|
|
|
|
|
|
if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
|
|
|
|
|
val skipCount = (page - 1) * size
|
|
|
|
|
|
|
|
|
|
if (parseAnimeType(param) == null) call.respond(
|
|
|
|
|
HttpStatusCode.BadRequest,
|
|
|
|
|
ErrorResponse(ErrorMessages.InvalidAnimeType.message)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
val timerKey = "${TimerKey.ANIME_TYPE}${param.lowercase()}"
|
|
|
|
|
val collection = database.getCollection(timerKey)
|
|
|
|
|
|
|
|
|
|
val needsUpdate = timers.needsUpdate(
|
|
|
|
|
amount = 30,
|
|
|
|
|
key = timerKey,
|
|
|
|
|
unit = TimeUnit.DAY,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if (needsUpdate) {
|
|
|
|
|
collection.deleteMany(Document())
|
|
|
|
|
|
|
|
|
|
val animes = directory
|
|
|
|
|
.find(Filters.eq("type", param.uppercase()))
|
|
|
|
|
.toList()
|
|
|
|
|
|
|
|
|
|
val animeTypes = animes.map { documentToAnimeTypeEntity(it) }
|
|
|
|
|
val documents = animeTypes.map { anime -> Document.parse(Json.encodeToString(anime)) }
|
|
|
|
|
if (documents.isNotEmpty()) collection.insertMany(documents)
|
|
|
|
|
timers.update(timerKey)
|
|
|
|
|
|
|
|
|
|
val animeTypeDb = collection
|
|
|
|
|
.find()
|
|
|
|
|
.skip(skipCount)
|
|
|
|
|
.limit(size)
|
|
|
|
|
.toList()
|
|
|
|
|
|
|
|
|
|
val animeTypeEntity = animeTypeDb.map { documentToAnimeTypeEntity(it) }
|
|
|
|
|
|
|
|
|
|
val response = PaginationResponse(
|
|
|
|
|
page = page,
|
|
|
|
|
data = animeTypeEntity,
|
|
|
|
|
size = animeTypeEntity.size
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
|
|
|
|
|
} else {
|
|
|
|
|
val elements = collection
|
|
|
|
|
.find()
|
|
|
|
|
.skip(skipCount)
|
|
|
|
|
.limit(size)
|
|
|
|
|
.toList()
|
|
|
|
|
|
|
|
|
|
val responseItems = elements.map { documentToAnimeDirectoryEntity(it) }
|
|
|
|
|
val response = PaginationResponse(
|
|
|
|
|
page = page,
|
|
|
|
|
data = responseItems,
|
|
|
|
|
size = responseItems.size
|
|
|
|
|
if (page < 1 || size < 1) call.badRequestError(ErrorMessages.InvalidSizeAndPage.message)
|
|
|
|
|
if (parseAnimeType(param) == null) call.badRequestError(ErrorMessages.InvalidAnimeType.message)
|
|
|
|
|
|
|
|
|
|
if (timers.needsUpdate(timerKey, 30, TimeUnit.DAY)) {
|
|
|
|
|
getRemoteData(
|
|
|
|
|
newCollection = collection,
|
|
|
|
|
remoteCollection = directory,
|
|
|
|
|
mapper = { documentToAnimeDirectoryEntity(it) },
|
|
|
|
|
filters = Filters.eq("type", param),
|
|
|
|
|
onQuerySuccess = { data ->
|
|
|
|
|
val documents = data.map { Document.parse(Json.encodeToString(it)) }
|
|
|
|
|
if (documents.isNotEmpty()) collection.insertMany(documents)
|
|
|
|
|
timers.update(timerKey)
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getLocalData(
|
|
|
|
|
page = page,
|
|
|
|
|
size = size,
|
|
|
|
|
skipCount = skipCount,
|
|
|
|
|
collection = collection,
|
|
|
|
|
mapper = { documentToAnimeDirectoryEntity(it) },
|
|
|
|
|
onQuerySuccess = { data -> call.respond(HttpStatusCode.OK, Json.encodeToString(data)) }
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
suspend fun getAnimeBySeason(call: RoutingCall) {
|
|
|
|
|
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
|
|
|
|
|
val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 10
|
|
|
|
|
val year = call.parameters["year"]?.toIntOrNull() ?: throw IllegalArgumentException(ErrorMessages.NotFound.message)
|
|
|
|
|
val season = call.parameters["season"] ?: throw IllegalArgumentException(ErrorMessages.NotFound.message)
|
|
|
|
|
val year = call.getIntSafeParam("year")
|
|
|
|
|
val season = call.getStringSafeParam("season")
|
|
|
|
|
val page = call.getIntSafeQueryParam("page", 1)
|
|
|
|
|
val size = call.getIntSafeQueryParam("size", 10)
|
|
|
|
|
|
|
|
|
|
if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
|
|
|
|
|
val skipCount = (page - 1) * size
|
|
|
|
|
|
|
|
|
|
val timerKey = "${TimerKey.ANIME_TYPE}${year}_${season.lowercase()}"
|
|
|
|
|
val collection = database.getCollection(timerKey)
|
|
|
|
|
if (page < 1 || size < 1) call.badRequestError(ErrorMessages.InvalidSizeAndPage.message)
|
|
|
|
|
|
|
|
|
|
if (timers.needsUpdate(timerKey, 30, TimeUnit.DAY)) {
|
|
|
|
|
getRemoteData(
|
|
|
|
|
newCollection = collection,
|
|
|
|
|
remoteCollection = directory,
|
|
|
|
|
mapper = { documentToAnimeDirectoryEntity(it) },
|
|
|
|
|
filters = Filters.and(
|
|
|
|
|
Filters.eq("year", year),
|
|
|
|
|
Filters.eq("season", season.lowercase())
|
|
|
|
|
),
|
|
|
|
|
onQuerySuccess = { data ->
|
|
|
|
|
val documents = data.map { Document.parse(Json.encodeToString(it)) }
|
|
|
|
|
if (documents.isNotEmpty()) collection.insertMany(documents)
|
|
|
|
|
timers.update(timerKey)
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val needsUpdate = timers.needsUpdate(
|
|
|
|
|
amount = 30,
|
|
|
|
|
key = timerKey,
|
|
|
|
|
unit = TimeUnit.DAY,
|
|
|
|
|
getLocalData(
|
|
|
|
|
page = page,
|
|
|
|
|
size = size,
|
|
|
|
|
skipCount = skipCount,
|
|
|
|
|
collection = collection,
|
|
|
|
|
mapper = { documentToAnimeDirectoryEntity(it) },
|
|
|
|
|
onQuerySuccess = { data -> call.respond(HttpStatusCode.OK, Json.encodeToString(data)) }
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (needsUpdate) {
|
|
|
|
|
collection.deleteMany(Document())
|
|
|
|
|
|
|
|
|
|
val animes = directory
|
|
|
|
|
.find(
|
|
|
|
|
Filters.and(
|
|
|
|
|
Filters.eq("year", year),
|
|
|
|
|
Filters.eq("season", season.lowercase())
|
|
|
|
|
)
|
|
|
|
|
).toList()
|
|
|
|
|
|
|
|
|
|
val animeTypes = animes.map { documentToAnimeTypeEntity(it) }
|
|
|
|
|
val documents = animeTypes.map { anime -> Document.parse(Json.encodeToString(anime)) }
|
|
|
|
|
if (documents.isNotEmpty()) collection.insertMany(documents)
|
|
|
|
|
timers.update(timerKey)
|
|
|
|
|
|
|
|
|
|
val animeSeasonDb = collection
|
|
|
|
|
.find()
|
|
|
|
|
.skip(skipCount)
|
|
|
|
|
.limit(size)
|
|
|
|
|
.toList()
|
|
|
|
|
|
|
|
|
|
val animeSeason = animeSeasonDb.map { documentToAnimeTypeEntity(it) }
|
|
|
|
|
val response = PaginationResponse(
|
|
|
|
|
page = page,
|
|
|
|
|
data = animeSeason,
|
|
|
|
|
size = animeSeason.size
|
|
|
|
|
)
|
|
|
|
|
private fun <T> getRemoteData(
|
|
|
|
|
filters: Bson,
|
|
|
|
|
mapper: (Document) -> T,
|
|
|
|
|
onQuerySuccess: (List<T>) -> Unit,
|
|
|
|
|
newCollection: MongoCollection<Document>,
|
|
|
|
|
remoteCollection: MongoCollection<Document>,
|
|
|
|
|
) {
|
|
|
|
|
newCollection.deleteMany(Document())
|
|
|
|
|
|
|
|
|
|
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
|
|
|
|
|
} else {
|
|
|
|
|
val elements = collection
|
|
|
|
|
.find()
|
|
|
|
|
.skip(skipCount)
|
|
|
|
|
.limit(size)
|
|
|
|
|
.toList()
|
|
|
|
|
val query = remoteCollection
|
|
|
|
|
.find(filters)
|
|
|
|
|
.toList()
|
|
|
|
|
.map { mapper(it) }
|
|
|
|
|
|
|
|
|
|
val responseItems = elements.map { documentToAnimeDirectoryEntity(it) }
|
|
|
|
|
val response = PaginationResponse(
|
|
|
|
|
page = page,
|
|
|
|
|
data = responseItems,
|
|
|
|
|
size = responseItems.size
|
|
|
|
|
)
|
|
|
|
|
onQuerySuccess(query)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private suspend fun <T> getLocalData(
|
|
|
|
|
page: Int,
|
|
|
|
|
size: Int,
|
|
|
|
|
skipCount: Int,
|
|
|
|
|
mapper: (Document) -> T,
|
|
|
|
|
collection: MongoCollection<Document>,
|
|
|
|
|
onQuerySuccess: suspend (PaginationResponse<T>) -> Unit
|
|
|
|
|
) {
|
|
|
|
|
val query = collection
|
|
|
|
|
.find()
|
|
|
|
|
.skip(skipCount)
|
|
|
|
|
.limit(size)
|
|
|
|
|
.toList()
|
|
|
|
|
.map { mapper(it) }
|
|
|
|
|
|
|
|
|
|
val paginate = PaginationResponse(
|
|
|
|
|
page = page,
|
|
|
|
|
data = query,
|
|
|
|
|
size = query.size
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
onQuerySuccess(paginate)
|
|
|
|
|
}
|