Include pagination with types in rankings and directory

v5 v5.3.0
Jéluchu 7 months ago
parent 5d1b33e1e8
commit df6baa761b

@ -8,10 +8,13 @@ sealed class ErrorMessages(val message: String) {
data object InvalidMalId : ErrorMessages("The provided id of malId is invalid") data object InvalidMalId : ErrorMessages("The provided id of malId is invalid")
data object InvalidDay : ErrorMessages("Invalid 'day' parameter. Valid values are: ${Day.entries.joinToString(", ") { it.name.lowercase() }}") data object InvalidDay : ErrorMessages("Invalid 'day' parameter. Valid values are: ${Day.entries.joinToString(", ") { it.name.lowercase() }}")
data object InvalidAnimeType : ErrorMessages("Invalid 'type' parameter. Valid values are: ${AnimeTypes.entries.joinToString(", ") { it.name.lowercase() }}") data object InvalidAnimeType : ErrorMessages("Invalid 'type' parameter. Valid values are: ${AnimeTypes.entries.joinToString(", ") { it.name.lowercase() }}")
data object InvalidMangaType : ErrorMessages("Invalid 'type' parameter. Valid values are: ${MangaTypes.entries.joinToString(", ") { it.name.lowercase() }}")
data object InvalidSizeAndPage : ErrorMessages("Invalid page and size parameters")
data object InvalidTopAnimeType : ErrorMessages("Invalid 'type' parameter. Valid values are: $animeTypesErrorList") data object InvalidTopAnimeType : ErrorMessages("Invalid 'type' parameter. Valid values are: $animeTypesErrorList")
data object InvalidTopAnimeFilterType : ErrorMessages("Invalid 'type' parameter. Valid values are: $animeFilterTypesErrorList") data object InvalidTopAnimeFilterType : ErrorMessages("Invalid 'type' parameter. Valid values are: $animeFilterTypesErrorList")
data object InvalidTopMangaType : ErrorMessages("Invalid 'type' parameter. Valid values are: $mangaTypesErrorList") data object InvalidTopMangaType : ErrorMessages("Invalid 'type' parameter. Valid values are: $mangaTypesErrorList")
data object InvalidTopMangaFilterType : ErrorMessages("Invalid 'type' parameter. Valid values are: $mangaFilterTypesErrorList") data object InvalidTopMangaFilterType : ErrorMessages("Invalid 'type' parameter. Valid values are: $mangaFilterTypesErrorList")
data object InvalidInput : ErrorMessages("Invalid input provided") data object InvalidInput : ErrorMessages("Invalid input provided")
data object InvalidValueTopPage : ErrorMessages("Value 26 is higher than the configured '25' max value")
data object UnauthorizedMongo : ErrorMessages("Check the MongoDb Connection String to be able to correctly access this request.") data object UnauthorizedMongo : ErrorMessages("Check the MongoDb Connection String to be able to correctly access this request.")
} }

@ -0,0 +1,12 @@
package com.jeluchu.core.models
import kotlinx.serialization.Serializable
@Serializable
data class PaginationResponse<T>(
val page: Int = 0,
val size: Int = 0,
val totalPages: Int = 0,
val totalItems: Int = 0,
val data: List<T> = emptyList()
)

