Skip to content

Commit df6baa7

Browse files
committed
Include pagination with types in rankings and directory
1 parent 5d1b33e commit df6baa7

File tree

11 files changed

+309
-83
lines changed

11 files changed

+309
-83
lines changed

src/main/kotlin/com/jeluchu/core/messages/ErrorMessages.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ sealed class ErrorMessages(val message: String) {
88
data object InvalidMalId : ErrorMessages("The provided id of malId is invalid")
99
data object InvalidDay : ErrorMessages("Invalid 'day' parameter. Valid values are: ${Day.entries.joinToString(", ") { it.name.lowercase() }}")
1010
data object InvalidAnimeType : ErrorMessages("Invalid 'type' parameter. Valid values are: ${AnimeTypes.entries.joinToString(", ") { it.name.lowercase() }}")
11+
data object InvalidMangaType : ErrorMessages("Invalid 'type' parameter. Valid values are: ${MangaTypes.entries.joinToString(", ") { it.name.lowercase() }}")
12+
data object InvalidSizeAndPage : ErrorMessages("Invalid page and size parameters")
1113
data object InvalidTopAnimeType : ErrorMessages("Invalid 'type' parameter. Valid values are: $animeTypesErrorList")
1214
data object InvalidTopAnimeFilterType : ErrorMessages("Invalid 'type' parameter. Valid values are: $animeFilterTypesErrorList")
1315
data object InvalidTopMangaType : ErrorMessages("Invalid 'type' parameter. Valid values are: $mangaTypesErrorList")
1416
data object InvalidTopMangaFilterType : ErrorMessages("Invalid 'type' parameter. Valid values are: $mangaFilterTypesErrorList")
1517
data object InvalidInput : ErrorMessages("Invalid input provided")
18+
data object InvalidValueTopPage : ErrorMessages("Value 26 is higher than the configured '25' max value")
1619
data object UnauthorizedMongo : ErrorMessages("Check the MongoDb Connection String to be able to correctly access this request.")
1720
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.jeluchu.core.models
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
data class PaginationResponse<T>(
7+
val page: Int = 0,
8+
val size: Int = 0,
9+
val totalPages: Int = 0,
10+
val totalItems: Int = 0,
11+
val data: List<T> = emptyList()
12+
)

src/main/kotlin/com/jeluchu/core/models/jikan/character/CharacterData.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ data class CharacterData(
3737
top: String
3838
) = CharacterTopEntity(
3939
malId = malId,
40-
image = images?.webp?.large.orEmpty(),
40+
image = images?.jpg?.generic.orEmpty(),
4141
name = name,
4242
nameKanji = nameKanji,
4343
top = top,

src/main/kotlin/com/jeluchu/core/models/jikan/people/PeopleData.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ data class PeopleData(
4646
top: String
4747
) = PeopleTopEntity(
4848
malId = malId,
49-
image = images?.webp?.large.orEmpty(),
49+
image = images?.jpg?.generic.orEmpty(),
5050
name = name,
5151
givenName = givenName,
5252
familyName = familyName,

src/main/kotlin/com/jeluchu/core/utils/Constants.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ object Routes {
2929
const val CHARACTER = "/characters"
3030
const val LAST_EPISODES = "/lastEpisodes"
3131
const val ID = "/{id}"
32-
const val TYPE = "/{type}"
32+
const val ANIME_TYPE = "/{type}"
3333
const val DAY = "/{day}"
3434
const val TOP_CHARACTER = "/top/character"
3535
const val RANKINGS = "/{type}/{filter}/{page}"

src/main/kotlin/com/jeluchu/features/anime/mappers/AnimeMappers.kt

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import com.jeluchu.features.anime.models.anime.*
66
import com.jeluchu.features.anime.models.directory.AnimeDirectoryEntity
77
import com.jeluchu.features.anime.models.directory.AnimeTypeEntity
88
import com.jeluchu.features.rankings.models.AnimeTopEntity
9+
import com.jeluchu.features.rankings.models.CharacterTopEntity
10+
import com.jeluchu.features.rankings.models.MangaTopEntity
11+
import com.jeluchu.features.rankings.models.PeopleTopEntity
912
import com.jeluchu.features.schedule.models.DayEntity
1013
import org.bson.Document
11-
import java.sql.Timestamp
1214
import java.time.ZonedDateTime
1315
import java.time.format.DateTimeFormatter
1416

@@ -231,7 +233,7 @@ fun documentToScheduleDayEntity(doc: Document) = DayEntity(
231233
title = doc.getStringSafe("title")
232234
)
233235

234-
fun documentToTopEntity(doc: Document) = AnimeTopEntity(
236+
fun documentToAnimeTopEntity(doc: Document) = AnimeTopEntity(
235237
malId = doc.getIntSafe("malId"),
236238
rank = doc.getIntSafe("rank"),
237239
score = doc.getFloatSafe("score"),
@@ -247,6 +249,41 @@ fun documentToTopEntity(doc: Document) = AnimeTopEntity(
247249
page = doc.getIntSafe("page"),
248250
)
249251

252+
fun documentToMangaTopEntity(doc: Document) = MangaTopEntity(
253+
malId = doc.getIntSafe("malId"),
254+
rank = doc.getIntSafe("rank"),
255+
score = doc.getDoubleSafe("score"),
256+
title = doc.getStringSafe("title"),
257+
image = doc.getStringSafe("image"),
258+
url = doc.getStringSafe("url"),
259+
volumes = doc.getIntSafe("volumes"),
260+
chapters = doc.getIntSafe("chapters"),
261+
status = doc.getStringSafe("status"),
262+
type = doc.getStringSafe("type"),
263+
subtype = doc.getStringSafe("subtype"),
264+
page = doc.getIntSafe("page"),
265+
)
266+
267+
fun documentToPeopleTopEntity(doc: Document) = PeopleTopEntity(
268+
malId = doc.getIntSafe("malId"),
269+
name = doc.getStringSafe("name"),
270+
givenName = doc.getStringSafe("givenName"),
271+
familyName = doc.getStringSafe("familyName"),
272+
image = doc.getStringSafe("image"),
273+
birthday = doc.getStringSafe("birthday"),
274+
page = doc.getIntSafe("page"),
275+
top = doc.getStringSafe("top"),
276+
)
277+
278+
fun documentToCharacterTopEntity(doc: Document) = CharacterTopEntity(
279+
malId = doc.getIntSafe("malId"),
280+
name = doc.getStringSafe("name"),
281+
nameKanji = doc.getStringSafe("nameKanji"),
282+
image = doc.getStringSafe("image"),
283+
top = doc.getStringSafe("top"),
284+
page = doc.getIntSafe("page"),
285+
)
286+
250287
fun documentToAnimeTypeEntity(doc: Document) = AnimeTypeEntity(
251288
score = doc.getString("score"),
252289
malId = doc.getIntSafe("malId"),

src/main/kotlin/com/jeluchu/features/anime/routes/AnimeRoutes.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ fun Route.animeEndpoints(
1919

2020
route(Routes.DIRECTORY) {
2121
getToJson { service.getDirectory(call) }
22-
getToJson(Routes.TYPE) { directoryService.getAnimeByType(call) }
22+
getToJson(Routes.ANIME_TYPE) { directoryService.getAnimeByType(call) }
2323
}
2424
}

src/main/kotlin/com/jeluchu/features/anime/services/AnimeService.kt

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
package com.jeluchu.features.anime.services
22

33
import com.jeluchu.core.connection.RestClient
4+
import com.jeluchu.core.enums.AnimeTypes
45
import com.jeluchu.core.enums.Day
56
import com.jeluchu.core.enums.TimeUnit
7+
import com.jeluchu.core.enums.parseAnimeType
68
import com.jeluchu.core.extensions.needsUpdate
79
import com.jeluchu.core.extensions.update
810
import com.jeluchu.core.messages.ErrorMessages
911
import com.jeluchu.core.models.ErrorResponse
12+
import com.jeluchu.core.models.PaginationResponse
1013
import com.jeluchu.core.models.animeflv.lastepisodes.LastEpisodeData.Companion.toEpisodeEntity
1114
import com.jeluchu.core.models.animeflv.lastepisodes.LastEpisodes
1215
import com.jeluchu.core.models.jikan.anime.AnimeData.Companion.toDayEntity
1316
import com.jeluchu.core.utils.BaseUrls
1417
import com.jeluchu.core.utils.Collections
1518
import com.jeluchu.core.utils.Endpoints
1619
import com.jeluchu.core.utils.TimerKey
17-
import com.jeluchu.features.anime.mappers.documentToAnimeDirectoryEntity
18-
import com.jeluchu.features.anime.mappers.documentToLastEpisodesEntity
19-
import com.jeluchu.features.anime.mappers.documentToMoreInfoEntity
20-
import com.jeluchu.features.anime.mappers.documentToScheduleDayEntity
20+
import com.jeluchu.features.anime.mappers.*
2121
import com.jeluchu.features.schedule.models.ScheduleEntity
2222
import com.mongodb.client.MongoDatabase
2323
import com.mongodb.client.model.Filters
@@ -36,10 +36,46 @@ class AnimeService(
3636
private val lastEpisodesCollection = database.getCollection(Collections.LAST_EPISODES)
3737

3838
suspend fun getDirectory(call: RoutingCall) = try {
39-
val elements = directoryCollection.find().toList()
40-
val directory = elements.map { documentToAnimeDirectoryEntity(it) }
41-
val json = Json.encodeToString(directory)
42-
call.respond(HttpStatusCode.OK, json)
39+
val type = call.request.queryParameters["type"].orEmpty()
40+
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
41+
val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 10
42+
43+
if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
44+
val skipCount = (page - 1) * size
45+
46+
if (parseAnimeType(type) == null) {
47+
val animes = directoryCollection
48+
.find()
49+
.skip(skipCount)
50+
.limit(size)
51+
.toList()
52+
53+
val elements = animes.map { documentToAnimeDirectoryEntity(it) }
54+
55+
val response = PaginationResponse(
56+
page = page,
57+
size = size,
58+
data = elements
59+
)
60+
61+
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
62+
} else {
63+
val animes = directoryCollection
64+
.find(Filters.eq("type", type.uppercase()))
65+
.skip(skipCount)
66+
.limit(size)
67+
.toList()
68+
69+
val elements = animes.map { documentToAnimeTypeEntity(it) }
70+
71+
val response = PaginationResponse(
72+
page = page,
73+
size = size,
74+
data = elements
75+
)
76+
77+
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
78+
}
4379
} catch (ex: Exception) {
4480
call.respond(HttpStatusCode.Unauthorized, ErrorResponse(ErrorMessages.UnauthorizedMongo.message))
4581
}
@@ -87,5 +123,10 @@ class AnimeService(
87123
val directory = map { documentToLastEpisodesEntity(it) }
88124
return Json.encodeToString(directory)
89125
}
126+
127+
private fun List<Document>.documentAnimeTypeMapper(): String {
128+
val directory = map { documentToAnimeTypeEntity(it) }
129+
return Json.encodeToString(directory)
130+
}
90131
}
91132

src/main/kotlin/com/jeluchu/features/anime/services/DirectoryService.kt

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.jeluchu.core.extensions.needsUpdate
66
import com.jeluchu.core.extensions.update
77
import com.jeluchu.core.messages.ErrorMessages
88
import com.jeluchu.core.models.ErrorResponse
9+
import com.jeluchu.core.models.PaginationResponse
910
import com.jeluchu.core.utils.Collections
1011
import com.jeluchu.core.utils.TimerKey
1112
import com.jeluchu.features.anime.mappers.documentToAnimeTypeEntity
@@ -25,7 +26,13 @@ class DirectoryService(
2526
private val directory = database.getCollection(Collections.ANIME_DETAILS)
2627

2728
suspend fun getAnimeByType(call: RoutingCall) {
29+
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
30+
val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 10
2831
val param = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidAnimeType.message)
32+
33+
if (page < 1 || size < 1) call.respond(HttpStatusCode.BadRequest, ErrorMessages.InvalidSizeAndPage.message)
34+
val skipCount = (page - 1) * size
35+
2936
if (parseAnimeType(param) == null) call.respond(
3037
HttpStatusCode.BadRequest,
3138
ErrorResponse(ErrorMessages.InvalidAnimeType.message)
@@ -42,16 +49,39 @@ class DirectoryService(
4249
val collection = database.getCollection(timerKey)
4350
collection.deleteMany(Document())
4451

45-
val animes = directory.find(Filters.eq("type", param.uppercase())).toList()
52+
val animes = directory
53+
.find(Filters.eq("type", param.uppercase()))
54+
.skip(skipCount)
55+
.limit(size)
56+
.toList()
57+
4658
val animeTypes = animes.map { documentToAnimeTypeEntity(it) }
4759
val documents = animeTypes.map { anime -> Document.parse(Json.encodeToString(anime)) }
4860
if (documents.isNotEmpty()) collection.insertMany(documents)
4961
timers.update(timerKey)
5062

51-
call.respond(HttpStatusCode.OK, Json.encodeToString(animeTypes))
63+
val response = PaginationResponse(
64+
page = page,
65+
size = size,
66+
data = animeTypes,
67+
totalItems = directory.countDocuments().toInt()
68+
)
69+
70+
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
5271
} else {
53-
val elements = directory.find().toList()
54-
call.respond(HttpStatusCode.OK, elements.documentAnimeTypeMapper())
72+
val elements = directory.find()
73+
.skip(skipCount)
74+
.limit(size)
75+
.toList()
76+
77+
val response = PaginationResponse(
78+
page = page,
79+
size = size,
80+
totalItems = directory.countDocuments().toInt(),
81+
data = elements.map { documentToAnimeTypeEntity(it) }
82+
)
83+
84+
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
5585
}
5686
}
5787

src/main/kotlin/com/jeluchu/features/rankings/routes/RankingsRoutes.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ fun Route.rankingsEndpoints(
1111
service: RankingsService = RankingsService(mongoDatabase)
1212
) = route(Routes.TOP) {
1313
route(Routes.ANIME) {
14-
getToJson(Routes.RANKINGS) { service.getAnimeRanking(call) }
14+
getToJson { service.getAnimeRanking(call) }
1515
}
1616
route(Routes.MANGA) {
17-
getToJson(Routes.RANKINGS) { service.getMangaRanking(call) }
17+
getToJson { service.getMangaRanking(call) }
1818
}
1919
route(Routes.PEOPLE) {
20-
getToJson(Routes.PAGE) { service.getPeopleRanking(call) }
20+
getToJson { service.getPeopleRanking(call) }
2121
}
2222
route(Routes.CHARACTER) {
23-
getToJson(Routes.PAGE) { service.getCharacterRanking(call) }
23+
getToJson { service.getCharacterRanking(call) }
2424
}
2525
}

0 commit comments

Comments
 (0)