Initial preview API for v5 with 2 enpoints

v5
Jéluchu 9 months ago
parent 2308dd49f5
commit ff7b55ce40

@ -0,0 +1,4 @@
build
.gradle
.git
*.md

48
.gitignore vendored

@ -1,2 +1,50 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
# System Files
.DS_Store .DS_Store
# Environment Variables
.env
# Docker
docker-compose.override.yml
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
gradle/
gradlew
gradlew.bat

@ -0,0 +1,29 @@
# Stage 1: Cache Gradle dependencies
FROM gradle:8-jdk21-corretto AS cache
RUN mkdir -p /home/gradle/cache_home
ENV GRADLE_USER_HOME /home/gradle/cache_home
COPY build.gradle.kts /home/gradle/src/build.gradle.kts
COPY settings.gradle.kts /home/gradle/src/settings.gradle.kts
COPY gradle/libs.versions.toml /home/gradle/src/gradle/libs.versions.toml
WORKDIR /home/gradle/src
RUN gradle clean build -i --stacktrace
# Stage 2: Build Application
FROM gradle:8-jdk21-corretto AS build
COPY --from=cache /home/gradle/cache_home /home/gradle/.gradle
COPY . /usr/src/app/
WORKDIR /usr/src/app
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
# Build the fat JAR, Gradle also supports shadow
# and boot JAR by default.
RUN gradle buildFatJar --no-daemon
# Stage 3: Create the Runtime Image
FROM amazoncorretto:21 AS runtime
EXPOSE 8080/tcp
RUN mkdir /app
COPY --from=build /home/gradle/src/build/libs/*.jar /app/app.jar
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]

@ -0,0 +1,33 @@
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.ktor)
alias(libs.plugins.kotlin.plugin.serialization)
}
group = "com.jeluchu"
version = "5.0.0"
application {
mainClass.set("io.ktor.server.netty.EngineMain")
val isDevelopment: Boolean = project.ext.has("development")
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
}
repositories {
mavenCentral()
}
dependencies {
implementation(libs.bson)
implementation(libs.logback.classic)
implementation(libs.ktor.server.core)
implementation(libs.ktor.server.netty)
implementation(libs.ktor.server.swagger)
implementation(libs.mongodb.driver.core)
implementation(libs.mongodb.driver.sync)
implementation(libs.ktor.server.config.yaml)
implementation(libs.ktor.server.status.pages)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ktor.server.content.negotiation)
}

@ -0,0 +1,18 @@
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: ktor_app
ports:
- "8080:8080"
env_file:
- .env
networks:
- app-network
networks:
app-network:
driver: bridge

@ -0,0 +1,34 @@
#!/bin/sh
# Check if MONGO_CONNECTION_STRING is defined
if [ -z "$MONGO_CONNECTION_STRING" ]; then
echo "ERROR: The environment variable for the database, MONGO_CONNECTION_STRING is not defined."
exit 1
fi
# Verificar si MONGO_DATABASE_NAME está definido
if [ -z "$MONGO_DATABASE_NAME" ]; then
echo "WARNING: The environment variable for the database, MONGO_DATABASE_NAME, is not defined. It is using mongodb as the default value."
MONGO_DATABASE_NAME="mongodb"
fi
# Generate the application.yaml file
cat <<EOF > /app/application.yaml
ktor:
application:
modules:
- com.jeluchu.ApplicationKt.module
deployment:
port: 8080
host: "0.0.0.0"
db:
mongo:
connectionStrings: "${MONGO_CONNECTION_STRING}"
database:
name: "${MONGO_DATABASE_NAME:-mongodb}"
EOF
# Run the application with the specified configuration
exec java -Dconfig.file=/app/application.yaml -jar /app/app.jar

@ -0,0 +1,2 @@
MONGO_CONNECTION_STRING=mongodb://user:password@host:port
MONGO_DATABASE_NAME=aruppi

@ -0,0 +1,3 @@
kotlin.code.style=official
org.gradle.caching=true
org.gradle.daemon=false

@ -0,0 +1 @@
rootProject.name = "aruppi-api"

@ -0,0 +1,15 @@
@file:Suppress("unused")
package com.jeluchu
import com.jeluchu.core.configuration.initInstallers
import com.jeluchu.core.configuration.initRoutes
import io.ktor.server.application.*
import io.ktor.server.netty.*
fun main(args: Array<String>) = EngineMain.main(args)
fun Application.module() {
initInstallers()
initRoutes()
}

@ -0,0 +1,24 @@
package com.jeluchu.core.configuration
import com.jeluchu.core.messages.ErrorMessages
import com.jeluchu.core.models.ErrorResponse
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.response.*
fun Application.initInstallers() {
install(StatusPages) {
status(HttpStatusCode.NotFound) { call, _ ->
call.respond(HttpStatusCode.NotFound,
ErrorResponse(ErrorMessages.NotFound.message)
)
}
}
install(ContentNegotiation) {
json()
}
}

@ -0,0 +1,33 @@
package com.jeluchu.core.configuration
import com.mongodb.client.MongoClients
import com.mongodb.client.MongoDatabase
import io.ktor.server.application.*
import io.ktor.server.config.*
/**
* Establishes connection with a MongoDB database.
*
* The following configuration properties (in application.yaml/application.conf) can be specified:
* * `db.mongo.connectionStrings` connectionStrings for your MongoDB
* * `db.mongo.database.name` name of the database
*
* IMPORTANT NOTE: in order to make MongoDB connection working, you have to start a MongoDB server first.
* See the instructions here: https://www.mongodb.com/docs/manual/administration/install-community/
* all the paramaters above
*
* @returns [MongoDatabase] instance
* */
fun Application.connectToMongoDB(): MongoDatabase {
val connectionStrings = environment.config.tryGetString("db.mongo.connectionStrings") ?: "mongodb://"
val databaseName = environment.config.tryGetString("db.mongo.database.name") ?: "aruppi"
val mongoClient = MongoClients.create(connectionStrings)
val database = mongoClient.getDatabase(databaseName)
monitor.subscribe(ApplicationStopped) {
mongoClient.close()
}
return database
}