@ -37,7 +37,7 @@ data class CharacterData(
top: String top: String
) = CharacterTopEntity( ) = CharacterTopEntity(
malId = malId, malId = malId,
image = images?.webp?.large.orEmpty(), image = images?.jpg?.generic.orEmpty(),
name = name, name = name,
nameKanji = nameKanji, nameKanji = nameKanji,
top = top, top = top,

@ -46,7 +46,7 @@ data class PeopleData(
top: String top: String
) = PeopleTopEntity( ) = PeopleTopEntity(
malId = malId, malId = malId,
image = images?.webp?.large.orEmpty(), image = images?.jpg?.generic.orEmpty(),
name = name, name = name,
givenName = givenName, givenName = givenName,
familyName = familyName, familyName = familyName,

@ -29,7 +29,7 @@ object Routes {
const val CHARACTER = "/characters" const val CHARACTER = "/characters"
const val LAST_EPISODES = "/lastEpisodes" const val LAST_EPISODES = "/lastEpisodes"
const val ID = "/{id}" const val ID = "/{id}"
const val TYPE = "/{type}" const val ANIME_TYPE = "/{type}"
const val DAY = "/{day}" const val DAY = "/{day}"
const val TOP_CHARACTER = "/top/character" const val TOP_CHARACTER = "/top/character"
const val RANKINGS = "/{type}/{filter}/{page}" const val RANKINGS = "/{type}/{filter}/{page}"

@ -6,9 +6,11 @@ import com.jeluchu.features.anime.models.anime.*
import com.jeluchu.features.anime.models.directory.AnimeDirectoryEntity import com.jeluchu.features.anime.models.directory.AnimeDirectoryEntity
import com.jeluchu.features.anime.models.directory.AnimeTypeEntity import com.jeluchu.features.anime.models.directory.AnimeTypeEntity
import com.jeluchu.features.rankings.models.AnimeTopEntity import com.jeluchu.features.rankings.models.AnimeTopEntity
import com.jeluchu.features.rankings.models.CharacterTopEntity
import com.jeluchu.features.rankings.models.MangaTopEntity
import com.jeluchu.features.rankings.models.PeopleTopEntity
import com.jeluchu.features.schedule.models.DayEntity import com.jeluchu.features.schedule.models.DayEntity
import org.bson.Document import org.bson.Document
import java.sql.Timestamp
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@ -231,7 +233,7 @@ fun documentToScheduleDayEntity(doc: Document) = DayEntity(
title = doc.getStringSafe("title") title = doc.getStringSafe("title")
) )
fun documentToTopEntity(doc: Document) = AnimeTopEntity( fun documentToAnimeTopEntity(doc: Document) = AnimeTopEntity(
malId = doc.getIntSafe("malId"), malId = doc.getIntSafe("malId"),
rank = doc.getIntSafe("rank"), rank = doc.getIntSafe("rank"),
score = doc.getFloatSafe("score"), score = doc.getFloatSafe("score"),
@ -247,6 +249,41 @@ fun documentToTopEntity(doc: Document) = AnimeTopEntity(
page = doc.getIntSafe("page"), page = doc.getIntSafe("page"),
) )
fun documentToMangaTopEntity(doc: Document) = MangaTopEntity(
malId = doc.getIntSafe("malId"),
rank = doc.getIntSafe("rank"),
score = doc.getDoubleSafe("score"),
title = doc.getStringSafe("title"),
image = doc.getStringSafe("image"),
url = doc.getStringSafe("url"),
volumes = doc.getIntSafe("volumes"),
chapters = doc.getIntSafe("chapters"),
status = doc.getStringSafe("status"),
type = doc.getStringSafe("type"),
subtype = doc.getStringSafe("subtype"),
page = doc.getIntSafe("page"),
)
fun documentToPeopleTopEntity(doc: Document) = PeopleTopEntity(
malId = doc.getIntSafe("malId"),
name = doc.getStringSafe("name"),
givenName = doc.getStringSafe("givenName"),
familyName = doc.getStringSafe("familyName"),
image = doc.getStringSafe("image"),
birthday = doc.getStringSafe("birthday"),
page = doc.getIntSafe("page"),
top = doc.getStringSafe("top"),
)
fun documentToCharacterTopEntity(doc: Document) = CharacterTopEntity(
malId = doc.getIntSafe("malId"),
name = doc.getStringSafe("name"),
nameKanji = doc.getStringSafe("nameKanji"),
image = doc.getStringSafe("image"),
top = doc.getStringSafe("top"),
page = doc.getIntSafe("page"),
)
fun documentToAnimeTypeEntity(doc: Document) = AnimeTypeEntity( fun documentToAnimeTypeEntity(doc: Document) = AnimeTypeEntity(
score = doc.getString("score"), score = doc.getString("score"),
malId = doc.getIntSafe("malId"), malId = doc.getIntSafe("malId"),

@ -19,6 +19,6 @@ fun Route.animeEndpoints(
route(Routes.DIRECTORY) { route(Routes.DIRECTORY) {
getToJson { service.getDirectory(call) } getToJson { service.getDirectory(call) }
getToJson(Routes.TYPE) { directoryService.getAnimeByType(call) } getToJson(Routes.ANIME_TYPE) { directoryService.getAnimeByType(call) }
} }
} }

@ -1,12 +1,15 @@
package com.jeluchu.features.anime.services package com.jeluchu.features.anime.services
import com.jeluchu.core.connection.RestClient import com.jeluchu.core.connection.RestClient
import com.jeluchu.core.enums.AnimeTypes
import com.jeluchu.core.enums.Day import com.jeluchu.core.enums.Day
import com.jeluchu.core.enums.TimeUnit import com.jeluchu.core.enums.TimeUnit
import com.jeluchu.core.enums.parseAnimeType
import com.jeluchu.core.extensions.needsUpdate import com.jeluchu.core.extensions.needsUpdate
import com.jeluchu.core.extensions.update import com.jeluchu.core.extensions.update
import com.jeluchu.core.messages.ErrorMessages import com.jeluchu.core.messages.ErrorMessages
import com.jeluchu.core.models.ErrorResponse import com.jeluchu.core.models.ErrorResponse
import com.jeluchu.core.models.PaginationResponse
import com.jeluchu.core.models.animeflv.lastepisodes.LastEpisodeData.Companion.toEpisodeEntity import com.jeluchu.core.models.animeflv.lastepisodes.LastEpisodeData.Companion.toEpisodeEntity
import com.jeluchu.core.models.animeflv.lastepisodes.LastEpisodes import com.jeluchu.core.models.animeflv.lastepisodes.LastEpisodes
import com.jeluchu.core.models.jikan.anime.AnimeData.Companion.toDayEntity import com.jeluchu.core.models.jikan.anime.AnimeData.Companion.toDayEntity
@ -14,10 +17,7 @@ import com.jeluchu.core.utils.BaseUrls
import com.jeluchu.core.utils.Collections import com.jeluchu.core.utils.Collections
import com.jeluchu.core.utils.Endpoints import com.jeluchu.core.utils.Endpoints
import com.jeluchu.core.utils.TimerKey import com.jeluchu.core.utils.TimerKey
import com.jeluchu.features.anime.mappers.documentToAnimeDirectoryEntity import com.jeluchu.features.anime.mappers.*
import com.jeluchu.features.anime.mappers.documentToLastEpisodesEntity
import com.jeluchu.features.anime.mappers.documentToMoreInfoEntity
import com.jeluchu.features.anime.mappers.documentToScheduleDayEntity
import com.jeluchu.features.schedule.models.ScheduleEntity import com.jeluchu.features.schedule.models.ScheduleEntity
import com.mongodb.client.MongoDatabase import com.mongodb.client.MongoDatabase
import com.mongodb.client.model.Filters import com.mongodb.client.model.Filters
@ -36,10 +36,46 @@ class AnimeService(
private val lastEpisodesCollection = database.getCollection(Collections.LAST_EPISODES) private val lastEpisodesCollection = database.getCollection(Collections.LAST_EPISODES)
suspend fun getDirectory(call: RoutingCall) = try { suspend fun getDirectory(call: RoutingCall) = try {
val elements = directoryCollection.find().toList() val type = call.request.queryParameters["type"].orEmpty()
val directory = elements.map { documentToAnimeDirectoryEntity(it) } val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
val json = Json.encodeToString(directory) val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 10
call.respond(HttpStatusCode.OK, json)
if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
val skipCount = (page - 1) * size
if (parseAnimeType(type) == null) {
val animes = directoryCollection
.find()
.skip(skipCount)
.limit(size)
.toList()
val elements = animes.map { documentToAnimeDirectoryEntity(it) }
val response = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
} else {
val animes = directoryCollection
.find(Filters.eq("type", type.uppercase()))
.skip(skipCount)
.limit(size)
.toList()
val elements = animes.map { documentToAnimeTypeEntity(it) }
val response = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
}
} catch (ex: Exception) { } catch (ex: Exception) {
call.respond(HttpStatusCode.Unauthorized, ErrorResponse(ErrorMessages.UnauthorizedMongo.message)) call.respond(HttpStatusCode.Unauthorized, ErrorResponse(ErrorMessages.UnauthorizedMongo.message))
} }
@ -87,5 +123,10 @@ class AnimeService(
val directory = map { documentToLastEpisodesEntity(it) } val directory = map { documentToLastEpisodesEntity(it) }
return Json.encodeToString(directory) return Json.encodeToString(directory)
} }
private fun List<Document>.documentAnimeTypeMapper(): String {
val directory = map { documentToAnimeTypeEntity(it) }
return Json.encodeToString(directory)
}
} }

@ -6,6 +6,7 @@ import com.jeluchu.core.extensions.needsUpdate
import com.jeluchu.core.extensions.update import com.jeluchu.core.extensions.update
import com.jeluchu.core.messages.ErrorMessages import com.jeluchu.core.messages.ErrorMessages
import com.jeluchu.core.models.ErrorResponse import com.jeluchu.core.models.ErrorResponse
import com.jeluchu.core.models.PaginationResponse
import com.jeluchu.core.utils.Collections import com.jeluchu.core.utils.Collections
import com.jeluchu.core.utils.TimerKey import com.jeluchu.core.utils.TimerKey
import com.jeluchu.features.anime.mappers.documentToAnimeTypeEntity import com.jeluchu.features.anime.mappers.documentToAnimeTypeEntity
@ -25,7 +26,13 @@ class DirectoryService(
private val directory = database.getCollection(Collections.ANIME_DETAILS) private val directory = database.getCollection(Collections.ANIME_DETAILS)
suspend fun getAnimeByType(call: RoutingCall) { 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.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidAnimeType.message)
if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
val skipCount = (page - 1) * size
if (parseAnimeType(param) == null) call.respond( if (parseAnimeType(param) == null) call.respond(
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
ErrorResponse(ErrorMessages.InvalidAnimeType.message) ErrorResponse(ErrorMessages.InvalidAnimeType.message)
@ -42,16 +49,39 @@ class DirectoryService(
val collection = database.getCollection(timerKey) val collection = database.getCollection(timerKey)
collection.deleteMany(Document()) collection.deleteMany(Document())
val animes = directory.find(Filters.eq("type", param.uppercase())).toList() val animes = directory
.find(Filters.eq("type", param.uppercase()))
.skip(skipCount)
.limit(size)
.toList()
val animeTypes = animes.map { documentToAnimeTypeEntity(it) } val animeTypes = animes.map { documentToAnimeTypeEntity(it) }
val documents = animeTypes.map { anime -> Document.parse(Json.encodeToString(anime)) } val documents = animeTypes.map { anime -> Document.parse(Json.encodeToString(anime)) }
if (documents.isNotEmpty()) collection.insertMany(documents) if (documents.isNotEmpty()) collection.insertMany(documents)
timers.update(timerKey) timers.update(timerKey)
call.respond(HttpStatusCode.OK, Json.encodeToString(animeTypes)) val response = PaginationResponse(
page = page,
size = size,
data = animeTypes,
totalItems = directory.countDocuments().toInt()
)
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
} else { } else {
val elements = directory.find().toList() val elements = directory.find()
call.respond(HttpStatusCode.OK, elements.documentAnimeTypeMapper()) .skip(skipCount)
.limit(size)
.toList()
val response = PaginationResponse(
page = page,
size = size,
totalItems = directory.countDocuments().toInt(),
data = elements.map { documentToAnimeTypeEntity(it) }
)
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
} }
} }

@ -11,15 +11,15 @@ fun Route.rankingsEndpoints(
service: RankingsService = RankingsService(mongoDatabase) service: RankingsService = RankingsService(mongoDatabase)
) = route(Routes.TOP) { ) = route(Routes.TOP) {
route(Routes.ANIME) { route(Routes.ANIME) {
getToJson(Routes.RANKINGS) { service.getAnimeRanking(call) } getToJson { service.getAnimeRanking(call) }
} }
route(Routes.MANGA) { route(Routes.MANGA) {
getToJson(Routes.RANKINGS) { service.getMangaRanking(call) } getToJson { service.getMangaRanking(call) }
} }
route(Routes.PEOPLE) { route(Routes.PEOPLE) {
getToJson(Routes.PAGE) { service.getPeopleRanking(call) } getToJson { service.getPeopleRanking(call) }
} }
route(Routes.CHARACTER) { route(Routes.CHARACTER) {
getToJson(Routes.PAGE) { service.getCharacterRanking(call) } getToJson { service.getCharacterRanking(call) }
} }
} }

@ -6,6 +6,7 @@ import com.jeluchu.core.extensions.needsUpdate
import com.jeluchu.core.extensions.update import com.jeluchu.core.extensions.update
import com.jeluchu.core.messages.ErrorMessages import com.jeluchu.core.messages.ErrorMessages
import com.jeluchu.core.models.ErrorResponse import com.jeluchu.core.models.ErrorResponse
import com.jeluchu.core.models.PaginationResponse
import com.jeluchu.core.models.jikan.anime.AnimeData.Companion.toAnimeTopEntity import com.jeluchu.core.models.jikan.anime.AnimeData.Companion.toAnimeTopEntity
import com.jeluchu.core.models.jikan.character.CharacterSearch import com.jeluchu.core.models.jikan.character.CharacterSearch
import com.jeluchu.core.models.jikan.manga.MangaData.Companion.toMangaTopEntity import com.jeluchu.core.models.jikan.manga.MangaData.Companion.toMangaTopEntity
@ -17,7 +18,10 @@ import com.jeluchu.core.utils.BaseUrls
import com.jeluchu.core.utils.Collections import com.jeluchu.core.utils.Collections
import com.jeluchu.core.utils.Endpoints import com.jeluchu.core.utils.Endpoints
import com.jeluchu.core.utils.parseDataToDocuments import com.jeluchu.core.utils.parseDataToDocuments
import com.jeluchu.features.anime.mappers.documentToTopEntity import com.jeluchu.features.anime.mappers.documentToAnimeTopEntity
import com.jeluchu.features.anime.mappers.documentToCharacterTopEntity
import com.jeluchu.features.anime.mappers.documentToMangaTopEntity
import com.jeluchu.features.anime.mappers.documentToPeopleTopEntity
import com.jeluchu.features.rankings.models.AnimeTopEntity import com.jeluchu.features.rankings.models.AnimeTopEntity
import com.jeluchu.features.rankings.models.CharacterTopEntity import com.jeluchu.features.rankings.models.CharacterTopEntity
import com.jeluchu.features.rankings.models.MangaTopEntity import com.jeluchu.features.rankings.models.MangaTopEntity
@ -41,13 +45,15 @@ class RankingsService(
private val characterRanking = database.getCollection(Collections.CHARACTER_RANKING) private val characterRanking = database.getCollection(Collections.CHARACTER_RANKING)
suspend fun getAnimeRanking(call: RoutingCall) { suspend fun getAnimeRanking(call: RoutingCall) {
val paramType = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopAnimeType.message) val filter = call.request.queryParameters["filter"] ?: "airing"
val paramPage = call.parameters["page"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
val paramFilter = call.parameters["filter"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopAnimeFilterType.message) val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 25
if (parseAnimeType(paramType) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopAnimeType.message)) val type = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopAnimeType.message)
if (parseAnimeFilterType(paramFilter) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopAnimeFilterType.message))
val timerKey = "${Collections.ANIME_RANKING}_${paramType}_${paramFilter}_${paramPage}" if (size > 25) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidValueTopPage.message))
if (parseAnimeType(type) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopAnimeType.message))
val timerKey = "${Collections.ANIME_RANKING}_${type}_${filter}_${page}"
val needsUpdate = timers.needsUpdate( val needsUpdate = timers.needsUpdate(
amount = 30, amount = 30,
@ -55,19 +61,22 @@ class RankingsService(
unit = TimeUnit.DAY unit = TimeUnit.DAY
) )
if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
val skipCount = (page - 1) * size
if (needsUpdate) { if (needsUpdate) {
animeRanking.deleteMany( animeRanking.deleteMany(
Filters.and( Filters.and(
Filters.eq("page", paramPage), Filters.eq("page", page),
Filters.eq("type", paramType), Filters.eq("type", type),
Filters.eq("subtype", paramFilter) Filters.eq("subtype", filter)
) )
) )
val params = mutableListOf<String>() val params = mutableListOf<String>()
params.add("type=$paramType") params.add("type=$type")
params.add("page=$paramPage") params.add("page=$page")
params.add("filter=$paramFilter") params.add("filter=$filter")
val response = RestClient.request( val response = RestClient.request(
BaseUrls.JIKAN + Endpoints.TOP_ANIME + "?${params.joinToString("&")}", BaseUrls.JIKAN + Endpoints.TOP_ANIME + "?${params.joinToString("&")}",
@ -75,9 +84,9 @@ class RankingsService(
).data?.map { anime -> ).data?.map { anime ->
anime.toAnimeTopEntity( anime.toAnimeTopEntity(
top = "anime", top = "anime",
page = paramPage, page = page,
type = paramType, type = type,
subType = paramFilter subType = filter
) )
} }
@ -85,42 +94,66 @@ class RankingsService(
if (documentsToInsert.isNotEmpty()) animeRanking.insertMany(documentsToInsert) if (documentsToInsert.isNotEmpty()) animeRanking.insertMany(documentsToInsert)
timers.update(timerKey) timers.update(timerKey)
call.respond(HttpStatusCode.OK, Json.encodeToString(response)) val elements = documentsToInsert.map { documentToAnimeTopEntity(it) }
val paginationResponse = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(paginationResponse))
} else { } else {
val elements = animeRanking.find().toList() val animes = animeRanking
val directory = elements.map { documentToTopEntity(it) } .find(Filters.eq("type", type.lowercase()))
val json = Json.encodeToString(directory) .skip(skipCount)
call.respond(HttpStatusCode.OK, json) .limit(size)
.toList()
val elements = animes.map { documentToAnimeTopEntity(it) }
val response = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
} }
} }
suspend fun getMangaRanking(call: RoutingCall) { suspend fun getMangaRanking(call: RoutingCall) {
val paramType = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopMangaType.message) val filter = call.request.queryParameters["filter"] ?: "publishing"
val paramPage = call.parameters["page"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
val paramFilter = call.parameters["filter"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopMangaFilterType.message) val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 25
if (parseMangaType(paramType) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopMangaType.message)) val type = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopMangaType.message)
if (parseMangaFilterType(paramFilter) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopMangaFilterType.message))
if (size > 25) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidValueTopPage.message))
if (parseMangaType(type) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopAnimeType.message))
val timerKey = "${Collections.MANGA_RANKING}_${type}_${filter}_${page}"
val timerKey = "${Collections.MANGA_RANKING}_${paramType}_${paramFilter}_${paramPage}"
val needsUpdate = timers.needsUpdate( val needsUpdate = timers.needsUpdate(
amount = 30, amount = 30,
key = timerKey, key = timerKey,
unit = TimeUnit.DAY unit = TimeUnit.DAY
) )
if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
val skipCount = (page - 1) * size
if (needsUpdate) { if (needsUpdate) {
mangaRanking.deleteMany( mangaRanking.deleteMany(
Filters.and( Filters.and(
Filters.eq("page", paramPage), Filters.eq("page", page),
Filters.eq("type", paramType), Filters.eq("type", type),
Filters.eq("subtype", paramFilter) Filters.eq("subtype", filter)
) )
) )
val params = mutableListOf<String>() val params = mutableListOf<String>()
params.add("type=$paramType") params.add("type=$type")
params.add("page=$paramPage") params.add("page=$page")
params.add("filter=$paramFilter") params.add("filter=$filter")
val response = RestClient.request( val response = RestClient.request(
BaseUrls.JIKAN + Endpoints.TOP_MANGA + "?${params.joinToString("&")}", BaseUrls.JIKAN + Endpoints.TOP_MANGA + "?${params.joinToString("&")}",
@ -128,9 +161,9 @@ class RankingsService(
).data?.map { anime -> ).data?.map { anime ->
anime.toMangaTopEntity( anime.toMangaTopEntity(
top = "manga", top = "manga",
page = paramPage, page = page,
type = paramType, type = type,
subType = paramFilter subType = filter
) )
} }
@ -138,32 +171,58 @@ class RankingsService(
if (documentsToInsert.isNotEmpty()) mangaRanking.insertMany(documentsToInsert) if (documentsToInsert.isNotEmpty()) mangaRanking.insertMany(documentsToInsert)
timers.update(timerKey) timers.update(timerKey)
call.respond(HttpStatusCode.OK, Json.encodeToString(response)) val elements = documentsToInsert.map { documentToMangaTopEntity(it) }
val paginationResponse = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(paginationResponse))
} else { } else {
val elements = mangaRanking.find().toList() val mangas = mangaRanking
val directory = elements.map { documentToTopEntity(it) } .find(Filters.eq("type", type.lowercase()))
val json = Json.encodeToString(directory) .skip(skipCount)
call.respond(HttpStatusCode.OK, json) .limit(size)
.toList()
val elements = mangas.map { documentToMangaTopEntity(it) }
val response = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
} }
} }
suspend fun getPeopleRanking(call: RoutingCall) { suspend fun getPeopleRanking(call: RoutingCall) {
val paramPage = call.parameters["page"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
val timerKey = "${Collections.PEOPLE_RANKING}_${paramPage}" val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 25
if (size > 25) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidValueTopPage.message))
val timerKey = "${Collections.PEOPLE_RANKING}_${page}"
val needsUpdate = timers.needsUpdate( val needsUpdate = timers.needsUpdate(
amount = 30, amount = 30,
key = timerKey, key = timerKey,
unit = TimeUnit.DAY unit = TimeUnit.DAY
) )
if (needsUpdate) { peopleRanking.deleteMany(Filters.and(Filters.eq("page", paramPage))) if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
val skipCount = (page - 1) * size
if (needsUpdate) {
peopleRanking.deleteMany(Filters.and(Filters.eq("page", page)))
val response = RestClient.request( val response = RestClient.request(
BaseUrls.JIKAN + Endpoints.TOP_PEOPLE + "?page=$paramPage", BaseUrls.JIKAN + Endpoints.TOP_PEOPLE + "?page=$page",
PeopleSearch.serializer() PeopleSearch.serializer()
).data?.map { anime -> ).data?.map { anime ->
anime.toPeopleTopEntity( anime.toPeopleTopEntity(
top = "people", top = "people",
page = paramPage page = page
) )
} }
@ -171,32 +230,58 @@ class RankingsService(
if (documentsToInsert.isNotEmpty()) peopleRanking.insertMany(documentsToInsert) if (documentsToInsert.isNotEmpty()) peopleRanking.insertMany(documentsToInsert)
timers.update(timerKey) timers.update(timerKey)
call.respond(HttpStatusCode.OK, Json.encodeToString(response)) val elements = documentsToInsert.map { documentToPeopleTopEntity(it) }
val paginationResponse = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(paginationResponse))
} else { } else {
val elements = peopleRanking.find().toList() val peoples = peopleRanking
val directory = elements.map { documentToTopEntity(it) } .find()
val json = Json.encodeToString(directory) .skip(skipCount)
call.respond(HttpStatusCode.OK, json) .limit(size)
.toList()
val elements = peoples.map { documentToPeopleTopEntity(it) }
val response = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
} }
} }
suspend fun getCharacterRanking(call: RoutingCall) { suspend fun getCharacterRanking(call: RoutingCall) {
val paramPage = call.parameters["page"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
val timerKey = "${Collections.CHARACTER_RANKING}_${paramPage}" val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 25
if (size > 25) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidValueTopPage.message))
val timerKey = "${Collections.CHARACTER_RANKING}_${page}"
val needsUpdate = timers.needsUpdate( val needsUpdate = timers.needsUpdate(
amount = 30, amount = 30,
key = timerKey, key = timerKey,
unit = TimeUnit.DAY unit = TimeUnit.DAY
) )
if (needsUpdate) { characterRanking.deleteMany(Filters.and(Filters.eq("page", paramPage))) if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
val skipCount = (page - 1) * size
if (needsUpdate) {
characterRanking.deleteMany(Filters.and(Filters.eq("page", page)))
val response = RestClient.request( val response = RestClient.request(
BaseUrls.JIKAN + Endpoints.TOP_CHARACTER + "?page=$paramPage", BaseUrls.JIKAN + Endpoints.TOP_CHARACTER + "?page=$page",
CharacterSearch.serializer() CharacterSearch.serializer()
).data?.map { anime -> ).data?.map { anime ->
anime.toCharacterTopEntity( anime.toCharacterTopEntity(
top = "character", top = "character",
page = paramPage page = page
) )
} }
@ -204,12 +289,30 @@ class RankingsService(
if (documentsToInsert.isNotEmpty()) characterRanking.insertMany(documentsToInsert) if (documentsToInsert.isNotEmpty()) characterRanking.insertMany(documentsToInsert)
timers.update(timerKey) timers.update(timerKey)
call.respond(HttpStatusCode.OK, Json.encodeToString(response)) val elements = documentsToInsert.map { documentToCharacterTopEntity(it) }
val paginationResponse = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(paginationResponse))
} else { } else {
val elements = characterRanking.find().toList() val characters = characterRanking
val directory = elements.map { documentToTopEntity(it) } .find()
val json = Json.encodeToString(directory) .skip(skipCount)
call.respond(HttpStatusCode.OK, json) .limit(size)
.toList()
val elements = characters.map { documentToCharacterTopEntity(it) }
val response = PaginationResponse(
page = page,
size = size,
data = elements
)
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
} }
} }
} }
Loading…
Cancel
Save