diff --git a/src/main/kotlin/com/jeluchu/core/enums/AnimeFilterTypes.kt b/src/main/kotlin/com/jeluchu/core/enums/AnimeFilterTypes.kt index 1b97dbc..578d9a2 100644 --- a/src/main/kotlin/com/jeluchu/core/enums/AnimeFilterTypes.kt +++ b/src/main/kotlin/com/jeluchu/core/enums/AnimeFilterTypes.kt @@ -10,4 +10,5 @@ enum class AnimeFilterTypes { FAVORITE, } -fun parseFilterType(type: String) = AnimeFilterTypes.entries.firstOrNull { it.name.equals(type, ignoreCase = true) } \ No newline at end of file +val animeFilterTypesErrorList = AnimeFilterTypes.entries.joinToString(", ") { it.name.lowercase() } +fun parseAnimeFilterType(type: String) = AnimeFilterTypes.entries.firstOrNull { it.name.equals(type, ignoreCase = true) } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/enums/AnimeTypes.kt b/src/main/kotlin/com/jeluchu/core/enums/AnimeTypes.kt index e7fa87a..7be2af3 100644 --- a/src/main/kotlin/com/jeluchu/core/enums/AnimeTypes.kt +++ b/src/main/kotlin/com/jeluchu/core/enums/AnimeTypes.kt @@ -15,4 +15,5 @@ enum class AnimeTypes { TV_SPECIAL, } -fun parseType(type: String) = AnimeTypes.entries.firstOrNull { it.name.equals(type, ignoreCase = true) } \ No newline at end of file +val animeTypesErrorList = AnimeTypes.entries.joinToString(", ") { it.name.lowercase() } +fun parseAnimeType(type: String) = AnimeTypes.entries.firstOrNull { it.name.equals(type, ignoreCase = true) } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/enums/MangaFilterTypes.kt b/src/main/kotlin/com/jeluchu/core/enums/MangaFilterTypes.kt new file mode 100644 index 0000000..285874c --- /dev/null +++ b/src/main/kotlin/com/jeluchu/core/enums/MangaFilterTypes.kt @@ -0,0 +1,14 @@ +package com.jeluchu.core.enums + +import kotlinx.serialization.Serializable + +@Serializable +enum class MangaFilterTypes { + PUBLISHING, + UPCOMING, + BYPOPULARITY, + FAVORITE, +} + +val mangaFilterTypesErrorList = MangaFilterTypes.entries.joinToString(", ") { it.name.lowercase() } +fun parseMangaFilterType(type: String) = MangaFilterTypes.entries.firstOrNull { it.name.equals(type, ignoreCase = true) } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/enums/MangaTypes.kt b/src/main/kotlin/com/jeluchu/core/enums/MangaTypes.kt new file mode 100644 index 0000000..37182eb --- /dev/null +++ b/src/main/kotlin/com/jeluchu/core/enums/MangaTypes.kt @@ -0,0 +1,17 @@ +package com.jeluchu.core.enums + +import kotlinx.serialization.Serializable + +@Serializable +enum class MangaTypes { + MANGA, + NOVEL, + LIGHTNOVEL, + ONESHOT, + DOUJIN, + MANHWA, + MANHUA +} + +val mangaTypesErrorList = MangaTypes.entries.joinToString(", ") { it.name.lowercase() } +fun parseMangaType(type: String) = MangaTypes.entries.firstOrNull { it.name.equals(type, ignoreCase = true) } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/extensions/RoutesExtensions.kt b/src/main/kotlin/com/jeluchu/core/extensions/RoutesExtensions.kt index c0021cd..e87a750 100644 --- a/src/main/kotlin/com/jeluchu/core/extensions/RoutesExtensions.kt +++ b/src/main/kotlin/com/jeluchu/core/extensions/RoutesExtensions.kt @@ -11,4 +11,11 @@ fun Route.getToJson( ): Route = get(path) { call.response.headers.append(HttpHeaders.ContentType, ContentType.Application.Json.toString()) withContext(Dispatchers.IO) { request() } +} + +fun Route.getToJson( + request: suspend RoutingContext.() -> Unit +): Route = get { + call.response.headers.append(HttpHeaders.ContentType, ContentType.Application.Json.toString()) + withContext(Dispatchers.IO) { request() } } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/extensions/UpdatingExtensions.kt b/src/main/kotlin/com/jeluchu/core/extensions/UpdatingExtensions.kt index 32ca6f4..3f7faa3 100644 --- a/src/main/kotlin/com/jeluchu/core/extensions/UpdatingExtensions.kt +++ b/src/main/kotlin/com/jeluchu/core/extensions/UpdatingExtensions.kt @@ -38,7 +38,7 @@ fun MongoCollection.update(key: String) { .append(TimerKey.LAST_UPDATED, Date.from(currentTime)) replaceOne( - eq(TimerKey.KEY, TimerKey.SCHEDULE), + eq(TimerKey.KEY, key), newTimestampDocument, ReplaceOptions().upsert(true) ) diff --git a/src/main/kotlin/com/jeluchu/core/messages/ErrorMessages.kt b/src/main/kotlin/com/jeluchu/core/messages/ErrorMessages.kt index 6e7c3f3..adf2189 100644 --- a/src/main/kotlin/com/jeluchu/core/messages/ErrorMessages.kt +++ b/src/main/kotlin/com/jeluchu/core/messages/ErrorMessages.kt @@ -1,12 +1,16 @@ package com.jeluchu.core.messages -import com.jeluchu.core.enums.Day +import com.jeluchu.core.enums.* sealed class ErrorMessages(val message: String) { data object NotFound : ErrorMessages("Nyaaaaaaaan! This request has not been found by our alpaca-neko") data object AnimeNotFound : ErrorMessages("This malId is not in our database") 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 InvalidTopAnimeType : ErrorMessages("Invalid 'type' parameter. Valid values are: $animeTypesErrorList") + data object InvalidTopAnimeFilterType : ErrorMessages("Invalid 'type' parameter. Valid values are: $animeFilterTypesErrorList") + data object InvalidTopMangaType : ErrorMessages("Invalid 'type' parameter. Valid values are: $mangaTypesErrorList") + data object InvalidTopMangaFilterType : ErrorMessages("Invalid 'type' parameter. Valid values are: $mangaFilterTypesErrorList") data object InvalidInput : ErrorMessages("Invalid input provided") data object UnauthorizedMongo : ErrorMessages("Check the MongoDb Connection String to be able to correctly access this request.") } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/models/jikan/anime/AnimeData.kt b/src/main/kotlin/com/jeluchu/core/models/jikan/anime/AnimeData.kt index 116b9b7..124077d 100644 --- a/src/main/kotlin/com/jeluchu/core/models/jikan/anime/AnimeData.kt +++ b/src/main/kotlin/com/jeluchu/core/models/jikan/anime/AnimeData.kt @@ -2,7 +2,8 @@ package com.jeluchu.core.models.jikan.anime import com.jeluchu.core.enums.Day import com.jeluchu.core.utils.toVideoPromo -import com.jeluchu.features.rankings.models.TopEntity +import com.jeluchu.features.rankings.models.AnimeTopEntity +import com.jeluchu.features.rankings.models.MangaTopEntity import com.jeluchu.features.schedule.models.DayEntity import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -284,12 +285,12 @@ data class AnimeData( title = titles?.first()?.title.orEmpty() ) - fun AnimeData.toTopEntity( + fun AnimeData.toAnimeTopEntity( page: Int, - rank: Int, + top: String, type: String, subType: String, - ) = TopEntity( + ) = AnimeTopEntity( malId = malId, rank = rank, score = score, @@ -300,6 +301,7 @@ data class AnimeData( season = season, year = year, airing = airing, + top = top, type = type, subtype = subType, page = page diff --git a/src/main/kotlin/com/jeluchu/core/models/jikan/character/CharacterData.kt b/src/main/kotlin/com/jeluchu/core/models/jikan/character/CharacterData.kt new file mode 100644 index 0000000..310b14d --- /dev/null +++ b/src/main/kotlin/com/jeluchu/core/models/jikan/character/CharacterData.kt @@ -0,0 +1,49 @@ +package fordelete + +import com.jeluchu.core.models.jikan.anime.Images +import com.jeluchu.core.models.jikan.people.PeopleData +import com.jeluchu.features.rankings.models.CharacterTopEntity +import com.jeluchu.features.rankings.models.PeopleTopEntity +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CharacterData( + @SerialName("mal_id") + val malId: Int? = 0, + + @SerialName("about") + val about: String? = "", + + @SerialName("favorites") + val favorites: Int? = 0, + + @SerialName("images") + val images: Images? = Images(), + + @SerialName("name") + val name: String? = "", + + @SerialName("name_kanji") + val nameKanji: String? = "", + + @SerialName("nicknames") + val nicknames: List? = emptyList(), + + @SerialName("url") + val url: String? = "" +) { + companion object { + fun CharacterData.toCharacterTopEntity( + page: Int, + top: String + ) = CharacterTopEntity( + malId = malId, + image = images?.webp?.large.orEmpty(), + name = name, + nameKanji = nameKanji, + top = top, + page = page + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/models/jikan/character/CharacterSearch.kt b/src/main/kotlin/com/jeluchu/core/models/jikan/character/CharacterSearch.kt new file mode 100644 index 0000000..b25a0d4 --- /dev/null +++ b/src/main/kotlin/com/jeluchu/core/models/jikan/character/CharacterSearch.kt @@ -0,0 +1,15 @@ +package com.jeluchu.core.models.jikan.character + +import com.jeluchu.core.models.jikan.search.Pagination +import fordelete.CharacterData +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CharacterSearch( + @SerialName("data") + val data: List? = emptyList(), + + @SerialName("pagination") + val pagination: Pagination? = Pagination() +) \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/models/jikan/manga/MangaData.kt b/src/main/kotlin/com/jeluchu/core/models/jikan/manga/MangaData.kt new file mode 100644 index 0000000..57140a8 --- /dev/null +++ b/src/main/kotlin/com/jeluchu/core/models/jikan/manga/MangaData.kt @@ -0,0 +1,126 @@ +package com.jeluchu.core.models.jikan.manga + +import com.jeluchu.core.models.jikan.anime.* +import com.jeluchu.features.rankings.models.MangaTopEntity +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class MangaData( + @SerialName("approved") + val approved: Boolean? = false, + + @SerialName("authors") + val authors: List? = emptyList(), + + @SerialName("background") + val background: String? = "", + + @SerialName("chapters") + val chapters: Int? = 0, + + @SerialName("demographics") + val demographics: List? = emptyList(), + + @SerialName("explicit_genres") + val explicitGenres: List? = emptyList(), + + @SerialName("favorites") + val favorites: Int? = 0, + + @SerialName("genres") + val genres: List? = emptyList(), + + @SerialName("images") + val images: Images? = Images(), + + @SerialName("mal_id") + val malId: Int? = 0, + + @SerialName("members") + val members: Int? = 0, + + @SerialName("popularity") + val popularity: Int? = 0, + + @SerialName("published") + val published: Published? = Published(), + + @SerialName("publishing") + val publishing: Boolean? = false, + + @SerialName("rank") + val rank: Int? = 0, + + @SerialName("score") + val score: Double? = 0.0, + + @SerialName("scored") + val scored: Double? = 0.0, + + @SerialName("scored_by") + val scoredBy: Int? = 0, + + @SerialName("serializations") + val serializations: List? = emptyList(), + + @SerialName("status") + val status: String? = "", + + @SerialName("synopsis") + val synopsis: String? = "", + + @SerialName("themes") + val themes: List? = emptyList(), + + @Deprecated("Use 'titles: List' to get the title") + @SerialName("title") + val title: String? = "", + + @Deprecated("Use 'titles: List<Title>' to get the title") + @SerialName("title_english") + val titleEnglish: String? = "", + + @Deprecated("Use 'titles: List<Title>' to get the title") + @SerialName("title_japanese") + val titleJapanese: String? = "", + + @Deprecated("Use 'titles: List<Title>' to get the title") + @SerialName("title_synonyms") + val titleSynonyms: List<String>? = emptyList(), + + @SerialName("titles") + val titles: List<Title>? = emptyList(), + + @SerialName("type") + val type: String? = "", + + @SerialName("url") + val url: String? = "", + + @SerialName("volumes") + val volumes: Int? = 0 +) { + companion object { + fun MangaData.toMangaTopEntity( + page: Int, + top: String, + type: String, + subType: String, + ) = MangaTopEntity( + malId = malId, + rank = rank, + score = score, + image = images?.webp?.large.orEmpty(), + title = titles?.first()?.title.orEmpty(), + url = url, + status = status, + volumes = volumes, + chapters = chapters, + top = top, + type = type, + subtype = subType, + page = page + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/models/jikan/manga/Published.kt b/src/main/kotlin/com/jeluchu/core/models/jikan/manga/Published.kt new file mode 100644 index 0000000..0576f52 --- /dev/null +++ b/src/main/kotlin/com/jeluchu/core/models/jikan/manga/Published.kt @@ -0,0 +1,20 @@ +package com.jeluchu.core.models.jikan.manga + +import com.jeluchu.core.models.jikan.anime.Prop +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Published( + @SerialName("from") + val from: String? = "", + + @SerialName("prop") + val prop: Prop? = Prop(), + + @SerialName("string") + val string: String? = "", + + @SerialName("to") + val to: String? = "" +) \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/models/jikan/people/PeopleData.kt b/src/main/kotlin/com/jeluchu/core/models/jikan/people/PeopleData.kt new file mode 100644 index 0000000..bf9e7b6 --- /dev/null +++ b/src/main/kotlin/com/jeluchu/core/models/jikan/people/PeopleData.kt @@ -0,0 +1,58 @@ +package com.jeluchu.core.models.jikan.people + +import com.jeluchu.core.models.jikan.anime.Images +import com.jeluchu.features.rankings.models.PeopleTopEntity +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PeopleData( + @SerialName("mal_id") + val malId: Int? = 0, + + @SerialName("about") + val about: String? = "", + + @SerialName("alternate_names") + val alternateNames: List<String>? = emptyList(), + + @SerialName("birthday") + val birthday: String? = "", + + @SerialName("family_name") + val familyName: String? = "", + + @SerialName("favorites") + val favorites: Int? = 0, + + @SerialName("given_name") + val givenName: String? = "", + + @SerialName("images") + val images: Images? = Images(), + + @SerialName("name") + val name: String? = "", + + @SerialName("url") + val url: String? = "", + + @SerialName("website_url") + val websiteUrl: String? = "" +) { + companion object { + fun PeopleData.toPeopleTopEntity( + page: Int, + top: String + ) = PeopleTopEntity( + malId = malId, + image = images?.webp?.large.orEmpty(), + name = name, + givenName = givenName, + familyName = familyName, + birthday = birthday, + top = top, + page = page + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/models/jikan/people/PeopleSearch.kt b/src/main/kotlin/com/jeluchu/core/models/jikan/people/PeopleSearch.kt new file mode 100644 index 0000000..05c8bcd --- /dev/null +++ b/src/main/kotlin/com/jeluchu/core/models/jikan/people/PeopleSearch.kt @@ -0,0 +1,14 @@ +package com.jeluchu.core.models.jikan.people + +import com.jeluchu.core.models.jikan.search.Pagination +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PeopleSearch( + @SerialName("data") + val data: List<PeopleData>? = emptyList(), + + @SerialName("pagination") + val pagination: Pagination? = Pagination() +) \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/models/jikan/search/Search.kt b/src/main/kotlin/com/jeluchu/core/models/jikan/search/AnimeSearch.kt similarity index 95% rename from src/main/kotlin/com/jeluchu/core/models/jikan/search/Search.kt rename to src/main/kotlin/com/jeluchu/core/models/jikan/search/AnimeSearch.kt index 1ab2620..0a69f38 100644 --- a/src/main/kotlin/com/jeluchu/core/models/jikan/search/Search.kt +++ b/src/main/kotlin/com/jeluchu/core/models/jikan/search/AnimeSearch.kt @@ -8,7 +8,7 @@ import kotlinx.serialization.Serializable * Search data class. */ @Serializable -data class Search( +data class AnimeSearch( /** * Pagination info for request */ diff --git a/src/main/kotlin/com/jeluchu/core/models/jikan/search/MangaSearch.kt b/src/main/kotlin/com/jeluchu/core/models/jikan/search/MangaSearch.kt new file mode 100644 index 0000000..c7b2486 --- /dev/null +++ b/src/main/kotlin/com/jeluchu/core/models/jikan/search/MangaSearch.kt @@ -0,0 +1,14 @@ +package com.jeluchu.core.models.jikan.search + +import com.jeluchu.core.models.jikan.manga.MangaData +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class MangaSearch( + @SerialName("data") + val data: List<MangaData>? = emptyList(), + + @SerialName("pagination") + val pagination: Pagination? = Pagination() +) \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/utils/Constants.kt b/src/main/kotlin/com/jeluchu/core/utils/Constants.kt index 7c6df1b..2b82b49 100644 --- a/src/main/kotlin/com/jeluchu/core/utils/Constants.kt +++ b/src/main/kotlin/com/jeluchu/core/utils/Constants.kt @@ -7,20 +7,24 @@ object BaseUrls { object Endpoints { const val SCHEDULES = "schedules" const val TOP_ANIME = "top/anime" + const val TOP_MANGA = "top/manga" + const val TOP_PEOPLE = "top/people" + const val TOP_CHARACTER = "top/characters" } - object Routes { const val TOP = "/top" const val ANIME = "/anime" + const val MANGA = "/manga" + const val PAGE = "/{page}" + const val PEOPLE = "/people" const val SCHEDULE = "/schedule" const val DIRECTORY = "/directory" - const val TOP_MANGA = "/top/manga" - const val TOP_PEOPLE = "/top/people" + const val CHARACTER = "/characters" const val ANIME_DETAILS = "/anime/{id}" - const val SCHEDULE_DAY = "/schedule/{day}" + const val DAY = "/{day}" const val TOP_CHARACTER = "/top/character" - const val TOP_ANIME = "/{type}/{filter}/{page}" + const val RANKINGS = "/{type}/{filter}/{page}" } object TimerKey { @@ -28,4 +32,14 @@ object TimerKey { const val RANKING = "ranking" const val SCHEDULE = "schedule" const val LAST_UPDATED = "lastUpdated" +} + +object Collections { + const val TIMERS = "timers" + const val SCHEDULES = "schedule" + const val ANIME_DETAILS = "anime_details" + const val ANIME_RANKING = "anime_ranking" + const val MANGA_RANKING = "manga_ranking" + const val PEOPLE_RANKING = "people_ranking" + const val CHARACTER_RANKING = "character_ranking" } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/core/utils/ParseDocuments.kt b/src/main/kotlin/com/jeluchu/core/utils/ParseDocuments.kt index 514a9b5..5f630dc 100644 --- a/src/main/kotlin/com/jeluchu/core/utils/ParseDocuments.kt +++ b/src/main/kotlin/com/jeluchu/core/utils/ParseDocuments.kt @@ -1,13 +1,14 @@ package com.jeluchu.core.utils -import com.jeluchu.features.rankings.models.TopEntity +import com.jeluchu.features.rankings.models.AnimeTopEntity import com.jeluchu.features.schedule.models.DayEntity import com.jeluchu.features.schedule.models.ScheduleData +import kotlinx.serialization.KSerializer import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import org.bson.Document -fun parseScheduleDataToDocuments(data: ScheduleData): List<Document> { +fun parseTopDataToDocuments(data: ScheduleData): List<Document> { val documents = mutableListOf<Document>() fun processDay(dayList: List<DayEntity>?) { dayList?.forEach { animeData -> @@ -28,7 +29,7 @@ fun parseScheduleDataToDocuments(data: ScheduleData): List<Document> { return documents } -fun parseScheduleDataToDocuments(data: List<TopEntity>?): List<Document> { +fun parseTopDataToDocuments(data: List<AnimeTopEntity>?): List<Document> { val documents = mutableListOf<Document>() data?.forEach { animeData -> val animeJsonString = Json.encodeToString(animeData) @@ -36,4 +37,14 @@ fun parseScheduleDataToDocuments(data: List<TopEntity>?): List<Document> { documents.add(document) } return documents +} + +fun <T> parseDataToDocuments(data: List<T>?, serializer: KSerializer<T>): List<Document> { + val documents = mutableListOf<Document>() + data?.forEach { item -> + val jsonString = Json.encodeToString(serializer, item) + val document = Document.parse(jsonString) + documents.add(document) + } + return documents } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/features/anime/mappers/AnimeMappers.kt b/src/main/kotlin/com/jeluchu/features/anime/mappers/AnimeMappers.kt index f86a6f7..98e802b 100644 --- a/src/main/kotlin/com/jeluchu/features/anime/mappers/AnimeMappers.kt +++ b/src/main/kotlin/com/jeluchu/features/anime/mappers/AnimeMappers.kt @@ -4,7 +4,7 @@ import com.example.models.* import com.jeluchu.core.extensions.* import com.jeluchu.features.anime.models.anime.Images import com.jeluchu.features.anime.models.directory.AnimeDirectoryEntity -import com.jeluchu.features.rankings.models.TopEntity +import com.jeluchu.features.rankings.models.AnimeTopEntity import com.jeluchu.features.schedule.models.DayEntity import org.bson.Document @@ -222,7 +222,7 @@ fun documentToScheduleDayEntity(doc: Document) = DayEntity( title = doc.getStringSafe("title") ) -fun documentToTopEntity(doc: Document) = TopEntity( +fun documentToTopEntity(doc: Document) = AnimeTopEntity( malId = doc.getIntSafe("malId"), rank = doc.getIntSafe("rank"), score = doc.getFloatSafe("score"), diff --git a/src/main/kotlin/com/jeluchu/features/anime/services/AnimeService.kt b/src/main/kotlin/com/jeluchu/features/anime/services/AnimeService.kt index 3d26e84..91532a1 100644 --- a/src/main/kotlin/com/jeluchu/features/anime/services/AnimeService.kt +++ b/src/main/kotlin/com/jeluchu/features/anime/services/AnimeService.kt @@ -2,6 +2,7 @@ package com.jeluchu.features.anime.services import com.jeluchu.core.messages.ErrorMessages import com.jeluchu.core.models.ErrorResponse +import com.jeluchu.core.utils.Collections import com.jeluchu.features.anime.mappers.documentToAnimeDirectoryEntity import com.jeluchu.features.anime.mappers.documentToMoreInfoEntity import com.mongodb.client.MongoDatabase @@ -15,7 +16,7 @@ import kotlinx.serialization.json.Json class AnimeService( database: MongoDatabase ) { - private val directoryCollection = database.getCollection("animedetails") + private val directoryCollection = database.getCollection(Collections.ANIME_DETAILS) suspend fun getDirectory(call: RoutingCall) = try { val elements = directoryCollection.find().toList() diff --git a/src/main/kotlin/com/jeluchu/features/rankings/models/TopEntity.kt b/src/main/kotlin/com/jeluchu/features/rankings/models/AnimeTopEntity.kt similarity index 90% rename from src/main/kotlin/com/jeluchu/features/rankings/models/TopEntity.kt rename to src/main/kotlin/com/jeluchu/features/rankings/models/AnimeTopEntity.kt index ad749f9..4b691af 100644 --- a/src/main/kotlin/com/jeluchu/features/rankings/models/TopEntity.kt +++ b/src/main/kotlin/com/jeluchu/features/rankings/models/AnimeTopEntity.kt @@ -4,7 +4,7 @@ import com.example.models.VideoPromo import kotlinx.serialization.Serializable @Serializable -data class TopEntity( +data class AnimeTopEntity( val malId: Int? = 0, val rank: Int? = 0, val score: Float? = 0f, @@ -15,6 +15,7 @@ data class TopEntity( val season: String? = "", val year: Int? = 0, val airing: Boolean? = false, + val top: String? = "", val type: String? = "", val subtype: String? = "", val page: Int? = 0 diff --git a/src/main/kotlin/com/jeluchu/features/rankings/models/CharacterTopEntity.kt b/src/main/kotlin/com/jeluchu/features/rankings/models/CharacterTopEntity.kt new file mode 100644 index 0000000..acf20fe --- /dev/null +++ b/src/main/kotlin/com/jeluchu/features/rankings/models/CharacterTopEntity.kt @@ -0,0 +1,13 @@ +package com.jeluchu.features.rankings.models + +import kotlinx.serialization.Serializable + +@Serializable +data class CharacterTopEntity( + val malId: Int? = 0, + val name: String? = "", + val nameKanji: String? = "", + val image: String? = "", + val top: String? = "", + val page: Int? = 0 +) \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/features/rankings/models/MangaTopEntity.kt b/src/main/kotlin/com/jeluchu/features/rankings/models/MangaTopEntity.kt new file mode 100644 index 0000000..ca1dc93 --- /dev/null +++ b/src/main/kotlin/com/jeluchu/features/rankings/models/MangaTopEntity.kt @@ -0,0 +1,20 @@ +package com.jeluchu.features.rankings.models + +import kotlinx.serialization.Serializable + +@Serializable +data class MangaTopEntity( + val malId: Int? = 0, + val rank: Int? = 0, + val score: Double? = 0.0, + val title: String? = "", + val image: String? = "", + val url: String? = "", + val volumes: Int? = 0, + val chapters: Int? = 0, + val status: String? = "", + val top: String? = "", + val type: String? = "", + val subtype: String? = "", + val page: Int? = 0 +) \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/features/rankings/models/PeopleTopEntity.kt b/src/main/kotlin/com/jeluchu/features/rankings/models/PeopleTopEntity.kt new file mode 100644 index 0000000..12543b2 --- /dev/null +++ b/src/main/kotlin/com/jeluchu/features/rankings/models/PeopleTopEntity.kt @@ -0,0 +1,15 @@ +package com.jeluchu.features.rankings.models + +import kotlinx.serialization.Serializable + +@Serializable +data class PeopleTopEntity( + val malId: Int? = 0, + val name: String? = "", + val givenName: String? = "", + val familyName: String? = "", + val image: String? = "", + val birthday: String? = "", + val top: String? = "", + val page: Int? = 0 +) \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/features/rankings/routes/RankingsRoutes.kt b/src/main/kotlin/com/jeluchu/features/rankings/routes/RankingsRoutes.kt index 6254e92..b6baea0 100644 --- a/src/main/kotlin/com/jeluchu/features/rankings/routes/RankingsRoutes.kt +++ b/src/main/kotlin/com/jeluchu/features/rankings/routes/RankingsRoutes.kt @@ -9,12 +9,17 @@ import io.ktor.server.routing.* fun Route.rankingsEndpoints( mongoDatabase: MongoDatabase, service: RankingsService = RankingsService(mongoDatabase) -) { - route(Routes.TOP) { - route(Routes.ANIME) { - getToJson(Routes.TOP_ANIME) { service.getAnimeRanking(call) } - } +) = route(Routes.TOP) { + route(Routes.ANIME) { + getToJson(Routes.RANKINGS) { service.getAnimeRanking(call) } + } + route(Routes.MANGA) { + getToJson(Routes.RANKINGS) { service.getMangaRanking(call) } + } + route(Routes.PEOPLE) { + getToJson(Routes.PAGE) { service.getPeopleRanking(call) } + } + route(Routes.CHARACTER) { + getToJson(Routes.PAGE) { service.getCharacterRanking(call) } } - - getToJson(Routes.TOP_MANGA) { } } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/features/rankings/services/RankingsService.kt b/src/main/kotlin/com/jeluchu/features/rankings/services/RankingsService.kt index db2f2bc..fc913ab 100644 --- a/src/main/kotlin/com/jeluchu/features/rankings/services/RankingsService.kt +++ b/src/main/kotlin/com/jeluchu/features/rankings/services/RankingsService.kt @@ -6,44 +6,63 @@ import com.jeluchu.core.extensions.needsUpdate import com.jeluchu.core.extensions.update import com.jeluchu.core.messages.ErrorMessages import com.jeluchu.core.models.ErrorResponse -import com.jeluchu.core.models.jikan.anime.AnimeData.Companion.toTopEntity -import com.jeluchu.core.models.jikan.search.Search -import com.jeluchu.core.utils.* -import com.jeluchu.features.anime.mappers.documentToScheduleDayEntity +import com.jeluchu.core.models.jikan.anime.AnimeData.Companion.toAnimeTopEntity +import com.jeluchu.core.models.jikan.character.CharacterSearch +import com.jeluchu.core.models.jikan.manga.MangaData.Companion.toMangaTopEntity +import com.jeluchu.core.models.jikan.people.PeopleData.Companion.toPeopleTopEntity +import com.jeluchu.core.models.jikan.people.PeopleSearch +import com.jeluchu.core.models.jikan.search.AnimeSearch +import com.jeluchu.core.models.jikan.search.MangaSearch +import com.jeluchu.core.utils.BaseUrls +import com.jeluchu.core.utils.Collections +import com.jeluchu.core.utils.Endpoints +import com.jeluchu.core.utils.parseDataToDocuments import com.jeluchu.features.anime.mappers.documentToTopEntity -import com.jeluchu.features.schedule.models.ScheduleEntity +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.mongodb.client.MongoDatabase import com.mongodb.client.model.Filters +import fordelete.CharacterData.Companion.toCharacterTopEntity import io.ktor.http.* import io.ktor.server.response.* import io.ktor.server.routing.* import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import org.bson.Document class RankingsService( database: MongoDatabase ) { - private val timers = database.getCollection("timers") - private val ranking = database.getCollection("ranking") + private val timers = database.getCollection(Collections.TIMERS) + private val animeRanking = database.getCollection(Collections.ANIME_RANKING) + private val mangaRanking = database.getCollection(Collections.MANGA_RANKING) + private val peopleRanking = database.getCollection(Collections.PEOPLE_RANKING) + private val characterRanking = database.getCollection(Collections.CHARACTER_RANKING) suspend fun getAnimeRanking(call: RoutingCall) { - val paramType = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) - val paramFilter = call.parameters["filter"] ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) + val paramType = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopAnimeType.message) val paramPage = call.parameters["page"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) - if (parseType(paramType) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidDay.message)) - if (parseFilterType(paramFilter) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidDay.message)) + val paramFilter = call.parameters["filter"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopAnimeFilterType.message) + if (parseAnimeType(paramType) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopAnimeType.message)) + if (parseAnimeFilterType(paramFilter) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopAnimeFilterType.message)) - val timerKey = "${TimerKey.RANKING}_${paramType}_${paramFilter}_${paramPage}" + val timerKey = "${Collections.ANIME_RANKING}_${paramType}_${paramFilter}_${paramPage}" val needsUpdate = timers.needsUpdate( - amount = 1, + amount = 30, key = timerKey, - unit = TimeUnit.MINUTE + unit = TimeUnit.DAY ) if (needsUpdate) { - ranking.deleteMany(Document()) + animeRanking.deleteMany( + Filters.and( + Filters.eq("page", paramPage), + Filters.eq("type", paramType), + Filters.eq("subtype", paramFilter) + ) + ) val params = mutableListOf<String>() params.add("type=$paramType") @@ -52,27 +71,145 @@ class RankingsService( val response = RestClient.request( BaseUrls.JIKAN + Endpoints.TOP_ANIME + "?${params.joinToString("&")}", - Search.serializer() - ).data?.mapIndexed { index, anime -> - anime.toTopEntity( + AnimeSearch.serializer() + ).data?.map { anime -> + anime.toAnimeTopEntity( + top = "anime", page = paramPage, - rank = index + 1, type = paramType, subType = paramFilter ) } - val documentsToInsert = parseScheduleDataToDocuments(response) - if (documentsToInsert.isNotEmpty()) ranking.insertMany(documentsToInsert) + val documentsToInsert = parseDataToDocuments(response, AnimeTopEntity.serializer()) + if (documentsToInsert.isNotEmpty()) animeRanking.insertMany(documentsToInsert) timers.update(timerKey) call.respond(HttpStatusCode.OK, Json.encodeToString(response)) } else { - val elements = ranking.find().toList() + val elements = animeRanking.find().toList() val directory = elements.map { documentToTopEntity(it) } val json = Json.encodeToString(directory) call.respond(HttpStatusCode.OK, json) } } -} + suspend fun getMangaRanking(call: RoutingCall) { + val paramType = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopMangaType.message) + val paramPage = call.parameters["page"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) + val paramFilter = call.parameters["filter"] ?: throw IllegalArgumentException(ErrorMessages.InvalidTopMangaFilterType.message) + if (parseMangaType(paramType) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopMangaType.message)) + if (parseMangaFilterType(paramFilter) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidTopMangaFilterType.message)) + + val timerKey = "${Collections.MANGA_RANKING}_${paramType}_${paramFilter}_${paramPage}" + val needsUpdate = timers.needsUpdate( + amount = 30, + key = timerKey, + unit = TimeUnit.DAY + ) + + if (needsUpdate) { + mangaRanking.deleteMany( + Filters.and( + Filters.eq("page", paramPage), + Filters.eq("type", paramType), + Filters.eq("subtype", paramFilter) + ) + ) + + val params = mutableListOf<String>() + params.add("type=$paramType") + params.add("page=$paramPage") + params.add("filter=$paramFilter") + + val response = RestClient.request( + BaseUrls.JIKAN + Endpoints.TOP_MANGA + "?${params.joinToString("&")}", + MangaSearch.serializer() + ).data?.map { anime -> + anime.toMangaTopEntity( + top = "manga", + page = paramPage, + type = paramType, + subType = paramFilter + ) + } + + val documentsToInsert = parseDataToDocuments(response, MangaTopEntity.serializer()) + if (documentsToInsert.isNotEmpty()) mangaRanking.insertMany(documentsToInsert) + timers.update(timerKey) + + call.respond(HttpStatusCode.OK, Json.encodeToString(response)) + } else { + val elements = mangaRanking.find().toList() + val directory = elements.map { documentToTopEntity(it) } + val json = Json.encodeToString(directory) + call.respond(HttpStatusCode.OK, json) + } + } + + suspend fun getPeopleRanking(call: RoutingCall) { + val paramPage = call.parameters["page"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) + val timerKey = "${Collections.PEOPLE_RANKING}_${paramPage}" + val needsUpdate = timers.needsUpdate( + amount = 30, + key = timerKey, + unit = TimeUnit.DAY + ) + + if (needsUpdate) { peopleRanking.deleteMany(Filters.and(Filters.eq("page", paramPage))) + val response = RestClient.request( + BaseUrls.JIKAN + Endpoints.TOP_PEOPLE + "?page=$paramPage", + PeopleSearch.serializer() + ).data?.map { anime -> + anime.toPeopleTopEntity( + top = "people", + page = paramPage + ) + } + + val documentsToInsert = parseDataToDocuments(response, PeopleTopEntity.serializer()) + if (documentsToInsert.isNotEmpty()) peopleRanking.insertMany(documentsToInsert) + timers.update(timerKey) + + call.respond(HttpStatusCode.OK, Json.encodeToString(response)) + } else { + val elements = peopleRanking.find().toList() + val directory = elements.map { documentToTopEntity(it) } + val json = Json.encodeToString(directory) + call.respond(HttpStatusCode.OK, json) + } + } + + suspend fun getCharacterRanking(call: RoutingCall) { + val paramPage = call.parameters["page"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) + val timerKey = "${Collections.CHARACTER_RANKING}_${paramPage}" + val needsUpdate = timers.needsUpdate( + amount = 30, + key = timerKey, + unit = TimeUnit.DAY + ) + + if (needsUpdate) { characterRanking.deleteMany(Filters.and(Filters.eq("page", paramPage))) + val response = RestClient.request( + BaseUrls.JIKAN + Endpoints.TOP_CHARACTER + "?page=$paramPage", + CharacterSearch.serializer() + ).data?.map { anime -> + anime.toCharacterTopEntity( + top = "character", + page = paramPage + ) + } + + val documentsToInsert = parseDataToDocuments(response, CharacterTopEntity.serializer()) + if (documentsToInsert.isNotEmpty()) characterRanking.insertMany(documentsToInsert) + timers.update(timerKey) + + call.respond(HttpStatusCode.OK, Json.encodeToString(response)) + } else { + val elements = characterRanking.find().toList() + val directory = elements.map { documentToTopEntity(it) } + val json = Json.encodeToString(directory) + call.respond(HttpStatusCode.OK, json) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/features/schedule/routes/ScheduleRoutes.kt b/src/main/kotlin/com/jeluchu/features/schedule/routes/ScheduleRoutes.kt index 74fc22e..35f434e 100644 --- a/src/main/kotlin/com/jeluchu/features/schedule/routes/ScheduleRoutes.kt +++ b/src/main/kotlin/com/jeluchu/features/schedule/routes/ScheduleRoutes.kt @@ -9,7 +9,7 @@ import io.ktor.server.routing.* fun Route.scheduleEndpoints( mongoDatabase: MongoDatabase, service: ScheduleService = ScheduleService(mongoDatabase) -) { - getToJson(Routes.SCHEDULE) { service.getSchedule(call) } - getToJson(Routes.SCHEDULE_DAY) { service.getScheduleByDay(call) } +) = route(Routes.SCHEDULE) { + getToJson { service.getSchedule(call) } + getToJson(Routes.DAY) { service.getScheduleByDay(call) } } \ No newline at end of file diff --git a/src/main/kotlin/com/jeluchu/features/schedule/services/ScheduleService.kt b/src/main/kotlin/com/jeluchu/features/schedule/services/ScheduleService.kt index da3c66f..90f9f2d 100644 --- a/src/main/kotlin/com/jeluchu/features/schedule/services/ScheduleService.kt +++ b/src/main/kotlin/com/jeluchu/features/schedule/services/ScheduleService.kt @@ -25,8 +25,8 @@ import org.bson.Document class ScheduleService( database: MongoDatabase ) { - private val timers = database.getCollection("timers") - private val schedules = database.getCollection("schedule") + private val timers = database.getCollection(Collections.TIMERS) + private val schedules = database.getCollection(Collections.SCHEDULES) suspend fun getSchedule(call: RoutingCall) { val needsUpdate = timers.needsUpdate( @@ -48,7 +48,7 @@ class ScheduleService( wednesday = getSchedule(Day.WEDNESDAY).data?.map { it.toDayEntity(Day.WEDNESDAY) }.orEmpty() ) - val documentsToInsert = parseScheduleDataToDocuments(response) + val documentsToInsert = parseTopDataToDocuments(response) if (documentsToInsert.isNotEmpty()) schedules.insertMany(documentsToInsert) timers.update(TimerKey.SCHEDULE)