@ -0,0 +1,15 @@
package com.jeluchu.core.configuration
import com.jeluchu.features.anime.routes.animeEndpoints
import com.mongodb.client.MongoDatabase
import io.ktor.server.application.*
import io.ktor.server.routing.*
fun Application.initRoutes(
mongoDatabase: MongoDatabase = connectToMongoDB()
) = routing {
route("api/v5") {
initDocumentation()
animeEndpoints(mongoDatabase)
}
}

@ -0,0 +1,11 @@
package com.jeluchu.core.configuration
import io.ktor.server.plugins.swagger.*
import io.ktor.server.routing.*
fun Route.initDocumentation() {
swaggerUI(
path = "/",
swaggerFile = "openapi/documentation.yaml"
)
}

@ -0,0 +1,10 @@
package com.jeluchu.core.messages
sealed class ErrorMessages(val message: String) {
data class Custom(val error: String) : ErrorMessages(error)
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 InvalidInput : ErrorMessages("Invalid input provided")
data object UnauthorizedMongo : ErrorMessages("Check the MongoDb Connection String to be able to correctly access this request.")
}

@ -0,0 +1,6 @@
package com.jeluchu.core.messages
sealed class GeneralMessages(val message: String) {
data class Custom(val info: String) : GeneralMessages(info)
data object DocumentationReference : GeneralMessages("More information on the Aruppi API can be found in the official documentation at: http://0.0.0.0:8080/swagger")
}

@ -0,0 +1,8 @@
package com.jeluchu.core.models
import kotlinx.serialization.Serializable
@Serializable
data class ErrorResponse(
val error: String
)

@ -0,0 +1,8 @@
package com.jeluchu.core.models
import kotlinx.serialization.Serializable
@Serializable
data class MessageResponse(
val message: String
)

