Improve directory response, reduce verbose log and refactoring

v5 v5.0.1
Jéluchu 9 months ago
parent d41154274d
commit 1976c82603

@ -0,0 +1,14 @@
package com.jeluchu.core.extensions
import io.ktor.http.*
import io.ktor.server.routing.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
fun Route.getToJson(
path: String,
request: suspend RoutingContext.() -> Unit
): Route = get(path) {
call.response.headers.append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
withContext(Dispatchers.IO) { request() }
}

@ -0,0 +1,6 @@
package com.jeluchu.core.utils
object Routes {
const val DIRECTORY = "/directory"
const val ANIME_DETAILS = "/anime/{id}"
}

@ -2,9 +2,25 @@ package com.jeluchu.features.anime.mappers
import com.example.models.* import com.example.models.*
import com.jeluchu.core.extensions.* import com.jeluchu.core.extensions.*
import kotlinx.serialization.json.Json import com.jeluchu.features.anime.models.directory.AnimeDirectoryEntity
import org.bson.Document import org.bson.Document
fun documentToAnimeDirectoryEntity(doc: Document) = AnimeDirectoryEntity(
rank = doc.getIntSafe("rank"),
year = doc.getIntSafe("year"),
url = doc.getStringSafe("url"),
malId = doc.getIntSafe("malId"),
type = doc.getStringSafe("type"),
score = doc.getStringSafe("score"),
title = doc.getStringSafe("title"),
status = doc.getStringSafe("status"),
season = doc.getStringSafe("season"),
poster = doc.getStringSafe("poster"),
airing = doc.getBooleanSafe("airing"),
genres = doc.getListSafe<String>("genres"),
episodesCount = doc.getIntSafe("episodesCount")
)
fun documentToMoreInfoEntity(doc: Document): MoreInfoEntity { fun documentToMoreInfoEntity(doc: Document): MoreInfoEntity {
return MoreInfoEntity( return MoreInfoEntity(
id = doc.getLongSafe("id"), id = doc.getLongSafe("id"),
@ -14,138 +30,138 @@ fun documentToMoreInfoEntity(doc: Document): MoreInfoEntity {
cover = doc.getStringSafe("cover"), cover = doc.getStringSafe("cover"),
genres = doc.getListSafe<String>("genres"), genres = doc.getListSafe<String>("genres"),
synopsis = doc.getStringSafe("synopsis"), synopsis = doc.getStringSafe("synopsis"),
episodes = doc.getListSafe<Document>("episodes").map { documentToMergedEpisode(it) } ?: emptyList(), episodes = doc.getListSafe<Document>("episodes").map { documentToMergedEpisode(it) },
episodesCount = doc.getIntSafe("episodesCount", 0) ?: 0, episodesCount = doc.getIntSafe("episodesCount", 0),
score = doc.getStringSafe("score") ?: "", score = doc.getStringSafe("score"),
staff = doc.getListSafe<Document>("staff").map { documentToStaff(it) } ?: emptyList(), staff = doc.getListSafe<Document>("staff").map { documentToStaff(it) },
characters = doc.getListSafe<Document>("characters").map { documentToCharacter(it) } ?: emptyList(), characters = doc.getListSafe<Document>("characters").map { documentToCharacter(it) },
status = doc.getStringSafe("status") ?: "", status = doc.getStringSafe("status"),
type = doc.getStringSafe("type") ?: "", type = doc.getStringSafe("type"),
url = doc.getStringSafe("url") ?: "", url = doc.getStringSafe("url"),
promo = doc.getDocumentSafe("promo")?.let { documentToVideoPromo(it) } ?: VideoPromo(), promo = doc.getDocumentSafe("promo")?.let { documentToVideoPromo(it) } ?: VideoPromo(),
source = doc.getStringSafe("source") ?: "", source = doc.getStringSafe("source"),
duration = doc.getStringSafe("duration") ?: "", duration = doc.getStringSafe("duration"),
rank = doc.getIntSafe("rank", 0) ?: 0, rank = doc.getIntSafe("rank", 0),
titles = doc.getListSafe<Document>("titles").map { documentToAlternativeTitles(it) } ?: emptyList(), titles = doc.getListSafe<Document>("titles").map { documentToAlternativeTitles(it) },
airing = doc.getBooleanSafe("airing") ?: false, airing = doc.getBooleanSafe("airing"),
aired = doc.getDocumentSafe("aired")?.let { documentToAiringTime(it) } ?: AiringTime(), aired = doc.getDocumentSafe("aired")?.let { documentToAiringTime(it) } ?: AiringTime(),
broadcast = doc.getDocumentSafe("broadcast")?.let { documentToAnimeBroadcast(it) } ?: AnimeBroadcast(), broadcast = doc.getDocumentSafe("broadcast")?.let { documentToAnimeBroadcast(it) } ?: AnimeBroadcast(),
season = doc.getStringSafe("season") ?: "", season = doc.getStringSafe("season"),
year = doc.getIntSafe("year", 0), year = doc.getIntSafe("year", 0),
external = doc.getListSafe<Document>("external").map { documentToExternalLinks(it) } ?: emptyList(), external = doc.getListSafe<Document>("external").map { documentToExternalLinks(it) },
streaming = doc.getListSafe<Document>("streaming").map { documentToExternalLinks(it) } ?: emptyList(), streaming = doc.getListSafe<Document>("streaming").map { documentToExternalLinks(it) },
studios = doc.getListSafe<Document>("studios").map { documentToCompanies(it) } ?: emptyList(), studios = doc.getListSafe<Document>("studios").map { documentToCompanies(it) },
licensors = doc.getListSafe<Document>("licensors").map { documentToCompanies(it) } ?: emptyList(), licensors = doc.getListSafe<Document>("licensors").map { documentToCompanies(it) },
producers = doc.getListSafe<Document>("producers").map { documentToCompanies(it) } ?: emptyList(), producers = doc.getListSafe<Document>("producers").map { documentToCompanies(it) },
theme = doc.getDocumentSafe("theme")?.let { documentToThemes(it) } ?: Themes(), theme = doc.getDocumentSafe("theme")?.let { documentToThemes(it) } ?: Themes(),
relations = doc.getListSafe<Document>("relations").map { documentToRelated(it) } ?: emptyList(), relations = doc.getListSafe<Document>("relations").map { documentToRelated(it) },
stats = doc.getDocumentSafe("stats")?.let { documentToStatistics(it) } ?: Statistics(), stats = doc.getDocumentSafe("stats")?.let { documentToStatistics(it) } ?: Statistics(),
gallery = doc.getListSafe<Document>("gallery").map { documentToImageMediaEntity(it) } ?: emptyList(), gallery = doc.getListSafe<Document>("gallery").map { documentToImageMediaEntity(it) },
episodeSource = doc.getStringSafe("episodeSource") ?: "" episodeSource = doc.getStringSafe("episodeSource")
) )
} }
fun documentToActor(doc: Document): Actor { fun documentToActor(doc: Document): Actor {
return Actor( return Actor(
person = doc.getDocumentSafe("person")?.let { documentToIndividual(it) } ?: Individual(), person = doc.getDocumentSafe("person")?.let { documentToIndividual(it) } ?: Individual(),
language = doc.getStringSafe("language") ?: "" language = doc.getStringSafe("language")
) )
} }
fun documentToAiringTime(doc: Document): AiringTime { fun documentToAiringTime(doc: Document): AiringTime {
return AiringTime( return AiringTime(
from = doc.getStringSafe("from") ?: "", from = doc.getStringSafe("from"),
to = doc.getStringSafe("to") ?: "" to = doc.getStringSafe("to")
) )
} }
fun documentToAlternativeTitles(doc: Document): AlternativeTitles { fun documentToAlternativeTitles(doc: Document): AlternativeTitles {
return AlternativeTitles( return AlternativeTitles(
title = doc.getStringSafe("title") ?: "", title = doc.getStringSafe("title"),
type = doc.getStringSafe("type") ?: "" type = doc.getStringSafe("type")
) )
} }
fun documentToAnimeBroadcast(doc: Document): AnimeBroadcast { fun documentToAnimeBroadcast(doc: Document): AnimeBroadcast {
return AnimeBroadcast( return AnimeBroadcast(
day = doc.getStringSafe("day") ?: "", day = doc.getStringSafe("day"),
time = doc.getStringSafe("time") ?: "", time = doc.getStringSafe("time"),
timezone = doc.getStringSafe("timezone") ?: "" timezone = doc.getStringSafe("timezone")
) )
} }
fun documentToAnimeSource(doc: Document): AnimeSource { fun documentToAnimeSource(doc: Document): AnimeSource {
return AnimeSource( return AnimeSource(
id = doc.getStringSafe("id") ?: "", id = doc.getStringSafe("id"),
source = doc.getStringSafe("source") ?: "" source = doc.getStringSafe("source")
) )
} }
fun documentToCharacter(doc: Document): Character { fun documentToCharacter(doc: Document): Character {
return Character( return Character(
character = doc.getDocumentSafe("character")?.let { documentToIndividual(it) } ?: Individual(), character = doc.getDocumentSafe("character")?.let { documentToIndividual(it) } ?: Individual(),
role = doc.getStringSafe("role") ?: "", role = doc.getStringSafe("role"),
voiceActor = doc.getListSafe<Document>("voiceActor").map { documentToActor(it) } ?: emptyList() voiceActor = doc.getListSafe<Document>("voiceActor").map { documentToActor(it) }
) )
} }
fun documentToCompanies(doc: Document): Companies { fun documentToCompanies(doc: Document): Companies {
return Companies( return Companies(
malId = doc.getIntSafe("malId", 0), malId = doc.getIntSafe("malId", 0),
name = doc.getStringSafe("name") ?: "", name = doc.getStringSafe("name"),
type = doc.getStringSafe("type") ?: "", type = doc.getStringSafe("type"),
url = doc.getStringSafe("url") ?: "" url = doc.getStringSafe("url")
) )
} }
fun documentToExternalLinks(doc: Document): ExternalLinks { fun documentToExternalLinks(doc: Document): ExternalLinks {
return ExternalLinks( return ExternalLinks(
url = doc.getStringSafe("url") ?: "", url = doc.getStringSafe("url"),
name = doc.getStringSafe("name") ?: "" name = doc.getStringSafe("name")
) )
} }
fun documentToImageMediaEntity(doc: Document): ImageMediaEntity { fun documentToImageMediaEntity(doc: Document): ImageMediaEntity {
return ImageMediaEntity( return ImageMediaEntity(
media = doc.getStringSafe("media") ?: "", media = doc.getStringSafe("media"),
thumbnail = doc.getStringSafe("thumbnail") ?: "", thumbnail = doc.getStringSafe("thumbnail"),
width = doc.getIntSafe("width", 0), width = doc.getIntSafe("width", 0),
height = doc.getIntSafe("height", 0), height = doc.getIntSafe("height", 0),
url = doc.getStringSafe("url") ?: "" url = doc.getStringSafe("url")
) )
} }
fun documentToImages(doc: Document): Images { fun documentToImages(doc: Document): Images {
return Images( return Images(
generic = doc.getStringSafe("generic") ?: "", generic = doc.getStringSafe("generic"),
small = doc.getStringSafe("small") ?: "", small = doc.getStringSafe("small"),
medium = doc.getStringSafe("medium") ?: "", medium = doc.getStringSafe("medium"),
large = doc.getStringSafe("large") ?: "", large = doc.getStringSafe("large"),
maximum = doc.getStringSafe("maximum") ?: "" maximum = doc.getStringSafe("maximum")
) )
} }
fun documentToIndividual(doc: Document): Individual { fun documentToIndividual(doc: Document): Individual {
return Individual( return Individual(
malId = doc.getIntSafe("malId", 0), malId = doc.getIntSafe("malId", 0),
url = doc.getStringSafe("url") ?: "", url = doc.getStringSafe("url"),
name = doc.getStringSafe("name") ?: "", name = doc.getStringSafe("name"),
images = doc.getStringSafe("images") ?: "" images = doc.getStringSafe("images")
) )
} }
fun documentToMergedEpisode(doc: Document): MergedEpisode { fun documentToMergedEpisode(doc: Document): MergedEpisode {
return MergedEpisode( return MergedEpisode(
number = doc.getIntSafe("number", 0), number = doc.getIntSafe("number", 0),
ids = doc.getListSafe<Document>("ids").map { documentToAnimeSource(it) }.toMutableList() ?: mutableListOf(), ids = doc.getListSafe<Document>("ids").map { documentToAnimeSource(it) }.toMutableList(),
nextEpisodeDate = doc.getStringSafe("nextEpisodeDate") ?: "" nextEpisodeDate = doc.getStringSafe("nextEpisodeDate")
) )
} }
fun documentToRelated(doc: Document): Related { fun documentToRelated(doc: Document): Related {
return Related( return Related(
entry = doc.getListSafe<Document>("entry").map { documentToCompanies(it) } ?: emptyList(), entry = doc.getListSafe<Document>("entry").map { documentToCompanies(it) },
relation = doc.getStringSafe("relation") ?: "" relation = doc.getStringSafe("relation")
) )
} }
@ -164,19 +180,19 @@ fun documentToScore(doc: Document): Score {
fun documentToStaff(doc: Document): Staff { fun documentToStaff(doc: Document): Staff {
return Staff( return Staff(
person = doc.get("person", Document::class.java)?.let { documentToIndividual(it) } ?: Individual(), person = doc.get("person", Document::class.java)?.let { documentToIndividual(it) } ?: Individual(),
positions = doc.getListSafe<String>("positions") ?: emptyList() positions = doc.getListSafe<String>("positions")
) )
} }
fun documentToStatistics(doc: Document): Statistics { fun documentToStatistics(doc: Document): Statistics {
return Statistics( return Statistics(
completed = doc.getIntSafe("completed") ?: 0, completed = doc.getIntSafe("completed"),
dropped = doc.getIntSafe("dropped") ?: 0, dropped = doc.getIntSafe("dropped"),
onHold = doc.getIntSafe("onHold") ?: 0, onHold = doc.getIntSafe("onHold"),
planToWatch = doc.getIntSafe("planToWatch") ?: 0, planToWatch = doc.getIntSafe("planToWatch"),
scores = doc.getListSafe<Document>("scores").map { documentToScore(it) } ?: emptyList(), scores = doc.getListSafe<Document>("scores").map { documentToScore(it) },
total = doc.getIntSafe("total") ?: 0, total = doc.getIntSafe("total"),
watching = doc.getIntSafe("watching") ?: 0 watching = doc.getIntSafe("watching")
) )
} }
@ -189,17 +205,9 @@ fun documentToThemes(doc: Document): Themes {
fun documentToVideoPromo(doc: Document): VideoPromo { fun documentToVideoPromo(doc: Document): VideoPromo {
return VideoPromo( return VideoPromo(
embedUrl = doc.getStringSafe("embedUrl") ?: "", embedUrl = doc.getStringSafe("embedUrl"),
url = doc.getStringSafe("url") ?: "", url = doc.getStringSafe("url"),
youtubeId = doc.getStringSafe("youtubeId") ?: "", youtubeId = doc.getStringSafe("youtubeId"),
images = doc.get("images", Document::class.java)?.let { documentToImages(it) } ?: Images() images = doc.get("images", Document::class.java)?.let { documentToImages(it) } ?: Images()
) )
} }
fun List<Document>.toMoreInfoEntity(): List<MoreInfoEntity> {
val json = Json { ignoreUnknownKeys = true }
val jsonStrings = map { it.toJson() }
return jsonStrings.map { json.decodeFromString<MoreInfoEntity>(it) }
}

@ -0,0 +1,20 @@
package com.jeluchu.features.anime.models.directory
import kotlinx.serialization.Serializable
@Serializable
data class AnimeDirectoryEntity(
val rank: Int = 0,
val year: Int = 0,
var malId: Int = 0,
val url: String = "",
var type: String = "",
var score: String = "",
var title: String = "",
var status: String = "",
val season: String = "",
var poster: String = "",
var episodesCount: Int = 0,
val airing: Boolean = false,
var genres: List<String> = emptyList()
)

@ -1,5 +1,7 @@
package com.jeluchu.features.anime.routes package com.jeluchu.features.anime.routes
import com.jeluchu.core.extensions.getToJson
import com.jeluchu.core.utils.Routes
import com.jeluchu.features.anime.services.AnimeService import com.jeluchu.features.anime.services.AnimeService
import com.mongodb.client.MongoDatabase import com.mongodb.client.MongoDatabase
import io.ktor.server.routing.* import io.ktor.server.routing.*
@ -8,6 +10,6 @@ fun Route.animeEndpoints(
mongoDatabase: MongoDatabase, mongoDatabase: MongoDatabase,
service: AnimeService = AnimeService(mongoDatabase) service: AnimeService = AnimeService(mongoDatabase)
) { ) {
get("/directory") { service.getDirectory(call) } getToJson(Routes.DIRECTORY) { service.getDirectory(call) }
get("/anime/{id}") { service.getAnimeByMalId(call) } getToJson(Routes.ANIME_DETAILS) { service.getAnimeByMalId(call) }
} }

@ -2,15 +2,13 @@ package com.jeluchu.features.anime.services
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.features.anime.mappers.documentToAnimeDirectoryEntity
import com.jeluchu.features.anime.mappers.documentToMoreInfoEntity import com.jeluchu.features.anime.mappers.documentToMoreInfoEntity
import com.jeluchu.features.anime.mappers.toMoreInfoEntity
import com.mongodb.client.MongoDatabase import com.mongodb.client.MongoDatabase
import com.mongodb.client.model.Filters import com.mongodb.client.model.Filters
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -19,27 +17,16 @@ class AnimeService(
) { ) {
private val directoryCollection = database.getCollection("animedetails") private val directoryCollection = database.getCollection("animedetails")
suspend fun getDirectory( suspend fun getDirectory(call: RoutingCall) = try {
call: RoutingCall
) = withContext(Dispatchers.IO) {
call.response.headers.append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
try {
val elements = directoryCollection.find().toList() val elements = directoryCollection.find().toList()
val directory = elements.map { documentToMoreInfoEntity(it) } val directory = elements.map { documentToAnimeDirectoryEntity(it) }
val json = Json.encodeToString(directory) val json = Json.encodeToString(directory)
call.respond(HttpStatusCode.OK, json) call.respond(HttpStatusCode.OK, json)
} catch (ex: Exception) { } catch (ex: Exception) {
call.respond(HttpStatusCode.Unauthorized, ErrorResponse(ErrorMessages.UnauthorizedMongo.message)) call.respond(HttpStatusCode.Unauthorized, ErrorResponse(ErrorMessages.UnauthorizedMongo.message))
} }
}
suspend fun getAnimeByMalId( suspend fun getAnimeByMalId(call: RoutingCall) = try {
call: RoutingCall
) = withContext(Dispatchers.IO) {
call.response.headers.append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
try {
val id = call.parameters["id"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message) val id = call.parameters["id"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message)
directoryCollection.find(Filters.eq("malId", id)).firstOrNull()?.let { anime -> directoryCollection.find(Filters.eq("malId", id)).firstOrNull()?.let { anime ->
val info = documentToMoreInfoEntity(anime) val info = documentToMoreInfoEntity(anime)
@ -49,5 +36,4 @@ class AnimeService(
call.respond(HttpStatusCode.NotFound, ErrorResponse(ErrorMessages.InvalidInput.message)) call.respond(HttpStatusCode.NotFound, ErrorResponse(ErrorMessages.InvalidInput.message))
} }
} }
}

@ -0,0 +1,11 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date %-5level [%thread] %logger{0}: %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Loading…
Cancel
Save