@ -0,0 +1,8 @@
package com.jeluchu.core.models
import kotlinx.serialization.Serializable
@Serializable
data class SuccessResponse(
val success: String
)

@ -0,0 +1,195 @@
package com.jeluchu.features.anime.mappers
import com.example.models.*
import org.bson.Document
fun documentToMoreInfoEntity(doc: Document): MoreInfoEntity {
return MoreInfoEntity(
id = doc.getLong("id") ?: 0,
malId = doc.getInteger("malId", 0) ?: 0,
title = doc.getString("title") ?: "",
poster = doc.getString("poster") ?: "",
cover = doc.getString("cover") ?: "",
genres = doc.getList("genres", String::class.java) ?: emptyList(),
synopsis = doc.getString("synopsis") ?: "",
episodes = doc.getList("episodes", Document::class.java)?.map { documentToMergedEpisode(it) } ?: emptyList(),
episodesCount = doc.getInteger("episodesCount", 0) ?: 0,
score = doc.getString("score") ?: "",
staff = doc.getList("staff", Document::class.java)?.map { documentToStaff(it) } ?: emptyList(),
characters = doc.getList("characters", Document::class.java)?.map { documentToCharacter(it) } ?: emptyList(),
status = doc.getString("status") ?: "",
type = doc.getString("type") ?: "",
url = doc.getString("url") ?: "",
promo = doc.get("promo", Document::class.java)?.let { documentToVideoPromo(it) } ?: VideoPromo(),
source = doc.getString("source") ?: "",
duration = doc.getString("duration") ?: "",
rank = doc.getInteger("rank", 0) ?: 0,
titles = doc.getList("titles", Document::class.java)?.map { documentToAlternativeTitles(it) } ?: emptyList(),
airing = doc.getBoolean("airing", false) ?: false,
aired = doc.get("aired", Document::class.java)?.let { documentToAiringTime(it) } ?: AiringTime(),
broadcast = doc.get("broadcast", Document::class.java)?.let { documentToAnimeBroadcast(it) } ?: AnimeBroadcast(),
season = doc.getString("season") ?: "",
year = doc.getInteger("year", 0),
external = doc.getList("external", Document::class.java)?.map { documentToExternalLinks(it) } ?: emptyList(),
streaming = doc.getList("streaming", Document::class.java)?.map { documentToExternalLinks(it) } ?: emptyList(),
studios = doc.getList("studios", Document::class.java)?.map { documentToCompanies(it) } ?: emptyList(),
licensors = doc.getList("licensors", Document::class.java)?.map { documentToCompanies(it) } ?: emptyList(),
producers = doc.getList("producers", Document::class.java)?.map { documentToCompanies(it) } ?: emptyList(),
theme = doc.get("theme", Document::class.java)?.let { documentToThemes(it) } ?: Themes(),
relations = doc.getList("relations", Document::class.java)?.map { documentToRelated(it) } ?: emptyList(),
stats = doc.get("stats", Document::class.java)?.let { documentToStatistics(it) } ?: Statistics(),
gallery = doc.getList("gallery", Document::class.java)?.map { documentToImageMediaEntity(it) } ?: emptyList(),
episodeSource = doc.getString("episodeSource") ?: ""
)
}
fun documentToActor(doc: Document): Actor {
return Actor(
person = doc.get("person", Document::class.java)?.let { documentToIndividual(it) } ?: Individual(),
language = doc.getString("language") ?: ""
)
}
fun documentToAiringTime(doc: Document): AiringTime {
return AiringTime(
from = doc.getString("from") ?: "",
to = doc.getString("to") ?: ""
)
}
fun documentToAlternativeTitles(doc: Document): AlternativeTitles {
return AlternativeTitles(
title = doc.getString("title") ?: "",
type = doc.getString("type") ?: ""
)
}
fun documentToAnimeBroadcast(doc: Document): AnimeBroadcast {
return AnimeBroadcast(
day = doc.getString("day") ?: "",
time = doc.getString("time") ?: "",
timezone = doc.getString("timezone") ?: ""
)
}
fun documentToAnimeSource(doc: Document): AnimeSource {
return AnimeSource(
id = doc.getString("id") ?: "",
source = doc.getString("source") ?: ""
)
}
fun documentToCharacter(doc: Document): Character {
return Character(
character = doc.get("character", Document::class.java)?.let { documentToIndividual(it) } ?: Individual(),
role = doc.getString("role") ?: "",
voiceActor = doc.getList("voiceActor", Document::class.java)?.map { documentToActor(it) } ?: emptyList()
)
}
fun documentToCompanies(doc: Document): Companies {
return Companies(
malId = doc.getInteger("malId", 0),
name = doc.getString("name") ?: "",
type = doc.getString("type") ?: "",
url = doc.getString("url") ?: ""
)
}
fun documentToExternalLinks(doc: Document): ExternalLinks {
return ExternalLinks(
url = doc.getString("url") ?: "",
name = doc.getString("name") ?: ""
)
}
fun documentToImageMediaEntity(doc: Document): ImageMediaEntity {
return ImageMediaEntity(
media = doc.getString("media") ?: "",
thumbnail = doc.getString("thumbnail") ?: "",
width = doc.getInteger("width", 0),
height = doc.getInteger("height", 0),
url = doc.getString("url") ?: ""
)
}
fun documentToImages(doc: Document): Images {
return Images(
generic = doc.getString("generic") ?: "",
small = doc.getString("small") ?: "",
medium = doc.getString("medium") ?: "",
large = doc.getString("large") ?: "",
maximum = doc.getString("maximum") ?: ""
)
}
fun documentToIndividual(doc: Document): Individual {
return Individual(
malId = doc.getInteger("malId", 0),
url = doc.getString("url") ?: "",
name = doc.getString("name") ?: "",
images = doc.getString("images") ?: ""
)
}
fun documentToMergedEpisode(doc: Document): MergedEpisode {
return MergedEpisode(
number = doc.getInteger("number", 0),
ids = doc.getList("ids", Document::class.java)?.map { documentToAnimeSource(it) }?.toMutableList() ?: mutableListOf(),
nextEpisodeDate = doc.getString("nextEpisodeDate") ?: ""
)
}
fun documentToRelated(doc: Document): Related {
return Related(
entry = doc.getList("entry", Document::class.java)?.map { documentToCompanies(it) } ?: emptyList(),
relation = doc.getString("relation") ?: ""
)
}
fun documentToScore(doc: Document): Score {
return Score(
percentage = when (val value = doc["percentage"]) {
is Double -> value
is Int -> value.toDouble()
else -> 0.0
},
score = doc.getInteger("score", 0),
votes = doc.getInteger("votes", 0)
)
}
fun documentToStaff(doc: Document): Staff {
return Staff(
person = doc.get("person", Document::class.java)?.let { documentToIndividual(it) } ?: Individual(),
positions = doc.getList("positions", String::class.java) ?: emptyList()
)
}
fun documentToStatistics(doc: Document): Statistics {
return Statistics(
completed = doc.getInteger("completed"),
dropped = doc.getInteger("dropped"),
onHold = doc.getInteger("onHold"),
planToWatch = doc.getInteger("planToWatch"),
scores = doc.getList("scores", Document::class.java)?.map { documentToScore(it) } ?: emptyList(),
total = doc.getInteger("total"),
watching = doc.getInteger("watching")
)
}
fun documentToThemes(doc: Document): Themes {
return Themes(
endings = doc.getList("endings", String::class.java) ?: emptyList(),
openings = doc.getList("openings", String::class.java) ?: emptyList()
)
}
fun documentToVideoPromo(doc: Document): VideoPromo {
return VideoPromo(
embedUrl = doc.getString("embedUrl") ?: "",
url = doc.getString("url") ?: "",
youtubeId = doc.getString("youtubeId") ?: "",
images = doc.get("images", Document::class.java)?.let { documentToImages(it) } ?: Images()
)
}

@ -0,0 +1,17 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Actor(
/**
* Person generic info.
* @see Individual
*/
val person: Individual = Individual(),
/**
* Language of the person.
*/
val language: String = ""
)

@ -0,0 +1,16 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class AiringTime(
/**
* Start date airing.
*/
val from: String = "",
/**
* End date airing.
*/
val to: String = ""
)

@ -0,0 +1,16 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class AlternativeTitles(
/**
* Title for anime.
*/
val title: String = "",
/**
* Title type for anime.
*/
val type: String = ""
)

@ -0,0 +1,21 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class AnimeBroadcast(
/**
* Day in broadcast.
*/
val day: String = "",
/**
* Time date in broadcast.
*/
val time: String = "",
/**
* Timezone in broadcast.
*/
val timezone: String = ""
)

@ -0,0 +1,10 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class AnimeSource(
val id: String,
val source: String
)

@ -0,0 +1,10 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Character(
var character: Individual = Individual(),
var role: String = "",
var voiceActor: List<Actor> = emptyList()
)

@ -0,0 +1,26 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Companies(
/**
* ID associated with MyAnimeList.
*/
val malId: Int = 0,
/**
* Name for company.
*/
val name: String = "",
/**
* Type for company.
*/
val type: String = "",
/**
* Url for company.
*/
val url: String = ""
)

@ -0,0 +1,16 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class ExternalLinks(
/**
* Url for trailer.
*/
val url: String = "",
/**
* Name of external info.
*/
val name: String = ""
)

@ -0,0 +1,12 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class ImageMediaEntity(
val media: String,
val thumbnail: String,
val width: Int,
val height: Int,
val url: String
)

@ -0,0 +1,12 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Images(
val generic: String = "",
val small: String = "",
val medium: String = "",
val large: String = "",
val maximum: String = ""
)

@ -0,0 +1,11 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Individual(
val malId: Int = 0,
val url: String = "",
val name: String = "",
val images: String = ""
)

@ -0,0 +1,10 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class MergedEpisode(
var number: Int,
var ids: MutableList<AnimeSource> = mutableListOf(),
var nextEpisodeDate: String = ""
)

@ -0,0 +1,53 @@
package com.example.models
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.bson.Document
@Serializable
data class MoreInfoEntity(
val id: Long? = null,
var malId: Int = 0,
var title: String = "",
var poster: String = "",
var cover: String = "",
var genres: List<String> = emptyList(),
var synopsis: String = "",
var episodes: List<MergedEpisode> = emptyList(),
var episodesCount: Int = 0,
var score: String = "",
var staff: List<Staff> = emptyList(),
var characters: List<Character> = emptyList(),
var status: String = "",
var type: String = "",
val url: String = "",
val promo: VideoPromo = VideoPromo(),
val source: String = "",
val duration: String = "",
val rank: Int = 0,
val titles: List<AlternativeTitles> = emptyList(),
val airing: Boolean = false,
val aired: AiringTime = AiringTime(),
val broadcast: AnimeBroadcast = AnimeBroadcast(),
val season: String = "",
val year: Int = 0,
val external: List<ExternalLinks> = emptyList(),
val streaming: List<ExternalLinks> = emptyList(),
val studios: List<Companies> = emptyList(),
val licensors: List<Companies> = emptyList(),
val producers: List<Companies> = emptyList(),
val theme: Themes = Themes(),
val relations: List<Related> = emptyList(),
val stats: Statistics = Statistics(),
val gallery: List<ImageMediaEntity> = emptyList(),
val episodeSource: String = ""
) {
fun toDocument(): Document = Document.parse(Json.encodeToString(this))
companion object {
private val json = Json { ignoreUnknownKeys = true }
fun fromDocument(document: Document): MoreInfoEntity = json.decodeFromString(document.toJson())
}
}

@ -0,0 +1,17 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Related(
/**
* List of entries for relation in anime.
* @see Companies
*/
val entry: List<Companies>,
/**
* Relation for anime.
*/
val relation: String
)

@ -0,0 +1,10 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Score(
val percentage: Double,
val score: Int,
val votes: Int
)

@ -0,0 +1,9 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Staff(
var person: Individual = Individual(),
var positions: List<String> = emptyList()
)

@ -0,0 +1,14 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Statistics(
val completed: Int? = null,
val dropped: Int? = null,
val onHold: Int? = null,
val planToWatch: Int? = null,
val scores: List<Score>? = emptyList(),
val total: Int? = null,
val watching: Int? = null
)

@ -0,0 +1,16 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
open class Themes(
/**
* List of endings.
*/
val endings: List<String> = emptyList(),
/**
* List of openings.
*/
val openings: List<String> = emptyList()
)

@ -0,0 +1,26 @@
package com.example.models
import kotlinx.serialization.Serializable
@Serializable
open class VideoPromo(
/**
* Embed url for trailer.
*/
val embedUrl: String = "",
/**
* Url for trailer.
*/
val url: String = "",
/**
* Youtube id for trailer.
*/
val youtubeId: String = "",
/**
* Images for trailer.
*/
val images: Images = Images()
)

@ -0,0 +1,13 @@
package com.jeluchu.features.anime.routes
import com.jeluchu.features.anime.services.AnimeService
import com.mongodb.client.MongoDatabase
import io.ktor.server.routing.*
fun Route.animeEndpoints(
mongoDatabase: MongoDatabase,
service: AnimeService = AnimeService(mongoDatabase)
) {
get("/directory") { service.getDirectory(call) }
get("/anime/{id}") { service.getAnimeByMalId(call) }
}

@ -0,0 +1,52 @@
package com.jeluchu.features.anime.services
import com.jeluchu.core.messages.ErrorMessages
import com.jeluchu.core.models.ErrorResponse
import com.jeluchu.features.anime.mappers.documentToMoreInfoEntity
import com.mongodb.client.MongoDatabase
import com.mongodb.client.model.Filters
import io.ktor.http.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
class AnimeService(
database: MongoDatabase
) {
private val directoryCollection = database.getCollection("animedetails")
suspend fun getDirectory(
call: RoutingCall
) = withContext(Dispatchers.IO) {
call.response.headers.append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
try {
val elements = directoryCollection.find().toList()
val directory = elements.map { documentToMoreInfoEntity(it) }
val json = Json.encodeToString(directory)
call.respond(HttpStatusCode.OK, json)
} catch (ex: Exception) {
call.respond(HttpStatusCode.Unauthorized, ErrorResponse(ErrorMessages.UnauthorizedMongo.message))
}
}
suspend fun getAnimeByMalId(
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)
directoryCollection.find(Filters.eq("malId", id)).firstOrNull()?.let { anime ->
val info = documentToMoreInfoEntity(anime)
call.respond(HttpStatusCode.OK, Json.encodeToString(info))
} ?: call.respond(HttpStatusCode.NotFound, ErrorResponse(ErrorMessages.AnimeNotFound.message))
} catch (ex: Exception) {
call.respond(HttpStatusCode.NotFound, ErrorResponse(ErrorMessages.InvalidInput.message))
}
}
}

@ -0,0 +1,13 @@
ktor:
application:
modules:
- com.jeluchu.ApplicationKt.module
deployment:
port: 8080
host: "0.0.0.0"
db:
mongo:
connectionStrings: ${?MONGO_CONNECTION_STRING}
database:
name: ${?MONGO_DATABASE_NAME}

@ -0,0 +1,36 @@
openapi: "3.0.3"
info:
title: "Aruppi API"
description: "Application API"
version: "5.0.0-preview"
servers:
- url: "http://0.0.0.0:8080/api/v5"
paths:
/directory:
get:
description: "Hello World!"
responses:
"200":
description: "OK"
content:
text/plain:
schema:
type: "string"
examples:
Example#1:
value: "Hello World!"
/anime/{id}:
get:
description: "Hello World!"
responses:
"200":
description: "OK"
content:
text/plain:
schema:
type: "string"
examples:
Example#1:
value: "Hello World!"
components:
schemas: {}
Loading…
Cancel
Save