diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml new file mode 100644 index 0000000..b820ab4 --- /dev/null +++ b/.github/workflows/develop.yml @@ -0,0 +1,36 @@ +name: Build +on: + push: + branches-ignore: + - stable + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=API \ No newline at end of file diff --git a/pom.xml b/pom.xml index 4242c24..043427f 100644 --- a/pom.xml +++ b/pom.xml @@ -13,11 +13,19 @@ official 1.7.20 11 - 1.4.4 + 1.4.5 UTF-8 true fr.ziedelth.ApplicationKt 5.9.1 + + + **/fr/ziedelth/events/**, + **/fr/ziedelth/listeners/**, + **/fr/ziedelth/plugins/**, + **/fr/ziedelth/utils/**, + **/fr/ziedelth/Application.kt + @@ -47,6 +55,17 @@ ${junit-jupiter.version} test + + com.h2database + h2 + 2.1.214 + test + + + io.ktor + ktor-server-test-host-jvm + ${ktor_version} + io.ktor ktor-server-core-jvm @@ -67,6 +86,12 @@ ktor-server-content-negotiation-jvm ${ktor_version} + + io.ktor + ktor-client-content-negotiation-jvm + ${ktor_version} + test + io.ktor ktor-serialization-gson-jvm @@ -199,9 +224,34 @@ maven-compiler-plugin ${kotlin.compiler.jvmTarget} - ${kotlin.compiler.jvmTarget} + + org.sonarsource.scanner.maven + sonar-maven-plugin + 3.9.1.2184 + + + org.jacoco + jacoco-maven-plugin + 0.8.8 + + + prepare-agent + + prepare-agent + + test-compile + + + report + + report + + test + + + \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/controllers/AbstractController.kt b/src/main/kotlin/fr/ziedelth/controllers/AbstractController.kt new file mode 100644 index 0000000..155a0b3 --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/controllers/AbstractController.kt @@ -0,0 +1,118 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.repositories.IPageRepository +import fr.ziedelth.utils.Decoder +import fr.ziedelth.utils.ImageCache +import fr.ziedelth.utils.RequestCache +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import io.ktor.util.pipeline.* +import java.io.Serializable +import java.lang.reflect.ParameterizedType +import java.util.* + +const val UNKNOWN_MESSAGE_ERROR = "Unknown error" +const val MISSING_PARAMETERS_MESSAGE_ERROR = "Missing parameters" + +open class IController(val prefix: String) { + val entityName: String = ((javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<*>).simpleName + val uuidRequest: UUID = UUID.randomUUID() + + fun PipelineContext.getPageAndLimit(): Pair { + val page = call.parameters["page"]!!.toIntOrNull() ?: throw IllegalArgumentException("Page is not valid") + val limit = call.parameters["limit"]!!.toIntOrNull() ?: throw IllegalArgumentException("Limit is not valid") + + if (page < 1 || limit < 1) { + throw IllegalArgumentException("Page or limit is not valid") + } + + if (limit > 30) { + throw IllegalArgumentException("Limit is too high") + } + + return Pair(page, limit) + } + + suspend fun printError(call: ApplicationCall, e: Exception) { + e.printStackTrace() + call.respond(HttpStatusCode.InternalServerError, e.message ?: UNKNOWN_MESSAGE_ERROR) + } + + protected fun Route.getWithPage(iPageRepository: IPageRepository) { + get("/country/{country}/page/{page}/limit/{limit}") { + try { + val country = call.parameters["country"]!! + val (page, limit) = getPageAndLimit() + println("GET $prefix/country/$country/page/$page/limit/$limit") + val request = RequestCache.get(uuidRequest, country, page, limit) + + if (request == null || request.isExpired()) { + val list = iPageRepository.getByPage(country, page, limit) + request?.update(list) ?: RequestCache.put(uuidRequest, country, page, limit, value = list) + } + + call.respond(RequestCache.get(uuidRequest, country, page, limit)!!.value!!) + } catch (e: Exception) { + printError(call, e) + } + } + } + + protected fun Route.getAnimeWithPage(iPageRepository: IPageRepository) { + get("/anime/{uuid}/page/{page}/limit/{limit}") { + try { + val animeUuid = call.parameters["uuid"]!! + val (page, limit) = getPageAndLimit() + println("GET $prefix/anime/$animeUuid/page/$page/limit/$limit") + call.respond(iPageRepository.getByPageWithAnime(UUID.fromString(animeUuid), page, limit)) + } catch (e: Exception) { + printError(call, e) + } + } + } + + protected fun Route.getWatchlistWithPage(iPageRepository: IPageRepository) { + post("/watchlist/page/{page}/limit/{limit}") { + try { + val watchlist = call.receive() + val (page, limit) = getPageAndLimit() + println("POST $prefix/watchlist/page/$page/limit/$limit") + val dataFromGzip = + Gson().fromJson(Decoder.fromGzip(watchlist), Array::class.java).map { UUID.fromString(it) } + call.respond(iPageRepository.getByPageWithList(dataFromGzip, page, limit)) + } catch (e: Exception) { + printError(call, e) + } + } + } + + fun Route.getAttachment() { + get("/attachment/{uuid}") { + val string = call.parameters["uuid"]!! + val uuidRegex = + "^[0-9(a-f|A-F)]{8}-[0-9(a-f|A-F)]{4}-4[0-9(a-f|A-F)]{3}-[89ab][0-9(a-f|A-F)]{3}-[0-9(a-f|A-F)]{12}\$".toRegex() + + if (!uuidRegex.matches(string)) { + println("GET $prefix/attachment/$string : Invalid UUID") + return@get call.respond(HttpStatusCode.BadRequest) + } + + val uuid = UUID.fromString(string) + println("GET ${prefix}/attachment/$uuid") + + if (!ImageCache.contains(uuid)) { + println("Attachment $uuid not found") + call.respond(HttpStatusCode.NoContent) + return@get + } + + val image = ImageCache.get(uuid)!! + println("Attachment $uuid found (${image.bytes.size} bytes)") + call.respondBytes(image.bytes, ContentType("image", "webp")) + } + } +} diff --git a/src/main/kotlin/fr/ziedelth/controllers/AnimeController.kt b/src/main/kotlin/fr/ziedelth/controllers/AnimeController.kt index 03c2831..00fb76c 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/AnimeController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/AnimeController.kt @@ -1,10 +1,11 @@ package fr.ziedelth.controllers -import com.google.gson.Gson import fr.ziedelth.entities.Anime import fr.ziedelth.entities.isNullOrNotValid -import fr.ziedelth.utils.Database -import fr.ziedelth.utils.Decoder +import fr.ziedelth.repositories.AnimeRepository +import fr.ziedelth.repositories.CountryRepository +import fr.ziedelth.repositories.EpisodeRepository +import fr.ziedelth.repositories.MangaRepository import fr.ziedelth.utils.ImageCache import fr.ziedelth.utils.RequestCache import io.ktor.http.* @@ -14,141 +15,61 @@ import io.ktor.server.response.* import io.ktor.server.routing.* import java.util.* -object AnimeController : IController("/animes") { - fun Routing.getAnimes() { - route(prefix) { +class AnimeController( + private val countryRepository: CountryRepository, + private val animeRepository: AnimeRepository, + private val episodeRepository: EpisodeRepository, + private val mangaRepository: MangaRepository +) : + IController("/animes") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { search() - getWithPage() - getWatchlistWithPage() + getByPage() + getWatchlistWithPage(animeRepository) getAttachment() create() merge() + diary() } } private fun Route.search() { route("/country/{country}/search") { get("/hash/{hash}") { - val country = call.parameters["country"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val hash = call.parameters["hash"] ?: return@get call.respond(HttpStatusCode.BadRequest) + val country = call.parameters["country"]!! + val hash = call.parameters["hash"]!! println("GET $prefix/country/$country/search/hash/$hash") - val session = Database.getSession() - - try { - val query = session.createQuery( - "SELECT a.uuid FROM Anime a JOIN a.hashes h WHERE a.country.tag = :tag AND h = :hash", - UUID::class.java - ) - query.maxResults = 1 - query.setParameter("tag", country) - query.setParameter("hash", hash) - val uuid = query.uniqueResult() ?: return@get call.respond(HttpStatusCode.NotFound) - call.respond(mapOf("uuid" to uuid)) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } + val anime = animeRepository.findByHash(country, hash) + call.respond(if (anime != null) mapOf("uuid" to anime) else HttpStatusCode.NotFound) } get("/name/{name}") { - val country = call.parameters["country"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val name = call.parameters["name"] ?: return@get call.respond(HttpStatusCode.BadRequest) + val country = call.parameters["country"]!! + val name = call.parameters["name"]!! println("GET $prefix/country/$country/search/name/$name") - val session = Database.getSession() - - try { - val query = session.createQuery( -// "FROM Anime a WHERE a.country.tag = :tag AND LOWER(name) LIKE CONCAT('%', :name, '%')", - "SELECT DISTINCT anime FROM Episode e WHERE e.anime.country.tag = :tag AND LOWER(e.anime.name) LIKE CONCAT('%', :name, '%') ORDER BY e.anime.name", - Anime::class.java - ) - query.setParameter("tag", country) - query.setParameter("name", name.lowercase()) - call.respond(query.list() ?: HttpStatusCode.NotFound) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } + call.respond(animeRepository.findByName(country, name)) } } } - private fun Route.getWithPage() { + private fun Route.getByPage() { get("/country/{country}/simulcast/{simulcast}/page/{page}/limit/{limit}") { - val country = call.parameters["country"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val simulcast = call.parameters["simulcast"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val page = call.parameters["page"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - val limit = call.parameters["limit"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - if (page < 1 || limit < 1) return@get call.respond(HttpStatusCode.BadRequest) - if (limit > 30) return@get call.respond(HttpStatusCode.BadRequest) - println("GET $prefix/country/$country/simulcast/$simulcast/page/$page/limit/$limit") - val request = RequestCache.get(uuidRequest, country, page, limit, simulcast) - - if (request == null || request.isExpired()) { - val session = Database.getSession() - - try { - val query = session.createQuery( - "FROM Anime a JOIN a.simulcasts s WHERE a.country.tag = :tag AND s.uuid = :simulcast ORDER BY a.name", - Anime::class.java - ) - query.setParameter("tag", country) - query.setParameter("simulcast", UUID.fromString(simulcast)) - query.firstResult = (limit * page) - limit - query.maxResults = limit - request?.update(query.list()) ?: RequestCache.put( - uuidRequest, - country, - page, - limit, - simulcast, - query.list() - ) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } - } - - call.respond( - RequestCache.get(uuidRequest, country, page, limit, simulcast)?.value ?: HttpStatusCode.NotFound - ) - } - } - - private fun Route.getWatchlistWithPage() { - post("/watchlist/page/{page}/limit/{limit}") { - val watchlist = call.receive() - val page = call.parameters["page"]?.toInt() ?: return@post call.respond(HttpStatusCode.BadRequest) - val limit = call.parameters["limit"]?.toInt() ?: return@post call.respond(HttpStatusCode.BadRequest) - if (page < 1 || limit < 1) return@post call.respond(HttpStatusCode.BadRequest) - if (limit > 30) return@post call.respond(HttpStatusCode.BadRequest) - println("POST $prefix/watchlist/page/$page/limit/$limit") - val session = Database.getSession() - try { - val dataFromGzip = - Gson().fromJson(Decoder.fromGzip(watchlist), Array::class.java).map { UUID.fromString(it) } + val country = call.parameters["country"]!! + val simulcast = call.parameters["simulcast"]!! + val (page, limit) = getPageAndLimit() + println("GET $prefix/country/$country/simulcast/$simulcast/page/$page/limit/$limit") + val request = RequestCache.get(uuidRequest, country, page, limit, simulcast) + + if (request == null || request.isExpired()) { + val list = animeRepository.getByPage(country, UUID.fromString(simulcast), page, limit) + request?.update(list) ?: RequestCache.put(uuidRequest, country, page, limit, simulcast, list) + } - val query = session.createQuery( - "FROM $entityName WHERE uuid IN :list ORDER BY name", - entityClass - ) - query.setParameter("list", dataFromGzip) - query.firstResult = (limit * page) - limit - query.maxResults = limit - call.respond(query.list()) + call.respond(RequestCache.get(uuidRequest, country, page, limit, simulcast)!!.value!!) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() + printError(call, e) } } } @@ -160,8 +81,9 @@ object AnimeController : IController("/animes") { try { val anime = call.receive() - anime.country = CountryController.getBy("uuid", anime.country?.uuid) ?: return@post run { + anime.country = countryRepository.find(anime.country!!.uuid) ?: return@post run { println("Country not found") + call.respond( HttpStatusCode.BadRequest, "Country not found" @@ -169,35 +91,36 @@ object AnimeController : IController("/animes") { } if (anime.isNullOrNotValid()) { - println("Missing parameters") + println(MISSING_PARAMETERS_MESSAGE_ERROR) println(anime) - call.respond(HttpStatusCode.BadRequest, "Missing parameters") + call.respond(HttpStatusCode.BadRequest, MISSING_PARAMETERS_MESSAGE_ERROR) return@post } - if (isExists("name", anime.name)) { + if (animeRepository.findOneByName( + anime.country!!.tag!!, + anime.name!! + )?.country?.uuid == anime.country!!.uuid + ) { println("$entityName already exists") call.respond(HttpStatusCode.Conflict, "$entityName already exists") return@post } val hash = anime.hash() - if (contains("hashes", hash)) { + + if (animeRepository.findByHash(anime.country!!.tag!!, hash) != null) { println("$entityName already exists") call.respond(HttpStatusCode.Conflict, "$entityName already exists") return@post } - if (!(anime.hashes.contains(hash))) { - anime.hashes.add(hash!!) - } - - val savedAnime = justSave(anime) + anime.hashes.add(hash) + val savedAnime = animeRepository.save(anime) ImageCache.cachingNetworkImage(savedAnime.uuid, savedAnime.image!!) call.respond(HttpStatusCode.Created, savedAnime) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") + printError(call, e) } } } @@ -208,7 +131,7 @@ object AnimeController : IController("/animes") { val uuids = call.receive>().map { UUID.fromString(it) } println("PUT $prefix/merge") // Get anime - val animes = uuids.mapNotNull { getBy("uuid", it) } + val animes = uuids.mapNotNull { animeRepository.find(it) } if (animes.isEmpty()) { println("Anime not found") @@ -217,7 +140,7 @@ object AnimeController : IController("/animes") { } // Get all countries - val countries = animes.map { it.country }.distinctBy { it?.uuid } + val countries = animes.map { it.country }.distinctBy { it!!.uuid } if (countries.size > 1) { println("Anime has different countries") @@ -233,46 +156,45 @@ object AnimeController : IController("/animes") { val simulcasts = animes.map { it.simulcasts }.flatten().distinctBy { it.uuid }.toMutableSet() // Get all episodes val episodes = - animes.map { EpisodeController.getAllBy("anime.uuid", it.uuid) }.flatten().distinctBy { it.uuid } + animes.map { episodeRepository.getAllBy("anime.uuid", it.uuid) }.flatten().distinctBy { it.uuid } .toMutableSet() // Get all mangas - val mangas = animes.map { MangaController.getAllBy("anime.uuid", it.uuid) }.flatten().distinctBy { it.uuid } + val mangas = animes.map { mangaRepository.getAllBy("anime.uuid", it.uuid) }.flatten().distinctBy { it.uuid } .toMutableSet() val firstAnime = animes.first() - val mergedAnime = Anime( - country = countries.first(), - name = "${animes.first().name} (${animes.size})", - releaseDate = firstAnime.releaseDate, - image = firstAnime.image, - description = firstAnime.description, - hashes = hashes, - genres = genres, - simulcasts = simulcasts - ) - val savedAnime = justSave(mergedAnime) + val savedAnime = animeRepository.find( + animeRepository.save( + Anime( + country = countries.first(), + name = "${animes.first().name} (${animes.size})", + releaseDate = firstAnime.releaseDate, + image = firstAnime.image, + description = firstAnime.description, + hashes = hashes, + genres = genres, + simulcasts = simulcasts + ) + ).uuid + )!! + ImageCache.cachingNetworkImage(savedAnime.uuid, savedAnime.image!!) - episodes.map { it.copy(anime = savedAnime) }.map { EpisodeController.justSave(it) } - mangas.map { it.copy(anime = savedAnime) }.map { MangaController.justSave(it) } + episodeRepository.saveAll(episodes.map { it.copy(anime = savedAnime) }) + mangaRepository.saveAll(mangas.map { it.copy(anime = savedAnime) }) // Delete animes - val session = Database.getSession() - val transaction = session.beginTransaction() + animeRepository.deleteAll(animes) + call.respond(HttpStatusCode.OK, savedAnime) + } + } - try { - session.createQuery("DELETE Anime WHERE uuid IN :list") - .setParameter("list", uuids) - .executeUpdate() - transaction.commit() - } catch (e: Exception) { - e.printStackTrace() - println("Error while deleting $prefix : ${e.message}") - transaction.rollback() - throw e - } finally { - session.close() - } + private fun Route.diary() { + get("/diary/country/{country}/day/{day}") { + val country = call.parameters["country"]!! + val day = call.parameters["day"]!!.toIntOrNull() ?: return@get call.respond(HttpStatusCode.BadRequest) + println("GET $prefix/diary/country/$country/day/$day") + call.respond(animeRepository.getDiary(country, day)) } } } diff --git a/src/main/kotlin/fr/ziedelth/controllers/CountryController.kt b/src/main/kotlin/fr/ziedelth/controllers/CountryController.kt index 016762b..f8a4945 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/CountryController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/CountryController.kt @@ -2,20 +2,28 @@ package fr.ziedelth.controllers import fr.ziedelth.entities.Country import fr.ziedelth.entities.isNullOrNotValid +import fr.ziedelth.repositories.CountryRepository import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -object CountryController : IController("/countries") { - fun Routing.getCountries() { - route(prefix) { +class CountryController(private val countryRepository: CountryRepository) : IController("/countries") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { getAll() create() } } + fun Route.getAll() { + get { + println("GET $prefix") + call.respond(countryRepository.getAll()) + } + } + private fun Route.create() { post { println("POST $prefix") @@ -24,24 +32,23 @@ object CountryController : IController("/countries") { val country = call.receive() if (country.isNullOrNotValid()) { - call.respond(HttpStatusCode.BadRequest, "Missing parameters") + call.respond(HttpStatusCode.BadRequest, MISSING_PARAMETERS_MESSAGE_ERROR) return@post } - if (isExists("tag", country.tag!!)) { + if (countryRepository.exists("tag", country.tag)) { call.respond(HttpStatusCode.Conflict, "$entityName already exists") return@post } - if (isExists("name", country.name!!)) { + if (countryRepository.exists("name", country.name)) { call.respond(HttpStatusCode.Conflict, "$entityName already exists") return@post } - call.respond(HttpStatusCode.Created, justSave(country)) + call.respond(HttpStatusCode.Created, countryRepository.save(country)) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") + printError(call, e) } } } diff --git a/src/main/kotlin/fr/ziedelth/controllers/DeviceController.kt b/src/main/kotlin/fr/ziedelth/controllers/DeviceController.kt deleted file mode 100644 index 37ccd69..0000000 --- a/src/main/kotlin/fr/ziedelth/controllers/DeviceController.kt +++ /dev/null @@ -1,85 +0,0 @@ -package fr.ziedelth.controllers - -import com.google.gson.Gson -import com.google.gson.JsonObject -import fr.ziedelth.entities.Device -import fr.ziedelth.entities.isNullOrNotValid -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.request.* -import io.ktor.server.response.* -import io.ktor.server.routing.* -import java.util.* - -object DeviceController : IController("/devices") { - private fun update(device: Device) { - val newDevice = getBy("name", device.name) - - if (newDevice.isNullOrNotValid()) { - println("Missing parameters") - println(device) - return - } - - newDevice!!.os = device.os - newDevice.model = device.model - newDevice.updatedAt = Calendar.getInstance() - justUpdate(newDevice) - } - - fun update(name: String, device: Device? = null) { - val newDevice = device ?: getBy("name", name) ?: return - newDevice.updatedAt = Calendar.getInstance() - justUpdate(newDevice) - } - - fun Routing.getDevices() { - route(prefix) { - create() - } - } - - private fun Route.create() { - post { - try { - val gson = Gson().fromJson(call.receiveText(), JsonObject::class.java) - - val device = Device( - name = gson.get("name")?.asString, - os = gson.get("os")?.asString, - model = gson.get("model")?.asString - ) - - println("POST $prefix") - - if (device.isNullOrNotValid()) { - println("Missing parameters") - println(device) - call.respond(HttpStatusCode.BadRequest, "Missing parameters") - return@post - } - - if (isExists("name", device.name)) { - println("$entityName already exists, updating") - - try { - update(device) - call.respond(HttpStatusCode.OK, "$entityName updated") - } catch (e: Exception) { - println("Error while updating $entityName") - println(e) - call.respond(HttpStatusCode.InternalServerError, "Error while updating $entityName") - } - - return@post - } - - val savedDevice = justSave(device) - call.respond(HttpStatusCode.Created, savedDevice) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } - } - } -} diff --git a/src/main/kotlin/fr/ziedelth/controllers/DeviceRedirectionController.kt b/src/main/kotlin/fr/ziedelth/controllers/DeviceRedirectionController.kt deleted file mode 100644 index 802076d..0000000 --- a/src/main/kotlin/fr/ziedelth/controllers/DeviceRedirectionController.kt +++ /dev/null @@ -1,136 +0,0 @@ -package fr.ziedelth.controllers - -import fr.ziedelth.entities.Device -import fr.ziedelth.entities.Episode -import fr.ziedelth.entities.Manga -import fr.ziedelth.entities.device_redirections.DeviceEpisodeRedirection -import fr.ziedelth.entities.device_redirections.DeviceMangaRedirection -import fr.ziedelth.utils.Database -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.request.* -import io.ktor.server.response.* -import io.ktor.server.routing.* -import java.util.* - -object DeviceRedirectionController : IController("/devices/redirection") { - fun Route.getRedirection() { - route(prefix) { - episode() - manga() - } - } - - private fun getEpisodeRedirection(device: Device, episode: Episode): DeviceEpisodeRedirection? { - val session = Database.getSession() - - try { - val query = session.createQuery( - "FROM DeviceEpisodeRedirection WHERE device = :device AND episode = :episode", - DeviceEpisodeRedirection::class.java - ) - query.maxResults = 1 - query.setParameter("device", device) - query.setParameter("episode", episode) - return query.uniqueResult() - } catch (e: Exception) { - return null - } finally { - session.close() - } - } - - private fun Route.episode() { - post("/episode") { - try { - val deviceName = call.request.header("Device") ?: return@post call.respond(HttpStatusCode.BadRequest) - val episodeId = UUID.fromString( - call.request.header("Episode") ?: return@post call.respond( - HttpStatusCode.BadRequest - ) - ) - println("POST $prefix/episode") - val device = DeviceController.getBy("name", deviceName) - val episode = EpisodeController.getBy("id", episodeId) - - if (device == null || episode == null) { - println("Missing parameters") - return@post call.respond(HttpStatusCode.BadRequest) - } - - DeviceController.update(deviceName, device) - - val redirection = getEpisodeRedirection(device, episode) - - if (redirection != null) { - redirection.timestamp = Calendar.getInstance() - justUpdate(redirection) - } else { - justSave(DeviceEpisodeRedirection(device = device, episode = episode)) - } - - call.respond(HttpStatusCode.Created, "$entityName created") - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } - } - } - - private fun getMangaRedirection(device: Device, manga: Manga): DeviceMangaRedirection? { - val session = Database.getSession() - - try { - val query = session.createQuery( - "FROM DeviceMangaRedirection WHERE device = :device AND manga = :manga", - DeviceMangaRedirection::class.java - ) - query.maxResults = 1 - query.setParameter("device", device) - query.setParameter("manga", manga) - return query.uniqueResult() - } catch (e: Exception) { - return null - } finally { - session.close() - } - } - - private fun Route.manga() { - post("/manga") { - try { - val deviceName = call.request.header("Device") ?: return@post call.respond(HttpStatusCode.BadRequest) - val mangaId = UUID.fromString( - call.request.header("Manga") ?: return@post call.respond( - HttpStatusCode.BadRequest - ) - ) - println("POST $prefix/manga") - val device = DeviceController.getBy("name", deviceName) - val manga = MangaController.getBy("uuid", mangaId) - - if (device == null || manga == null) { - println("Missing parameters") - call.respond(HttpStatusCode.BadRequest) - return@post - } - - DeviceController.update(deviceName, device) - - val redirection = getMangaRedirection(device, manga) - - if (redirection != null) { - redirection.timestamp = Calendar.getInstance() - justUpdate(redirection) - } else { - justSave(DeviceMangaRedirection(device = device, manga = manga)) - } - - call.respond(HttpStatusCode.Created, "$entityName created") - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/controllers/DiaryController.kt b/src/main/kotlin/fr/ziedelth/controllers/DiaryController.kt deleted file mode 100644 index 5d743b9..0000000 --- a/src/main/kotlin/fr/ziedelth/controllers/DiaryController.kt +++ /dev/null @@ -1,37 +0,0 @@ -package fr.ziedelth.controllers - -import fr.ziedelth.entities.Anime -import fr.ziedelth.utils.Database -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.response.* -import io.ktor.server.routing.* - -object DiaryController : IController("/diary") { - fun Routing.getDiary() { - route(prefix) { - get("/country/{country}/day/{day}") { - val country = call.parameters["country"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val day = call.parameters["day"]?.toIntOrNull() ?: return@get call.respond(HttpStatusCode.BadRequest) - println("GET $prefix/country/$country/day/$day") - val session = Database.getSession() - - try { - val query = session.createQuery( - "SELECT anime FROM Episode episode WHERE episode.anime.country.tag = :tag AND current_date - to_date(episode.releaseDate, 'YYYY-MM-DDTHH:MI:SS') <= 7 AND FUNCTION('date_part', 'dow', to_date(episode.releaseDate, 'YYYY-MM-DDTHH:MI:SS')) = :day ORDER BY episode.anime.name ASC", - entityClass - ) - query.setParameter("tag", country) - query.setParameter("day", day) - val list = query.list()?.distinctBy { it.uuid } - call.respond(list ?: HttpStatusCode.NotFound) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } - } - } - } -} diff --git a/src/main/kotlin/fr/ziedelth/controllers/EpisodeController.kt b/src/main/kotlin/fr/ziedelth/controllers/EpisodeController.kt index fc43eb7..50fde26 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/EpisodeController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/EpisodeController.kt @@ -1,140 +1,42 @@ package fr.ziedelth.controllers -import com.google.gson.Gson import fr.ziedelth.entities.Episode import fr.ziedelth.entities.Simulcast import fr.ziedelth.entities.isNullOrNotValid import fr.ziedelth.events.EpisodesReleaseEvent -import fr.ziedelth.utils.Database -import fr.ziedelth.utils.Decoder +import fr.ziedelth.repositories.* import fr.ziedelth.utils.ImageCache -import fr.ziedelth.utils.RequestCache import fr.ziedelth.utils.plugins.PluginManager import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -import java.util.* -object EpisodeController : IController("/episodes") { - fun Routing.getEpisodes() { - route(prefix) { - getWithPage() - getAnimeWithPage() - getWatchlistWithPage() +class EpisodeController( + private val platformRepository: PlatformRepository, + private val animeRepository: AnimeRepository, + private val simulcastRepository: SimulcastRepository, + private val episodeTypeRepository: EpisodeTypeRepository, + private val langTypeRepository: LangTypeRepository, + private val episodeRepository: EpisodeRepository, +) : IController("/episodes") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { + getWithPage(episodeRepository) + getAnimeWithPage(episodeRepository) + getWatchlistWithPage(episodeRepository) getAttachment() create() } } - private fun Route.getWithPage() { - get("/country/{country}/page/{page}/limit/{limit}") { - val country = call.parameters["country"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val page = call.parameters["page"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - val limit = call.parameters["limit"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - if (page < 1 || limit < 1) return@get call.respond(HttpStatusCode.BadRequest) - if (limit > 30) return@get call.respond(HttpStatusCode.BadRequest) - println("GET $prefix/country/$country/page/$page/limit/$limit") - val request = RequestCache.get(uuidRequest, country, page, limit) - - if (request == null || request.isExpired()) { - val session = Database.getSession() - - try { - val query = session.createQuery( - "FROM Episode WHERE anime.country.tag = :tag ORDER BY releaseDate DESC, anime.name, season DESC, number DESC, episodeType.name, langType.name", - Episode::class.java - ) - query.setParameter("tag", country) - query.firstResult = (limit * page) - limit - query.maxResults = limit - request?.update(query.list()) ?: RequestCache.put( - uuidRequest, - country, - page, - limit, - value = query.list() - ) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } - } - - call.respond(RequestCache.get(uuidRequest, country, page, limit)?.value ?: HttpStatusCode.NotFound) - } - } - - private fun Route.getAnimeWithPage() { - get("/anime/{uuid}/page/{page}/limit/{limit}") { - val animeUuid = call.parameters["uuid"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val page = call.parameters["page"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - val limit = call.parameters["limit"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - if (page < 1 || limit < 1) return@get call.respond(HttpStatusCode.BadRequest) - if (limit > 30) return@get call.respond(HttpStatusCode.BadRequest) - println("GET $prefix/anime/$animeUuid/page/$page/limit/$limit") - val session = Database.getSession() - - try { - val query = session.createQuery( - "FROM Episode WHERE anime.uuid = :uuid ORDER BY season DESC, number DESC, episodeType.name, langType.name", - Episode::class.java - ) - query.setParameter("uuid", UUID.fromString(animeUuid)) - query.firstResult = (limit * page) - limit - query.maxResults = limit - call.respond(query.list() ?: HttpStatusCode.NotFound) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } - } - } - - private fun Route.getWatchlistWithPage() { - post("/watchlist/page/{page}/limit/{limit}") { - val watchlist = call.receive() - val page = call.parameters["page"]?.toInt() ?: return@post call.respond(HttpStatusCode.BadRequest) - val limit = call.parameters["limit"]?.toInt() ?: return@post call.respond(HttpStatusCode.BadRequest) - if (page < 1 || limit < 1) return@post call.respond(HttpStatusCode.BadRequest) - if (limit > 30) return@post call.respond(HttpStatusCode.BadRequest) - println("POST $prefix/watchlist/page/$page/limit/$limit") - val session = Database.getSession() - - try { - val dataFromGzip = - Gson().fromJson(Decoder.fromGzip(watchlist), Array::class.java).map { UUID.fromString(it) } - - val query = session.createQuery( - "FROM $entityName WHERE anime.uuid IN :list ORDER BY releaseDate DESC, anime.name, season DESC, number DESC, episodeType.name, langType.name", - entityClass - ) - query.setParameter("list", dataFromGzip) - query.firstResult = (limit * page) - limit - query.maxResults = limit - call.respond(query.list()) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } - } - } - private fun merge(episode: Episode) { - episode.platform = - PlatformController.getBy("uuid", episode.platform!!.uuid) ?: throw Exception("Platform not found") - episode.anime = AnimeController.getBy("uuid", episode.anime!!.uuid) ?: throw Exception("Anime not found") + episode.platform = platformRepository.find(episode.platform!!.uuid) ?: throw Exception("Platform not found") + episode.anime = animeRepository.find(episode.anime!!.uuid) ?: throw Exception("Anime not found") episode.episodeType = - EpisodeTypeController.getBy("uuid", episode.episodeType!!.uuid) ?: throw Exception("EpisodeType not found") - episode.langType = - LangTypeController.getBy("uuid", episode.langType!!.uuid) ?: throw Exception("LangType not found") + episodeTypeRepository.find(episode.episodeType!!.uuid) ?: throw Exception("EpisodeType not found") + episode.langType = langTypeRepository.find(episode.langType!!.uuid) ?: throw Exception("LangType not found") if (episode.isNullOrNotValid()) { throw Exception("Episode is not valid") @@ -142,7 +44,8 @@ object EpisodeController : IController("/episodes") { val tmpSimulcast = Simulcast.getSimulcast(episode.releaseDate.split("-")[0].toInt(), episode.releaseDate.split("-")[1].toInt()) - val simulcast = SimulcastController.getBy(tmpSimulcast) + val simulcast = + simulcastRepository.findBySeasonAndYear(tmpSimulcast.season!!, tmpSimulcast.year!!) ?: tmpSimulcast if (episode.anime!!.simulcasts.isEmpty() || episode.anime!!.simulcasts.none { it.uuid == simulcast.uuid }) { episode.anime!!.simulcasts.add(simulcast) @@ -154,21 +57,20 @@ object EpisodeController : IController("/episodes") { println("POST $prefix/multiple") try { - val episodes = call.receive>().filter { !isExists("hash", it.hash!!) } + val episodes = call.receive>().filter { !episodeRepository.exists("hash", it.hash!!) } val savedEpisodes = mutableListOf() episodes.forEach { merge(it) - val savedEpisode = justSave(it) + val savedEpisode = episodeRepository.save(it) savedEpisodes.add(savedEpisode) ImageCache.cachingNetworkImage(savedEpisode.uuid, savedEpisode.image!!) } - call.respond(HttpStatusCode.Created, episodes) + call.respond(HttpStatusCode.Created, savedEpisodes) PluginManager.callEvent(EpisodesReleaseEvent(savedEpisodes)) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") + printError(call, e) } } } diff --git a/src/main/kotlin/fr/ziedelth/controllers/EpisodeTypeController.kt b/src/main/kotlin/fr/ziedelth/controllers/EpisodeTypeController.kt index 8685b2a..a264eac 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/EpisodeTypeController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/EpisodeTypeController.kt @@ -1,20 +1,30 @@ package fr.ziedelth.controllers import fr.ziedelth.entities.EpisodeType +import fr.ziedelth.entities.isNullOrNotValid +import fr.ziedelth.repositories.EpisodeTypeRepository import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -object EpisodeTypeController : IController("/episodetypes") { - fun Routing.getEpisodeTypes() { - route(prefix) { +class EpisodeTypeController(private val episodeTypeRepository: EpisodeTypeRepository) : + IController("/episodetypes") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { getAll() create() } } + fun Route.getAll() { + get { + println("GET $prefix") + call.respond(episodeTypeRepository.getAll()) + } + } + private fun Route.create() { post { println("POST $prefix") @@ -22,20 +32,19 @@ object EpisodeTypeController : IController("/episodetypes") { try { val episodeType = call.receive() - if (episodeType.name.isNullOrBlank()) { - call.respond(HttpStatusCode.BadRequest, "Missing parameters") + if (episodeType.isNullOrNotValid()) { + call.respond(HttpStatusCode.BadRequest, MISSING_PARAMETERS_MESSAGE_ERROR) return@post } - if (isExists("name", episodeType.name)) { + if (episodeTypeRepository.exists("name", episodeType.name)) { call.respond(HttpStatusCode.Conflict, "$entityName already exists") return@post } - call.respond(HttpStatusCode.Created, justSave(episodeType)) + call.respond(HttpStatusCode.Created, episodeTypeRepository.save(episodeType)) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") + printError(call, e) } } } diff --git a/src/main/kotlin/fr/ziedelth/controllers/GenreController.kt b/src/main/kotlin/fr/ziedelth/controllers/GenreController.kt index f148bd2..24cfcb4 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/GenreController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/GenreController.kt @@ -1,20 +1,29 @@ package fr.ziedelth.controllers import fr.ziedelth.entities.Genre +import fr.ziedelth.entities.isNullOrNotValid +import fr.ziedelth.repositories.GenreRepository import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -object GenreController : IController("/genres") { - fun Routing.getGenres() { - route(prefix) { +class GenreController(private val genreRepository: GenreRepository) : IController("/genres") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { getAll() create() } } + fun Route.getAll() { + get { + println("GET $prefix") + call.respond(genreRepository.getAll()) + } + } + private fun Route.create() { post { println("POST $prefix") @@ -22,20 +31,19 @@ object GenreController : IController("/genres") { try { val genre = call.receive() - if (genre.name.isNullOrBlank()) { - call.respond(HttpStatusCode.BadRequest, "Missing parameters") + if (genre.isNullOrNotValid()) { + call.respond(HttpStatusCode.BadRequest, MISSING_PARAMETERS_MESSAGE_ERROR) return@post } - if (isExists("name", genre.name)) { + if (genreRepository.exists("name", genre.name)) { call.respond(HttpStatusCode.Conflict, "$entityName already exists") return@post } - call.respond(HttpStatusCode.Created, justSave(genre)) + call.respond(HttpStatusCode.Created, genreRepository.save(genre)) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") + printError(call, e) } } } diff --git a/src/main/kotlin/fr/ziedelth/controllers/IController.kt b/src/main/kotlin/fr/ziedelth/controllers/IController.kt deleted file mode 100644 index 4b15407..0000000 --- a/src/main/kotlin/fr/ziedelth/controllers/IController.kt +++ /dev/null @@ -1,170 +0,0 @@ -package fr.ziedelth.controllers - -import fr.ziedelth.utils.Database -import fr.ziedelth.utils.ImageCache -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.response.* -import io.ktor.server.routing.* -import java.io.Serializable -import java.lang.reflect.ParameterizedType -import java.util.* - -open class IController(val prefix: String) { - val entityClass: Class = - (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class - val entityName: String = entityClass.simpleName - val uuidRequest: UUID = UUID.randomUUID() - - private fun getAll(): MutableList { - val session = Database.getSession() - - try { - val query = session.createQuery("FROM $entityName", entityClass) - return query.list() - } catch (e: Exception) { - e.printStackTrace() - println("Error while getting $prefix : ${e.message}") - throw e - } finally { - session.close() - } - } - - fun getAllBy(field: String, value: Any?): MutableList { - val session = Database.getSession() - - try { - val query = session.createQuery( - "FROM $entityName WHERE $field = :${field.filter { it.isLetterOrDigit() }.trim()}", - entityClass - ) - query.setParameter(field.filter { it.isLetterOrDigit() }.trim(), value) - return query.list() - } catch (e: Exception) { - e.printStackTrace() - println("Error while getting $prefix : ${e.message}") - throw e - } finally { - session.close() - } - } - - fun getBy(field: String, value: Any?): T? { - val session = Database.getSession() - - try { - val query = session.createQuery("FROM $entityName WHERE $field = :$field", entityClass) - query.maxResults = 1 - query.setParameter(field, value) - return query.uniqueResult() - } catch (e: Exception) { - e.printStackTrace() - println("Error while getting $prefix : ${e.message}") - throw e - } finally { - session.close() - } - } - - fun isExists(field: String, value: Any?): Boolean { - val session = Database.getSession() - val query = session.createQuery("SELECT COUNT(*) FROM $entityName WHERE $field = :$field", Long::class.java) - query.maxResults = 1 - query.setParameter(field, value) - val list = query.uniqueResult() - session.close() - return list > 0 - } - - fun contains(fieldList: String, searchValue: String?): Boolean { - val session = Database.getSession() - val query = session.createQuery( - "SELECT COUNT(*) FROM $entityName JOIN $fieldList l WHERE l = :search", - Long::class.java - ) - query.maxResults = 1 - query.setParameter("search", searchValue) - val list = query.uniqueResult() - session.close() - return list > 0 - } - - fun justSave(dtoIn: T): T { - val session = Database.getSession() - val transaction = session.beginTransaction() - - try { - val entity = session.merge(dtoIn) - session.persist(entity) - transaction.commit() - return entity - } catch (e: Exception) { - e.printStackTrace() - println("Error while saving $prefix : ${e.message}") - transaction.rollback() - throw e - } finally { - session.close() - } - } - - fun justUpdate(dtoIn: T) { - val session = Database.getSession() - val transaction = session.beginTransaction() - - try { - session.update(dtoIn) - transaction.commit() - } catch (e: Exception) { - e.printStackTrace() - println("Error while updating $prefix : ${e.message}") - transaction.rollback() - throw e - } finally { - session.close() - } - } - - fun Route.getAll() { - get { - println("GET $prefix") - - try { - call.respond(this@IController.getAll()) - } catch (e: Exception) { - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } - } - } - - fun Route.getAttachment() { - get("/attachment/{uuid}") { - val string = call.parameters["uuid"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val uuidRegex = - "^[0-9(a-f|A-F)]{8}-[0-9(a-f|A-F)]{4}-4[0-9(a-f|A-F)]{3}-[89ab][0-9(a-f|A-F)]{3}-[0-9(a-f|A-F)]{12}\$".toRegex() - - if (!uuidRegex.matches(string)) { - println("GET $prefix/attachment/$string : Invalid UUID") - return@get call.respond(HttpStatusCode.BadRequest) - } - - val uuid = UUID.fromString(string) - println("GET ${prefix}/attachment/$uuid") - - if (!ImageCache.contains(uuid)) { - println("Attachment $uuid not found") - call.respond(HttpStatusCode.NoContent) - return@get - } - - val image = ImageCache.get(uuid) ?: run { - println("Attachment $uuid not found") - return@get call.respond(HttpStatusCode.NoContent) - } - - println("Attachment $uuid found (${image.bytes.size} bytes)") - call.respondBytes(image.bytes, ContentType("image", "webp")) - } - } -} diff --git a/src/main/kotlin/fr/ziedelth/controllers/LangTypeController.kt b/src/main/kotlin/fr/ziedelth/controllers/LangTypeController.kt index aef913c..42822e3 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/LangTypeController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/LangTypeController.kt @@ -1,20 +1,29 @@ package fr.ziedelth.controllers import fr.ziedelth.entities.LangType +import fr.ziedelth.entities.isNullOrNotValid +import fr.ziedelth.repositories.LangTypeRepository import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -object LangTypeController : IController("/langtypes") { - fun Routing.getLangTypes() { - route(prefix) { +class LangTypeController(private val langTypeRepository: LangTypeRepository) : IController("/langtypes") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { getAll() create() } } + fun Route.getAll() { + get { + println("GET $prefix") + call.respond(langTypeRepository.getAll()) + } + } + private fun Route.create() { post { println("POST $prefix") @@ -22,20 +31,19 @@ object LangTypeController : IController("/langtypes") { try { val langType = call.receive() - if (langType.name.isNullOrBlank()) { - call.respond(HttpStatusCode.BadRequest, "Missing parameters") + if (langType.isNullOrNotValid()) { + call.respond(HttpStatusCode.BadRequest, MISSING_PARAMETERS_MESSAGE_ERROR) return@post } - if (isExists("name", langType.name)) { + if (langTypeRepository.exists("name", langType.name)) { call.respond(HttpStatusCode.Conflict, "$entityName already exists") return@post } - call.respond(HttpStatusCode.Created, justSave(langType)) + call.respond(HttpStatusCode.Created, langTypeRepository.save(langType)) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") + printError(call, e) } } } diff --git a/src/main/kotlin/fr/ziedelth/controllers/MangaController.kt b/src/main/kotlin/fr/ziedelth/controllers/MangaController.kt index 8870e4f..5b0d7e2 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/MangaController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/MangaController.kt @@ -1,164 +1,52 @@ package fr.ziedelth.controllers -import com.google.gson.Gson import fr.ziedelth.entities.Manga import fr.ziedelth.entities.isNullOrNotValid import fr.ziedelth.events.MangasReleaseEvent -import fr.ziedelth.utils.Database -import fr.ziedelth.utils.Decoder +import fr.ziedelth.repositories.AnimeRepository +import fr.ziedelth.repositories.MangaRepository +import fr.ziedelth.repositories.PlatformRepository import fr.ziedelth.utils.ImageCache -import fr.ziedelth.utils.RequestCache import fr.ziedelth.utils.plugins.PluginManager import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -import java.util.* -object MangaController : IController("/mangas") { - fun Routing.getMangas() { - route(prefix) { +class MangaController( + private val platformRepository: PlatformRepository, + private val animeRepository: AnimeRepository, + private val mangaRepository: MangaRepository, +) : IController("/mangas") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { search() - getWithPage() - getAnimeWithPage() - getWatchlistWithPage() + getWithPage(mangaRepository) + getAnimeWithPage(mangaRepository) + getWatchlistWithPage(mangaRepository) getAttachment() create() } } private fun Route.search() { - route("/country/{country}/search") { - get("/ean/{ean}") { - val country = call.parameters["country"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val ean = call.parameters["ean"]?.toLongOrNull() ?: return@get call.respond(HttpStatusCode.BadRequest) - println("GET ${prefix}/country/$country/search/ean/$ean") - val session = Database.getSession() - - try { - val query = session.createQuery( - "FROM $entityName WHERE anime.country.tag = :tag AND ean = :ean", - entityClass - ) - query.maxResults = 1 - query.setParameter("tag", country) - query.setParameter("ean", ean) - call.respond(query.uniqueResult() ?: return@get call.respond(HttpStatusCode.NotFound)) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } - } - } - } - - private fun Route.getWithPage() { - get("/country/{country}/page/{page}/limit/{limit}") { - val country = call.parameters["country"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val page = call.parameters["page"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - val limit = call.parameters["limit"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - if (page < 1 || limit < 1) return@get call.respond(HttpStatusCode.BadRequest) - if (limit > 30) return@get call.respond(HttpStatusCode.BadRequest) - println("GET $prefix/country/$country/page/$page/limit/$limit") - val request = RequestCache.get(uuidRequest, country, page, limit) - - if (request == null || request.isExpired()) { - val session = Database.getSession() - - try { - val query = session.createQuery( - "FROM $entityName WHERE anime.country.tag = :tag AND ean IS NOT NULL ORDER BY releaseDate DESC, anime.name", - entityClass - ) - query.setParameter("tag", country) - query.firstResult = (limit * page) - limit - query.maxResults = limit - request?.update(query.list()) ?: RequestCache.put( - uuidRequest, - country, - page, - limit, - value = query.list() - ) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } - } - - call.respond(RequestCache.get(uuidRequest, country, page, limit)?.value ?: HttpStatusCode.NotFound) - } - } - - private fun Route.getAnimeWithPage() { - get("/anime/{uuid}/page/{page}/limit/{limit}") { - val animeUuid = call.parameters["uuid"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val page = call.parameters["page"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - val limit = call.parameters["limit"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - if (page < 1 || limit < 1) return@get call.respond(HttpStatusCode.BadRequest) - if (limit > 30) return@get call.respond(HttpStatusCode.BadRequest) - println("GET ${prefix}/anime/$animeUuid/page/$page/limit/$limit") - val session = Database.getSession() - + get("/country/{country}/search/ean/{ean}") { try { - val query = session.createQuery( - "FROM $entityName WHERE anime.uuid = :uuid ORDER BY releaseDate DESC, anime.name", - entityClass - ) - query.setParameter("uuid", UUID.fromString(animeUuid)) - query.firstResult = (limit * page) - limit - query.maxResults = limit - call.respond(query.list() ?: HttpStatusCode.NotFound) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } - } - } - - private fun Route.getWatchlistWithPage() { - post("/watchlist/page/{page}/limit/{limit}") { - val watchlist = call.receive() - val page = call.parameters["page"]?.toInt() ?: return@post call.respond(HttpStatusCode.BadRequest) - val limit = call.parameters["limit"]?.toInt() ?: return@post call.respond(HttpStatusCode.BadRequest) - if (page < 1 || limit < 1) return@post call.respond(HttpStatusCode.BadRequest) - if (limit > 30) return@post call.respond(HttpStatusCode.BadRequest) - println("POST $prefix/watchlist/page/$page/limit/$limit") - val session = Database.getSession() - - try { - val dataFromGzip = - Gson().fromJson(Decoder.fromGzip(watchlist), Array::class.java).map { UUID.fromString(it) } - - val query = session.createQuery( - "FROM $entityName WHERE uuid IN :list ORDER BY releaseDate DESC, anime.name", - entityClass - ) - query.setParameter("list", dataFromGzip) - query.firstResult = (limit * page) - limit - query.maxResults = limit - call.respond(query.list()) + val country = call.parameters["country"]!! + val ean = call.parameters["ean"]!!.toLongOrNull() ?: return@get call.respond(HttpStatusCode.BadRequest) + println("GET ${prefix}/country/$country/search/ean/$ean") + call.respond(mangaRepository.getByEAN(country, ean) ?: return@get call.respond(HttpStatusCode.NotFound)) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() + printError(call, e) } } } - private fun merge(manga: Manga) { manga.platform = - PlatformController.getBy("uuid", manga.platform!!.uuid) ?: throw Exception("Platform not found") - manga.anime = AnimeController.getBy("uuid", manga.anime!!.uuid) ?: throw Exception("Anime not found") + platformRepository.find(manga.platform!!.uuid) ?: throw Exception("Platform not found") + manga.anime = animeRepository.find(manga.anime!!.uuid) ?: throw Exception("Anime not found") if (manga.isNullOrNotValid()) { throw Exception("Manga is not valid") @@ -170,21 +58,20 @@ object MangaController : IController("/mangas") { println("POST $prefix/multiple") try { - val mangas = call.receive>().filter { !isExists("hash", it.hash!!) } + val mangas = call.receive>().filter { !mangaRepository.exists("hash", it.hash!!) } val savedMangas = mutableListOf() mangas.forEach { merge(it) - val savedManga = justSave(it) + val savedManga = mangaRepository.save(it) savedMangas.add(savedManga) ImageCache.cachingNetworkImage(savedManga.uuid, savedManga.cover!!) } - call.respond(HttpStatusCode.Created, mangas) + call.respond(HttpStatusCode.Created, savedMangas) PluginManager.callEvent(MangasReleaseEvent(savedMangas)) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") + printError(call, e) } } } diff --git a/src/main/kotlin/fr/ziedelth/controllers/NewsController.kt b/src/main/kotlin/fr/ziedelth/controllers/NewsController.kt index f2861ed..9c3f9ac 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/NewsController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/NewsController.kt @@ -3,8 +3,9 @@ package fr.ziedelth.controllers import fr.ziedelth.entities.News import fr.ziedelth.entities.isNullOrNotValid import fr.ziedelth.events.NewsReleaseEvent -import fr.ziedelth.utils.Database -import fr.ziedelth.utils.RequestCache +import fr.ziedelth.repositories.CountryRepository +import fr.ziedelth.repositories.NewsRepository +import fr.ziedelth.repositories.PlatformRepository import fr.ziedelth.utils.plugins.PluginManager import io.ktor.http.* import io.ktor.server.application.* @@ -12,57 +13,21 @@ import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -object NewsController : IController("/news") { - fun Routing.getNews() { - route(prefix) { - getWithPage() +class NewsController( + private val countryRepository: CountryRepository, + private val platformRepository: PlatformRepository, + private val newsRepository: NewsRepository, +) : IController("/news") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { + getWithPage(newsRepository) create() } } - private fun Route.getWithPage() { - get("/country/{country}/page/{page}/limit/{limit}") { - val country = call.parameters["country"] ?: return@get call.respond(HttpStatusCode.BadRequest) - val page = call.parameters["page"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - val limit = call.parameters["limit"]?.toInt() ?: return@get call.respond(HttpStatusCode.BadRequest) - if (page < 1 || limit < 1) return@get call.respond(HttpStatusCode.BadRequest) - if (limit > 30) return@get call.respond(HttpStatusCode.BadRequest) - println("GET $prefix/country/$country/page/$page/limit/$limit") - val request = RequestCache.get(uuidRequest, country, page, limit) - - if (request == null || request.isExpired()) { - val session = Database.getSession() - - try { - val query = session.createQuery( - "FROM News WHERE country.tag = :tag ORDER BY releaseDate DESC", - News::class.java - ) - query.setParameter("tag", country) - query.firstResult = (limit * page) - limit - query.maxResults = limit - request?.update(query.list()) ?: RequestCache.put( - uuidRequest, - country, - page, - limit, - value = query.list() - ) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } - } - - call.respond(RequestCache.get(uuidRequest, country, page, limit)?.value ?: HttpStatusCode.NotFound) - } - } - private fun merge(news: News) { - news.platform = PlatformController.getBy("uuid", news.platform!!.uuid) ?: throw Exception("Platform not found") - news.country = CountryController.getBy("uuid", news.country!!.uuid) ?: throw Exception("Country not found") + news.platform = platformRepository.find(news.platform!!.uuid) ?: throw Exception("Platform not found") + news.country = countryRepository.find(news.country!!.uuid) ?: throw Exception("Country not found") if (news.isNullOrNotValid()) { throw Exception("News is not valid") @@ -74,19 +39,18 @@ object NewsController : IController("/news") { println("POST $prefix/multiple") try { - val news = call.receive>().filter { !isExists("hash", it.hash!!) } + val news = call.receive>().filter { !newsRepository.exists("hash", it.hash!!) } val savedNews = mutableListOf() news.forEach { merge(it) - savedNews.add(justSave(it)) + savedNews.add(newsRepository.save(it)) } - call.respond(HttpStatusCode.Created, news) + call.respond(HttpStatusCode.Created, savedNews) PluginManager.callEvent(NewsReleaseEvent(savedNews)) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") + printError(call, e) } } } diff --git a/src/main/kotlin/fr/ziedelth/controllers/PlatformController.kt b/src/main/kotlin/fr/ziedelth/controllers/PlatformController.kt index 75153b8..c5a51c9 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/PlatformController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/PlatformController.kt @@ -1,21 +1,30 @@ package fr.ziedelth.controllers import fr.ziedelth.entities.Platform +import fr.ziedelth.entities.isNullOrNotValid +import fr.ziedelth.repositories.PlatformRepository import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -object PlatformController : IController("/platforms") { - fun Routing.getPlatforms() { - route(prefix) { +class PlatformController(private val platformRepository: PlatformRepository) : IController("/platforms") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { getAll() getAttachment() create() } } + fun Route.getAll() { + get { + println("GET $prefix") + call.respond(platformRepository.getAll()) + } + } + private fun Route.create() { post { println("POST $prefix") @@ -23,20 +32,19 @@ object PlatformController : IController("/platforms") { try { val platform = call.receive() - if (platform.name.isNullOrBlank() || platform.url.isNullOrBlank() || platform.image.isNullOrBlank()) { - call.respond(HttpStatusCode.BadRequest, "Missing parameters") + if (platform.isNullOrNotValid()) { + call.respond(HttpStatusCode.BadRequest, MISSING_PARAMETERS_MESSAGE_ERROR) return@post } - if (isExists("name", platform.name)) { + if (platformRepository.exists("name", platform.name)) { call.respond(HttpStatusCode.Conflict, "$entityName already exists") return@post } - call.respond(HttpStatusCode.Created, justSave(platform)) + call.respond(HttpStatusCode.Created, platformRepository.save(platform)) } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") + printError(call, e) } } } diff --git a/src/main/kotlin/fr/ziedelth/controllers/SimulcastController.kt b/src/main/kotlin/fr/ziedelth/controllers/SimulcastController.kt index 2d52625..3a71116 100644 --- a/src/main/kotlin/fr/ziedelth/controllers/SimulcastController.kt +++ b/src/main/kotlin/fr/ziedelth/controllers/SimulcastController.kt @@ -1,52 +1,20 @@ package fr.ziedelth.controllers import fr.ziedelth.entities.Simulcast -import fr.ziedelth.utils.Database -import io.ktor.http.* +import fr.ziedelth.repositories.SimulcastRepository import io.ktor.server.application.* import io.ktor.server.response.* import io.ktor.server.routing.* -object SimulcastController : IController("/simulcasts") { - fun Routing.getSimulcasts() { - route(prefix) { +class SimulcastController(private val simulcastRepository: SimulcastRepository) : + IController("/simulcasts") { + fun getRoutes(routing: Routing) { + routing.route(prefix) { get("/country/{country}") { - val country = call.parameters["country"] ?: return@get call.respond(HttpStatusCode.BadRequest) + val country = call.parameters["country"]!! println("GET $prefix/country/$country") - val session = Database.getSession() - - try { - val query = session.createQuery( - "SELECT DISTINCT simulcasts FROM Anime WHERE country.tag = :tag", - Simulcast::class.java - ) - query.setParameter("tag", country) - call.respond(query.list()) - } catch (e: Exception) { - e.printStackTrace() - call.respond(HttpStatusCode.InternalServerError, e.message ?: "Unknown error") - } finally { - session.close() - } + call.respond(simulcastRepository.getAll(country)) } } } - - fun getBy(simulcast: Simulcast): Simulcast { - val session = Database.getSession() - - try { - val query = - session.createQuery("FROM Simulcast WHERE season = :season AND year = :year", Simulcast::class.java) - query.setParameter("season", simulcast.season) - query.setParameter("year", simulcast.year) - return query.list().firstOrNull() ?: simulcast - } catch (e: Exception) { - e.printStackTrace() - println("Error while getting $prefix : ${e.message}") - throw e - } finally { - session.close() - } - } } diff --git a/src/main/kotlin/fr/ziedelth/entities/Anime.kt b/src/main/kotlin/fr/ziedelth/entities/Anime.kt index a786a39..593287d 100644 --- a/src/main/kotlin/fr/ziedelth/entities/Anime.kt +++ b/src/main/kotlin/fr/ziedelth/entities/Anime.kt @@ -3,7 +3,6 @@ package fr.ziedelth.entities import fr.ziedelth.utils.DATE_FORMAT_REGEX import fr.ziedelth.utils.toISO8601 import jakarta.persistence.* -import org.hibernate.Hibernate import org.hibernate.annotations.LazyCollection import org.hibernate.annotations.LazyCollectionOption import java.io.Serializable @@ -12,11 +11,12 @@ import java.util.* fun Anime?.isNullOrNotValid() = this == null || this.isNotValid() @Entity -data class Anime( +@Table(name = "anime") +class Anime( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), - @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) + @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.MERGE, CascadeType.PERSIST]) @JoinColumn( name = "country_uuid", nullable = false, @@ -39,7 +39,7 @@ data class Anime( foreignKey = ForeignKey(foreignKeyDefinition = "FOREIGN KEY (anime_uuid) REFERENCES anime (uuid) ON DELETE CASCADE") ) val hashes: MutableSet = mutableSetOf(), - @ManyToMany(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) + @ManyToMany(fetch = FetchType.EAGER, cascade = [CascadeType.MERGE, CascadeType.PERSIST]) @JoinTable( name = "anime_genre", joinColumns = [ @@ -56,7 +56,7 @@ data class Anime( ] ) val genres: MutableSet = mutableSetOf(), - @ManyToMany(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) + @ManyToMany(fetch = FetchType.EAGER, cascade = [CascadeType.MERGE, CascadeType.PERSIST]) @JoinTable( name = "anime_simulcast", joinColumns = [ @@ -74,27 +74,12 @@ data class Anime( ) val simulcasts: MutableSet = mutableSetOf(), ) : Serializable { - fun hash(): String? = name?.lowercase()?.filter { it.isLetterOrDigit() || it.isWhitespace() || it == '-' }?.trim() - ?.replace("\\s+".toRegex(), "-")?.replace("--", "-") + fun hash(): String = name!!.lowercase().filter { it.isLetterOrDigit() || it.isWhitespace() || it == '-' }.trim() + .replace("\\s+".toRegex(), "-").replace("--", "-") fun isNotValid(): Boolean = country.isNullOrNotValid() || name.isNullOrBlank() || ( releaseDate.isBlank() || !releaseDate.matches( DATE_FORMAT_REGEX ) ) || image.isNullOrBlank() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as Anime - - return uuid != null && uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , country = $country , name = $name , releaseDate = $releaseDate , image = $image , description = $description , hashes = $hashes )" - } } diff --git a/src/main/kotlin/fr/ziedelth/entities/Country.kt b/src/main/kotlin/fr/ziedelth/entities/Country.kt index 0bea844..c66b61f 100644 --- a/src/main/kotlin/fr/ziedelth/entities/Country.kt +++ b/src/main/kotlin/fr/ziedelth/entities/Country.kt @@ -1,17 +1,14 @@ package fr.ziedelth.entities -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.Id -import org.hibernate.Hibernate +import jakarta.persistence.* import java.io.Serializable import java.util.* fun Country?.isNullOrNotValid() = this == null || this.isNotValid() @Entity -data class Country( +@Table(name = "country") +class Country( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), @@ -21,19 +18,4 @@ data class Country( val name: String? = null ) : Serializable { fun isNotValid(): Boolean = tag.isNullOrBlank() || name.isNullOrBlank() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as Country - - return uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , tag = $tag , name = $name )" - } } diff --git a/src/main/kotlin/fr/ziedelth/entities/Device.kt b/src/main/kotlin/fr/ziedelth/entities/Device.kt deleted file mode 100644 index 4c17bfe..0000000 --- a/src/main/kotlin/fr/ziedelth/entities/Device.kt +++ /dev/null @@ -1,44 +0,0 @@ -package fr.ziedelth.entities - -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.Id -import org.hibernate.Hibernate -import java.io.Serializable -import java.util.* - -fun Device?.isNullOrNotValid() = this == null || this.isNotValid() - -@Entity -data class Device( - @Id - @GeneratedValue - val uuid: UUID = UUID.randomUUID(), - @Column(nullable = false, unique = true) - var name: String? = null, - @Column(nullable = false) - var os: String? = null, - @Column(nullable = false) - var model: String? = null, - @Column(nullable = false, name = "created_at") - val createdAt: Calendar = Calendar.getInstance(), - @Column(nullable = false, name = "updated_at") - var updatedAt: Calendar = Calendar.getInstance(), -) : Serializable { - fun isNotValid(): Boolean = name.isNullOrBlank() || os.isNullOrBlank() || model.isNullOrBlank() - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as Device - - return uuid != null && uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , name = $name , os = $os , model = $model , createdAt = $createdAt , updatedAt = $updatedAt )" - } -} diff --git a/src/main/kotlin/fr/ziedelth/entities/Episode.kt b/src/main/kotlin/fr/ziedelth/entities/Episode.kt index 25478ea..597ce4d 100644 --- a/src/main/kotlin/fr/ziedelth/entities/Episode.kt +++ b/src/main/kotlin/fr/ziedelth/entities/Episode.kt @@ -3,24 +3,18 @@ package fr.ziedelth.entities import fr.ziedelth.utils.DATE_FORMAT_REGEX import fr.ziedelth.utils.toISO8601 import jakarta.persistence.* -import org.hibernate.Hibernate import java.io.Serializable import java.util.* fun Episode?.isNullOrNotValid() = this == null || this.isNotValid() @Entity -data class Episode( +@Table(name = "episode") +class Episode( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), - @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) - @JoinColumn( - name = "platform_uuid", - nullable = false, - foreignKey = ForeignKey(foreignKeyDefinition = "FOREIGN KEY (platform_uuid) REFERENCES platform(uuid) ON DELETE CASCADE") - ) - var platform: Platform? = null, + platform: Platform? = null, @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) @JoinColumn( name = "anime_uuid", @@ -58,24 +52,25 @@ data class Episode( val image: String? = null, @Column(nullable = false) val duration: Long = -1 -) : Serializable { +) : Platformeable(platform), Serializable { fun isNotValid(): Boolean = platform.isNullOrNotValid() || anime.isNullOrNotValid() || episodeType.isNullOrNotValid() || langType.isNullOrNotValid() || hash.isNullOrBlank() || ( releaseDate.isBlank() || !releaseDate.matches(DATE_FORMAT_REGEX) ) || season == null || number == null || url.isNullOrBlank() || image.isNullOrBlank() - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as Episode - - return uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , platform = $platform , anime = $anime , episodeType = $episodeType , langType = $langType , hash = $hash , releaseDate = $releaseDate , season = $season , number = $number , title = $title , url = $url , image = $image , duration = $duration )" - } + fun copy(anime: Anime? = this.anime) = Episode( + this.uuid, + platform, + anime, + episodeType, + langType, + hash, + releaseDate, + season, + number, + title, + url, + image, + duration + ) } diff --git a/src/main/kotlin/fr/ziedelth/entities/EpisodeType.kt b/src/main/kotlin/fr/ziedelth/entities/EpisodeType.kt index ef576d8..b121cc6 100644 --- a/src/main/kotlin/fr/ziedelth/entities/EpisodeType.kt +++ b/src/main/kotlin/fr/ziedelth/entities/EpisodeType.kt @@ -1,17 +1,14 @@ package fr.ziedelth.entities -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.Id -import org.hibernate.Hibernate +import jakarta.persistence.* import java.io.Serializable import java.util.* fun EpisodeType?.isNullOrNotValid() = this == null || this.isNotValid() @Entity -data class EpisodeType( +@Table(name = "episodetype") +class EpisodeType( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), @@ -19,19 +16,4 @@ data class EpisodeType( val name: String? = null ) : Serializable { fun isNotValid(): Boolean = name.isNullOrBlank() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as EpisodeType - - return uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , name = $name )" - } } diff --git a/src/main/kotlin/fr/ziedelth/entities/Genre.kt b/src/main/kotlin/fr/ziedelth/entities/Genre.kt index c4eb7c3..d2d1632 100644 --- a/src/main/kotlin/fr/ziedelth/entities/Genre.kt +++ b/src/main/kotlin/fr/ziedelth/entities/Genre.kt @@ -1,17 +1,14 @@ package fr.ziedelth.entities -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.Id -import org.hibernate.Hibernate +import jakarta.persistence.* import java.io.Serializable import java.util.* fun Genre?.isNullOrNotValid() = this == null || this.isNotValid() @Entity -data class Genre( +@Table(name = "genre") +class Genre( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), @@ -19,19 +16,4 @@ data class Genre( val name: String? = null ) : Serializable { fun isNotValid(): Boolean = name.isNullOrBlank() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as Genre - - return uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , name = $name )" - } } diff --git a/src/main/kotlin/fr/ziedelth/entities/LangType.kt b/src/main/kotlin/fr/ziedelth/entities/LangType.kt index f863feb..57f39cc 100644 --- a/src/main/kotlin/fr/ziedelth/entities/LangType.kt +++ b/src/main/kotlin/fr/ziedelth/entities/LangType.kt @@ -1,17 +1,14 @@ package fr.ziedelth.entities -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.Id -import org.hibernate.Hibernate +import jakarta.persistence.* import java.io.Serializable import java.util.* fun LangType?.isNullOrNotValid() = this == null || this.isNotValid() @Entity -data class LangType( +@Table(name = "langtype") +class LangType( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), @@ -19,19 +16,4 @@ data class LangType( val name: String? = null ) : Serializable { fun isNotValid(): Boolean = name.isNullOrBlank() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as LangType - - return uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , name = $name )" - } } diff --git a/src/main/kotlin/fr/ziedelth/entities/Manga.kt b/src/main/kotlin/fr/ziedelth/entities/Manga.kt index 400eba8..13b4c69 100644 --- a/src/main/kotlin/fr/ziedelth/entities/Manga.kt +++ b/src/main/kotlin/fr/ziedelth/entities/Manga.kt @@ -3,24 +3,18 @@ package fr.ziedelth.entities import fr.ziedelth.utils.DATE_FORMAT_REGEX import fr.ziedelth.utils.toISO8601 import jakarta.persistence.* -import org.hibernate.Hibernate import java.io.Serializable import java.util.* fun Manga?.isNullOrNotValid() = this == null || this.isNotValid() @Entity -data class Manga( +@Table(name = "manga") +class Manga( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), - @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) - @JoinColumn( - name = "platform_uuid", - nullable = false, - foreignKey = ForeignKey(foreignKeyDefinition = "FOREIGN KEY (platform_uuid) REFERENCES platform(uuid) ON DELETE CASCADE") - ) - var platform: Platform? = null, + platform: Platform? = null, @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) @JoinColumn( name = "anime_uuid", @@ -46,24 +40,24 @@ data class Manga( val age: Int? = null, @Column(nullable = true) val price: Double? = null -) : Serializable { +) : Platformeable(platform), Serializable { fun isNotValid(): Boolean = platform.isNullOrNotValid() || anime.isNullOrNotValid() || hash.isNullOrBlank() || (releaseDate.isBlank() || !releaseDate.matches( DATE_FORMAT_REGEX )) || url.isNullOrBlank() || cover.isNullOrBlank() || editor.isNullOrBlank() - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as Manga - - return uuid != null && uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , platform = $platform , anime = $anime , hash = $hash , releaseDate = $releaseDate , url = $url , cover = $cover , editor = $editor , ref = $ref , ean = $ean , age = $age , price = $price )" - } + fun copy(anime: Anime? = this.anime) = Manga( + uuid = uuid, + platform = platform, + anime = anime, + hash = hash, + releaseDate = releaseDate, + url = url, + cover = cover, + editor = editor, + ref = ref, + ean = ean, + age = age, + price = price + ) } diff --git a/src/main/kotlin/fr/ziedelth/entities/News.kt b/src/main/kotlin/fr/ziedelth/entities/News.kt index c962ca5..38763ce 100644 --- a/src/main/kotlin/fr/ziedelth/entities/News.kt +++ b/src/main/kotlin/fr/ziedelth/entities/News.kt @@ -3,24 +3,18 @@ package fr.ziedelth.entities import fr.ziedelth.utils.DATE_FORMAT_REGEX import fr.ziedelth.utils.toISO8601 import jakarta.persistence.* -import org.hibernate.Hibernate import java.io.Serializable import java.util.* fun News?.isNullOrNotValid() = this == null || this.isNotValid() @Entity -data class News( +@Table(name = "news") +class News( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), - @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) - @JoinColumn( - name = "platform_uuid", - nullable = false, - foreignKey = ForeignKey(foreignKeyDefinition = "FOREIGN KEY (platform_uuid) REFERENCES platform(uuid) ON DELETE CASCADE") - ) - var platform: Platform? = null, + platform: Platform? = null, @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) @JoinColumn( name = "country_uuid", @@ -38,24 +32,9 @@ data class News( val description: String? = null, @Column(nullable = false, columnDefinition = "TEXT") val url: String? = null -) : Serializable { +) : Platformeable(platform), Serializable { fun isNotValid(): Boolean = platform.isNullOrNotValid() || country.isNullOrNotValid() || hash.isNullOrBlank() || ( releaseDate.isBlank() || !releaseDate.matches(DATE_FORMAT_REGEX) ) || title.isNullOrBlank() || description.isNullOrBlank() || url.isNullOrBlank() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as News - - return uuid != null && uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , platform = $platform , country = $country , hash = $hash , releaseDate = $releaseDate , title = $title , description = $description , url = $url )" - } } diff --git a/src/main/kotlin/fr/ziedelth/entities/Platform.kt b/src/main/kotlin/fr/ziedelth/entities/Platform.kt index 8f7006b..c0cca79 100644 --- a/src/main/kotlin/fr/ziedelth/entities/Platform.kt +++ b/src/main/kotlin/fr/ziedelth/entities/Platform.kt @@ -1,17 +1,14 @@ package fr.ziedelth.entities -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.Id -import org.hibernate.Hibernate +import jakarta.persistence.* import java.io.Serializable import java.util.* fun Platform?.isNullOrNotValid() = this == null || this.isNotValid() @Entity -data class Platform( +@Table(name = "platform") +class Platform( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), @@ -23,19 +20,4 @@ data class Platform( val image: String? = null ) : Serializable { fun isNotValid(): Boolean = name.isNullOrBlank() || url.isNullOrBlank() || image.isNullOrBlank() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as Platform - - return uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , name = $name , url = $url , image = $image )" - } } diff --git a/src/main/kotlin/fr/ziedelth/entities/Platformeable.kt b/src/main/kotlin/fr/ziedelth/entities/Platformeable.kt new file mode 100644 index 0000000..c3e0730 --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/entities/Platformeable.kt @@ -0,0 +1,13 @@ +package fr.ziedelth.entities + +import jakarta.persistence.* + +open class Platformeable( + @ManyToOne(fetch = FetchType.EAGER, cascade = [CascadeType.ALL]) + @JoinColumn( + name = "platform_uuid", + nullable = false, + foreignKey = ForeignKey(foreignKeyDefinition = "FOREIGN KEY (platform_uuid) REFERENCES platform(uuid) ON DELETE CASCADE") + ) + var platform: Platform? = null, +) \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/entities/Simulcast.kt b/src/main/kotlin/fr/ziedelth/entities/Simulcast.kt index 86304ca..5bb89d6 100644 --- a/src/main/kotlin/fr/ziedelth/entities/Simulcast.kt +++ b/src/main/kotlin/fr/ziedelth/entities/Simulcast.kt @@ -1,40 +1,20 @@ package fr.ziedelth.entities import jakarta.persistence.* -import org.hibernate.Hibernate import java.io.Serializable import java.util.* -fun Simulcast?.isNullOrNotValid() = this == null || this.isNotValid() - @Entity -@Table(uniqueConstraints = [UniqueConstraint(columnNames = arrayOf("season", "year"))]) -data class Simulcast( +@Table(name = "simulcast", uniqueConstraints = [UniqueConstraint(columnNames = arrayOf("season", "year"))]) +class Simulcast( @Id @GeneratedValue val uuid: UUID = UUID.randomUUID(), @Column(nullable = false) val season: String? = null, - @Column(nullable = false) + @Column(nullable = false, name = "\"year\"") val year: Int? = null ) : Serializable { - fun isNotValid(): Boolean = season.isNullOrBlank() || year == null - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as Simulcast - - return uuid != null && uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , season = $season , year = $year )" - } - companion object { fun getSimulcast(year: Int, month: Int): Simulcast { val seasons = arrayOf("WINTER", "SPRING", "SUMMER", "AUTUMN") diff --git a/src/main/kotlin/fr/ziedelth/entities/device_redirections/DeviceEpisodeRedirection.kt b/src/main/kotlin/fr/ziedelth/entities/device_redirections/DeviceEpisodeRedirection.kt deleted file mode 100644 index b743f76..0000000 --- a/src/main/kotlin/fr/ziedelth/entities/device_redirections/DeviceEpisodeRedirection.kt +++ /dev/null @@ -1,37 +0,0 @@ -package fr.ziedelth.entities.device_redirections - -import fr.ziedelth.entities.Device -import fr.ziedelth.entities.Episode -import jakarta.persistence.* -import org.hibernate.Hibernate -import java.io.Serializable -import java.util.* - -@Entity -@Table(name = "device_episode_redirection") -data class DeviceEpisodeRedirection( - @Id - @GeneratedValue - val uuid: UUID = UUID.randomUUID(), - @Column(nullable = false) - var timestamp: Calendar = Calendar.getInstance(), - @ManyToOne(optional = false) - val device: Device? = null, - @ManyToOne(optional = false) - val episode: Episode? = null, -) : Serializable { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as DeviceEpisodeRedirection - - return uuid != null && uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid )" - } -} diff --git a/src/main/kotlin/fr/ziedelth/entities/device_redirections/DeviceMangaRedirection.kt b/src/main/kotlin/fr/ziedelth/entities/device_redirections/DeviceMangaRedirection.kt deleted file mode 100644 index 8104ca2..0000000 --- a/src/main/kotlin/fr/ziedelth/entities/device_redirections/DeviceMangaRedirection.kt +++ /dev/null @@ -1,37 +0,0 @@ -package fr.ziedelth.entities.device_redirections - -import fr.ziedelth.entities.Device -import fr.ziedelth.entities.Manga -import jakarta.persistence.* -import org.hibernate.Hibernate -import java.io.Serializable -import java.util.* - -@Entity -@Table(name = "device_manga_redirection") -data class DeviceMangaRedirection( - @Id - @GeneratedValue - val uuid: UUID = UUID.randomUUID(), - @Column(nullable = false) - var timestamp: Calendar = Calendar.getInstance(), - @ManyToOne(optional = false) - val device: Device? = null, - @ManyToOne(optional = false) - val manga: Manga? = null, -) : Serializable { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as DeviceMangaRedirection - - return uuid != null && uuid == other.uuid - } - - override fun hashCode(): Int = javaClass.hashCode() - - @Override - override fun toString(): String { - return this::class.simpleName + "(uuid = $uuid , timestamp = $timestamp , device = $device , manga = $manga )" - } -} diff --git a/src/main/kotlin/fr/ziedelth/plugins/HTTP.kt b/src/main/kotlin/fr/ziedelth/plugins/HTTP.kt index 855a1f0..f38c65c 100644 --- a/src/main/kotlin/fr/ziedelth/plugins/HTTP.kt +++ b/src/main/kotlin/fr/ziedelth/plugins/HTTP.kt @@ -1,11 +1,11 @@ package fr.ziedelth.plugins import io.ktor.http.* +import io.ktor.serialization.gson.* import io.ktor.server.application.* import io.ktor.server.plugins.compression.* +import io.ktor.server.plugins.contentnegotiation.* import io.ktor.server.plugins.cors.routing.* -import io.ktor.server.websocket.* -import java.time.Duration fun Application.configureHTTP() { install(Compression) { @@ -17,16 +17,22 @@ fun Application.configureHTTP() { minimumSize(1024) // condition } } + install(CORS) { HttpMethod.DefaultMethods.forEach { allowMethod(it) } allowHeader(HttpHeaders.Authorization) anyHost() } - install(WebSockets) { - pingPeriod = Duration.ofSeconds(15) - timeout = Duration.ofSeconds(15) - maxFrameSize = Long.MAX_VALUE - masking = false + install(ContentNegotiation) { + gson { + } } + +// install(WebSockets) { +// pingPeriod = Duration.ofSeconds(15) +// timeout = Duration.ofSeconds(15) +// maxFrameSize = Long.MAX_VALUE +// masking = false +// } } diff --git a/src/main/kotlin/fr/ziedelth/plugins/Routing.kt b/src/main/kotlin/fr/ziedelth/plugins/Routing.kt index 4a064e1..09e083d 100644 --- a/src/main/kotlin/fr/ziedelth/plugins/Routing.kt +++ b/src/main/kotlin/fr/ziedelth/plugins/Routing.kt @@ -1,42 +1,39 @@ package fr.ziedelth.plugins -import fr.ziedelth.controllers.AnimeController.getAnimes -import fr.ziedelth.controllers.CountryController.getCountries -import fr.ziedelth.controllers.DeviceController.getDevices -import fr.ziedelth.controllers.DeviceRedirectionController.getRedirection -import fr.ziedelth.controllers.DiaryController.getDiary -import fr.ziedelth.controllers.EpisodeController.getEpisodes -import fr.ziedelth.controllers.EpisodeTypeController.getEpisodeTypes -import fr.ziedelth.controllers.GenreController.getGenres -import fr.ziedelth.controllers.LangTypeController.getLangTypes -import fr.ziedelth.controllers.MangaController.getMangas -import fr.ziedelth.controllers.NewsController.getNews -import fr.ziedelth.controllers.PlatformController.getPlatforms -import fr.ziedelth.controllers.SimulcastController.getSimulcasts -import io.ktor.serialization.gson.* +import fr.ziedelth.controllers.* +import fr.ziedelth.repositories.* import io.ktor.server.application.* -import io.ktor.server.plugins.contentnegotiation.* import io.ktor.server.routing.* fun Application.configureRouting() { - install(ContentNegotiation) { - gson { - } - } - routing { - getCountries() - getPlatforms() - getSimulcasts() - getGenres() - getAnimes() - getEpisodeTypes() - getLangTypes() - getEpisodes() - getNews() - getMangas() - getDevices() - getRedirection() - getDiary() + val countryRepository = CountryRepository() + val platformRepository = PlatformRepository() + val simulcastRepository = SimulcastRepository() + val genreRepository = GenreRepository() + val animeRepository = AnimeRepository() + val episodeTypeRepository = EpisodeTypeRepository() + val langTypeRepository = LangTypeRepository() + val episodeRepository = EpisodeRepository() + val mangaRepository = MangaRepository() + val newsRepository = NewsRepository() + + CountryController(countryRepository).getRoutes(this) + PlatformController(platformRepository).getRoutes(this) + SimulcastController(simulcastRepository).getRoutes(this) + GenreController(genreRepository).getRoutes(this) + AnimeController(countryRepository, animeRepository, episodeRepository, mangaRepository).getRoutes(this) + EpisodeTypeController(episodeTypeRepository).getRoutes(this) + LangTypeController(langTypeRepository).getRoutes(this) + EpisodeController( + platformRepository, + animeRepository, + simulcastRepository, + episodeTypeRepository, + langTypeRepository, + episodeRepository + ).getRoutes(this) + NewsController(countryRepository, platformRepository, newsRepository).getRoutes(this) + MangaController(platformRepository, animeRepository, mangaRepository).getRoutes(this) } } diff --git a/src/main/kotlin/fr/ziedelth/repositories/AbstractRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/AbstractRepository.kt new file mode 100644 index 0000000..f8e86ea --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/AbstractRepository.kt @@ -0,0 +1,133 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.utils.Database +import org.hibernate.Session +import java.lang.reflect.ParameterizedType +import java.util.* + +open class AbstractRepository(val getSession: () -> Session = { Database.getSession() }) { + private val entityClass: Class = + (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class + private val entityName: String = entityClass.simpleName + + fun find(uuid: UUID): T? { + val session = getSession.invoke() + val entity = session.find(entityClass, uuid) + session.close() + return entity + } + + fun exists(field: String, value: Any?): Boolean { + val session = getSession.invoke() + val query = session.createQuery("SELECT uuid FROM $entityName WHERE $field = :$field", UUID::class.java) + query.maxResults = 1 + query.setParameter(field, value) + val uuid = query.uniqueResult() + session.close() + return uuid != null + } + + fun findAll(uuids: List): List { + val session = getSession.invoke() + val entities = session.createQuery("FROM $entityName WHERE uuid IN :uuids", entityClass) + .setParameter("uuids", uuids) + .resultList + session.close() + return entities + } + + fun getAll(): MutableList { + val session = getSession.invoke() + val list = session.createQuery("FROM $entityName", entityClass).list() + session.close() + return list + } + + fun getAllBy(field: String, value: Any?): MutableList { + val session = getSession.invoke() + val query = session.createQuery("FROM $entityName WHERE $field = :value", entityClass) + query.setParameter("value", value) + val list = query.list() + session.close() + return list + } + + fun save(entity: T): T { + val session = getSession.invoke() + val transaction = session.beginTransaction() + + try { + val mergedEntity = session.merge(entity) + session.persist(mergedEntity) + transaction.commit() + return mergedEntity + } catch (e: Exception) { + transaction.rollback() + throw e + } finally { + session.close() + } + } + + fun saveAll(entities: List): List { + val session = getSession.invoke() + val transaction = session.beginTransaction() + + try { + val mergedEntities = entities.map { + val mergedEntity = session.merge(it) + session.persist(mergedEntity) + mergedEntity + } + + transaction.commit() + return mergedEntities + } catch (e: Exception) { + transaction.rollback() + throw e + } finally { + session.close() + } + } + + fun delete(entity: T) { + val session = getSession.invoke() + val transaction = session.beginTransaction() + + try { + session.remove(session.merge(entity)) + transaction.commit() + } catch (e: Exception) { + transaction.rollback() + throw e + } finally { + session.close() + } + } + + fun deleteAll(entities: List) { + val session = getSession.invoke() + val transaction = session.beginTransaction() + + try { + entities.forEach { session.remove(session.merge(it)) } + transaction.commit() + } catch (e: Exception) { + transaction.rollback() + throw e + } finally { + session.close() + } + } + + fun getByPage(page: Int, limit: Int, queryRaw: String, vararg pair: Pair): List { + val session = getSession.invoke() + val query = session.createQuery(queryRaw, entityClass) + pair.forEach { query.setParameter(it.first, it.second) } + query.firstResult = (limit * page) - limit + query.maxResults = limit + val list = query.list() + session.close() + return list + } +} diff --git a/src/main/kotlin/fr/ziedelth/repositories/AnimeRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/AnimeRepository.kt new file mode 100644 index 0000000..72433f2 --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/AnimeRepository.kt @@ -0,0 +1,90 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.Anime +import fr.ziedelth.utils.Database +import org.hibernate.Session +import java.util.* + +class AnimeRepository(session: () -> Session = { Database.getSession() }) : AbstractRepository(session), + IPageRepository { + fun findByHash(tag: String, hash: String): UUID? { + val session = getSession.invoke() + val query = session.createQuery( + "SELECT a.uuid FROM Anime a JOIN a.hashes h WHERE a.country.tag = :tag AND h = :hash", + UUID::class.java + ) + query.maxResults = 1 + query.setParameter("tag", tag) + query.setParameter("hash", hash) + val uuid = query.uniqueResult() + session.close() + return uuid + } + + fun findOneByName(tag: String, name: String): Anime? { + val session = getSession.invoke() + val query = session.createQuery( + "FROM Anime WHERE country.tag = :tag AND LOWER(name) = :name", + Anime::class.java + ) + query.maxResults = 1 + query.setParameter("tag", tag) + query.setParameter("name", name.lowercase()) + val uuid = query.uniqueResult() + session.close() + return uuid + } + + fun findByName(tag: String, name: String): List { + val session = getSession.invoke() + val query = session.createQuery( + "SELECT DISTINCT anime FROM Episode e WHERE e.anime.country.tag = :tag AND LOWER(e.anime.name) LIKE CONCAT('%', :name, '%') ORDER BY e.anime.name", + Anime::class.java + ) + query.setParameter("tag", tag) + query.setParameter("name", name.lowercase()) + val list = query.list() + session.close() + return list + } + + fun getByPage(tag: String, simulcast: UUID, page: Int, limit: Int): List { + return super.getByPage( + page, + limit, + "FROM Anime a JOIN a.simulcasts s WHERE a.country.tag = :tag AND s.uuid = :simulcast ORDER BY a.name", + "tag" to tag, + "simulcast" to simulcast + ) + } + + override fun getByPageWithList(list: List, page: Int, limit: Int): List { + return super.getByPage( + page, + limit, + "FROM Anime WHERE uuid IN :list ORDER BY name", + "list" to list + ) + } + + fun getDiary(tag: String, day: Int): List { + val session = getSession.invoke() + val query = session.createQuery( + "SELECT DISTINCT anime FROM Episode episode WHERE episode.anime.country.tag = :tag AND current_date - to_date(episode.releaseDate, 'YYYY-MM-DDTHH:MI:SS') <= 7 AND FUNCTION('date_part', 'dow', to_date(episode.releaseDate, 'YYYY-MM-DDTHH:MI:SS')) = :day ORDER BY episode.anime.name ASC", + Anime::class.java + ) + query.setParameter("tag", tag) + query.setParameter("day", day) + val list = query.list() + session.close() + return list ?: emptyList() + } + + override fun getByPage(tag: String, page: Int, limit: Int): List { + TODO("Not yet implemented") + } + + override fun getByPageWithAnime(uuid: UUID, page: Int, limit: Int): List { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/CountryRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/CountryRepository.kt new file mode 100644 index 0000000..00d9a6b --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/CountryRepository.kt @@ -0,0 +1,7 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.Country +import fr.ziedelth.utils.Database +import org.hibernate.Session + +class CountryRepository(session: () -> Session = { Database.getSession() }) : AbstractRepository(session) \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/EpisodeRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/EpisodeRepository.kt new file mode 100644 index 0000000..8b3b61c --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/EpisodeRepository.kt @@ -0,0 +1,36 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.Episode +import fr.ziedelth.utils.Database +import org.hibernate.Session +import java.util.* + +class EpisodeRepository(session: () -> Session = { Database.getSession() }) : AbstractRepository(session), + IPageRepository { + override fun getByPage(tag: String, page: Int, limit: Int): List { + return super.getByPage( + page, + limit, + "FROM Episode WHERE anime.country.tag = :tag ORDER BY releaseDate DESC, anime.name, season DESC, number DESC, episodeType.name, langType.name", + "tag" to tag + ) + } + + override fun getByPageWithAnime(uuid: UUID, page: Int, limit: Int): List { + return super.getByPage( + page, + limit, + "FROM Episode WHERE anime.uuid = :uuid ORDER BY season DESC, number DESC, episodeType.name, langType.name", + "uuid" to uuid + ) + } + + override fun getByPageWithList(list: List, page: Int, limit: Int): List { + return super.getByPage( + page, + limit, + "FROM Episode WHERE anime.uuid IN :list ORDER BY releaseDate DESC, anime.name, season DESC, number DESC, episodeType.name, langType.name", + "list" to list + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/EpisodeTypeRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/EpisodeTypeRepository.kt new file mode 100644 index 0000000..4c2536b --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/EpisodeTypeRepository.kt @@ -0,0 +1,8 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.EpisodeType +import fr.ziedelth.utils.Database +import org.hibernate.Session + +class EpisodeTypeRepository(session: () -> Session = { Database.getSession() }) : + AbstractRepository(session) \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/GenreRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/GenreRepository.kt new file mode 100644 index 0000000..2ceddef --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/GenreRepository.kt @@ -0,0 +1,7 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.Genre +import fr.ziedelth.utils.Database +import org.hibernate.Session + +class GenreRepository(session: () -> Session = { Database.getSession() }) : AbstractRepository(session) \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/IPageRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/IPageRepository.kt new file mode 100644 index 0000000..92b4e67 --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/IPageRepository.kt @@ -0,0 +1,9 @@ +package fr.ziedelth.repositories + +import java.util.* + +interface IPageRepository { + fun getByPage(tag: String, page: Int, limit: Int): List + fun getByPageWithAnime(uuid: UUID, page: Int, limit: Int): List + fun getByPageWithList(list: List, page: Int, limit: Int): List +} \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/LangTypeRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/LangTypeRepository.kt new file mode 100644 index 0000000..307b144 --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/LangTypeRepository.kt @@ -0,0 +1,7 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.LangType +import fr.ziedelth.utils.Database +import org.hibernate.Session + +class LangTypeRepository(session: () -> Session = { Database.getSession() }) : AbstractRepository(session) \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/MangaRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/MangaRepository.kt new file mode 100644 index 0000000..8e52cc2 --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/MangaRepository.kt @@ -0,0 +1,47 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.Manga +import fr.ziedelth.utils.Database +import org.hibernate.Session +import java.util.* + +class MangaRepository(session: () -> Session = { Database.getSession() }) : AbstractRepository(session), + IPageRepository { + fun getByEAN(tag: String, ean: Long): Manga? { + val session = getSession.invoke() + val query = session.createQuery("FROM Manga WHERE anime.country.tag = :tag AND ean = :ean", Manga::class.java) + query.maxResults = 1 + query.setParameter("tag", tag) + query.setParameter("ean", ean) + val result = query.uniqueResult() + session.close() + return result + } + + override fun getByPage(tag: String, page: Int, limit: Int): List { + return super.getByPage( + page, + limit, + "FROM Manga WHERE anime.country.tag = :tag AND ean IS NOT NULL ORDER BY releaseDate DESC, anime.name", + "tag" to tag, + ) + } + + override fun getByPageWithAnime(uuid: UUID, page: Int, limit: Int): List { + return super.getByPage( + page, + limit, + "FROM Manga WHERE anime.uuid = :uuid ORDER BY releaseDate DESC, anime.name", + "uuid" to uuid, + ) + } + + override fun getByPageWithList(list: List, page: Int, limit: Int): List { + return super.getByPage( + page, + limit, + "FROM Manga WHERE uuid IN :list ORDER BY releaseDate DESC, anime.name", + "list" to list + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/NewsRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/NewsRepository.kt new file mode 100644 index 0000000..55159f3 --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/NewsRepository.kt @@ -0,0 +1,26 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.News +import fr.ziedelth.utils.Database +import org.hibernate.Session +import java.util.* + +class NewsRepository(session: () -> Session = { Database.getSession() }) : AbstractRepository(session), + IPageRepository { + override fun getByPage(tag: String, page: Int, limit: Int): List { + return super.getByPage( + page, + limit, + "FROM News WHERE country.tag = :tag ORDER BY releaseDate DESC", + "tag" to tag, + ) + } + + override fun getByPageWithAnime(uuid: UUID, page: Int, limit: Int): List { + TODO("Not yet implemented") + } + + override fun getByPageWithList(list: List, page: Int, limit: Int): List { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/PlatformRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/PlatformRepository.kt new file mode 100644 index 0000000..701fd40 --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/PlatformRepository.kt @@ -0,0 +1,7 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.Platform +import fr.ziedelth.utils.Database +import org.hibernate.Session + +class PlatformRepository(session: () -> Session = { Database.getSession() }) : AbstractRepository(session) \ No newline at end of file diff --git a/src/main/kotlin/fr/ziedelth/repositories/SimulcastRepository.kt b/src/main/kotlin/fr/ziedelth/repositories/SimulcastRepository.kt new file mode 100644 index 0000000..51d407c --- /dev/null +++ b/src/main/kotlin/fr/ziedelth/repositories/SimulcastRepository.kt @@ -0,0 +1,29 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.entities.Simulcast +import fr.ziedelth.utils.Database +import org.hibernate.Session + +class SimulcastRepository(session: () -> Session = { Database.getSession() }) : AbstractRepository(session) { + fun getAll(tag: String?): List { + val session = getSession.invoke() + val query = session.createQuery( + "SELECT DISTINCT simulcasts FROM Anime WHERE country.tag = :tag", + Simulcast::class.java + ) + query.setParameter("tag", tag) + val list = query.list() + session.close() + return list + } + + fun findBySeasonAndYear(season: String, year: Int): Simulcast? { + val session = getSession.invoke() + val query = session.createQuery("FROM Simulcast WHERE season = :season AND year = :year", Simulcast::class.java) + query.setParameter("season", season) + query.setParameter("year", year) + val simulcast = query.uniqueResult() + session.close() + return simulcast + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/AbstractAPITest.kt b/src/test/kotlin/fr/ziedelth/AbstractAPITest.kt new file mode 100644 index 0000000..4248ee9 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/AbstractAPITest.kt @@ -0,0 +1,141 @@ +package fr.ziedelth + +import fr.ziedelth.entities.* +import fr.ziedelth.plugins.* +import fr.ziedelth.utils.DatabaseTest +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach + +internal abstract class AbstractAPITest { + @BeforeEach + fun tearUp() { + countryRepository.saveAll( + listOf( + Country(tag = "fr", name = "France"), + Country(tag = "jp", name = "Japan") + ) + ) + val countries = countryRepository.getAll() + + val platform1 = Platform(name = "Netflix", image = "hello", url = "hello") + val platform2 = Platform(name = "Crunchyroll", image = "hello", url = "hello") + val platform3 = Platform(name = "Wakanim", image = "hello", url = "hello") + platformRepository.saveAll(listOf(platform1, platform2, platform3)) + val platforms = platformRepository.getAll() + + val simulcast1 = Simulcast(season = "WINTER", year = 2020) + val simulcast2 = Simulcast(season = "SPRING", year = 2020) + simulcastRepository.saveAll(listOf(simulcast1, simulcast2)) + val simulcasts = simulcastRepository.getAll() + + val genre1 = Genre(name = "Action") + val genre2 = Genre(name = "Comedy") + val genre3 = Genre(name = "Drama") + genreRepository.saveAll(listOf(genre1, genre2, genre3)) + val genres = genreRepository.getAll() + + val anime1 = Anime( + country = countries.first(), + name = "One Piece", + image = "hello", + hashes = mutableSetOf("hello"), + simulcasts = mutableSetOf(simulcasts.first()), + genres = mutableSetOf(genres.first(), genres.last()), + ) + val anime2 = Anime( + country = countries.first(), + name = "Naruto", + image = "hello", + hashes = mutableSetOf("hello2"), + simulcasts = mutableSetOf(simulcasts.first()), + genres = mutableSetOf(genres.first(), genres.last()), + ) + val anime3 = Anime( + country = countries.first(), + name = "Bleach", + image = "hello", + hashes = mutableSetOf("hello3"), + simulcasts = mutableSetOf(simulcasts.first()), + genres = mutableSetOf(genres.first(), genres.last()), + ) + animeRepository.saveAll(listOf(anime1, anime2, anime3)) + val animes = animeRepository.getAll() + + val episodeType1 = EpisodeType(name = "Episode") + val episodeType2 = EpisodeType(name = "OAV") + val episodeType3 = EpisodeType(name = "Film") + episodeTypeRepository.saveAll(listOf(episodeType1, episodeType2, episodeType3)) + val episodeTypes = episodeTypeRepository.getAll() + + val langType1 = LangType(name = "SUBTITLES") + val langType2 = LangType(name = "VOICE") + langTypeRepository.saveAll(listOf(langType1, langType2)) + val langTypes = langTypeRepository.getAll() + + animes.forEach { + val episodes = (1..10).map { episode -> + val platform = platforms.random() + val episodeType = episodeTypes.random() + val langType = langTypes.random() + + Episode( + platform = platform, + anime = it, + episodeType = episodeType, + langType = langType, + hash = "EP-$episode-${platform.name}-${episodeType.name}-${langType.name}-${Math.random()}", + season = 1, + number = episode, + url = "hello", + image = "hello", + duration = 1440 + ) + } + episodeRepository.saveAll(episodes) + + val mangas = (1..10).map { manga -> + val platform = platforms.random() + + Manga( + platform = platform, + anime = it, + hash = "MA-$manga-${platform.name}-${Math.random()}", + url = "hello", + cover = "hello", + editor = "MyEditor $manga", + ref = "MyRef $manga", + ean = Math.random().toLong(), + age = 18, + price = 10.0, + ) + } + mangaRepository.saveAll(mangas) + } + + newsRepository.saveAll( + listOf( + News( + country = countries.first(), + platform = platforms.first(), + title = "News 1", + description = "Content 1", + hash = "hello", + url = "hello", + ), + News( + country = countries.first(), + platform = platforms.first(), + title = "News 2", + description = "Content 2", + hash = "hello2", + url = "hello", + ), + ) + ) + } + + @AfterEach + fun tearDown() { + DatabaseTest.clean() + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/AnimeControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/AnimeControllerTest.kt new file mode 100644 index 0000000..e8d1306 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/AnimeControllerTest.kt @@ -0,0 +1,271 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import com.google.gson.JsonObject +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Anime +import fr.ziedelth.entities.Country +import fr.ziedelth.plugins.* +import fr.ziedelth.utils.Encoder +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class AnimeControllerTest : AbstractAPITest() { + @Test + fun searchByHash() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.get("/animes/country/fr/search/hash/hello") + val json = Gson().fromJson(response.bodyAsText(), JsonObject::class.java) + val uuid = json.get("uuid").asString + + expect(HttpStatusCode.OK) { response.status } + checkNotNull(uuid) + + expect(HttpStatusCode.NotFound) { client.get("/animes/country/fr/search/hash/azertyuiop").status } + } + } + + @Test + fun searchByName() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.get("/animes/country/fr/search/name/Naruto") + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { response.status } + expect(1) { json.size } + } + } + + @Test + fun getByPage() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + val simulcast = simulcastRepository.getAll().first() + + // NOT CACHED + + val responseNotCached = + client.get("/animes/country/${country.tag}/simulcast/${simulcast.uuid}/page/1/limit/12") + val jsonNotCached = Gson().fromJson(responseNotCached.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { responseNotCached.status } + expect(3) { jsonNotCached.size } + + // CACHED + + val responseCached = + client.get("/animes/country/${country.tag}/simulcast/${simulcast.uuid}/page/1/limit/12") + val jsonCached = Gson().fromJson(responseCached.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { responseCached.status } + expect(3) { jsonCached.size } + } + } + + @Test + fun getByPageError() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + val simulcast = simulcastRepository.getAll().first() + + // ERROR + + val responseError = + client.get("/animes/country/${country.tag}/simulcast/${simulcast.uuid}/page/ae/limit/12") + + expect(HttpStatusCode.InternalServerError) { responseError.status } + } + } + + @Test + fun getWatchlistByPage() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val anime = animeRepository.getAll().first() + val bodyRequest = Encoder.toGzip("[\"${anime.uuid}\"]") + + val response = client.post("/animes/watchlist/page/1/limit/12") { + setBody(bodyRequest) + } + + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { response.status } + expect(1) { json.size } + } + } + + @Test + fun getWatchlistByPageError() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val anime = animeRepository.getAll().first() + val bodyRequest = Encoder.toGzip("[\"${anime.uuid}\"]") + + val responseError = client.post("/animes/watchlist/page/ae/limit/12") { + setBody(bodyRequest) + } + + expect(HttpStatusCode.InternalServerError) { responseError.status } + } + } + + @Test + fun create() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + + val response = client.post("/animes") { + contentType(ContentType.Application.Json) + setBody(Anime(country = country, name = "Test", description = "Test", image = "Test")) + } + + expect(HttpStatusCode.Created) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Anime::class.java) + checkNotNull(json.uuid) + } + } + + @Test + fun createError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + + expect(HttpStatusCode.BadRequest) { + client.post("/animes") { + contentType(ContentType.Application.Json) + setBody( + Anime( + country = Country(tag = "us", name = "United States"), + name = "Test", + description = "Test", + image = "Test" + ) + ) + }.status + } + + expect(HttpStatusCode.BadRequest) { + client.post("/animes") { + contentType(ContentType.Application.Json) + setBody(Anime(country = country, description = "Test", image = "Test")) + }.status + } + + expect(HttpStatusCode.Conflict) { + client.post("/animes") { + contentType(ContentType.Application.Json) + setBody(Anime(country = country, name = "One Piece", description = "Test", image = "Test")) + }.status + } + } + } + + @Test + fun merge() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val animes = animeRepository.getAll() + val uuids = animes.map { it.uuid.toString() } + + val response = client.put("/animes/merge") { + contentType(ContentType.Application.Json) + setBody(uuids) + } + + expect(HttpStatusCode.OK) { response.status } + expect(1) { animeRepository.getAll().size } + } + } + + @Test + fun mergeError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + expect(HttpStatusCode.NotFound) { + client.put("/animes/merge") { + contentType(ContentType.Application.Json) + setBody(emptyList()) + }.status + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/CountryControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/CountryControllerTest.kt new file mode 100644 index 0000000..32b41a0 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/CountryControllerTest.kt @@ -0,0 +1,95 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Country +import fr.ziedelth.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class CountryControllerTest : AbstractAPITest() { + @Test + fun getAll() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.get("/countries") + expect(HttpStatusCode.OK) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + expect(2) { json.size } + } + } + + @Test + fun create() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.post("/countries") { + contentType(ContentType.Application.Json) + setBody(Country(tag = "us", name = "United States")) + } + + expect(HttpStatusCode.Created) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Country::class.java) + checkNotNull(json.uuid) + } + } + + @Test + fun createError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + expect(HttpStatusCode.BadRequest) { + client.post("/countries") { + contentType(ContentType.Application.Json) + setBody(Country(name = "France")) + }.status + } + + expect(HttpStatusCode.Conflict) { + client.post("/countries") { + contentType(ContentType.Application.Json) + setBody(Country(tag = "fr", name = "Test")) + }.status + } + + expect(HttpStatusCode.Conflict) { + client.post("/countries") { + contentType(ContentType.Application.Json) + setBody(Country(tag = "test", name = "France")) + }.status + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/EpisodeControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/EpisodeControllerTest.kt new file mode 100644 index 0000000..3cce111 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/EpisodeControllerTest.kt @@ -0,0 +1,327 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.* +import fr.ziedelth.plugins.* +import fr.ziedelth.utils.Encoder +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class EpisodeControllerTest : AbstractAPITest() { + @Test + fun getByPage() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + + // NOT CACHED + + val responseNotCached = + client.get("/episodes/country/${country.tag}/page/1/limit/12") + val jsonNotCached = Gson().fromJson(responseNotCached.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { responseNotCached.status } + expect(12) { jsonNotCached.size } + + // CACHED + + val responseCached = + client.get("/episodes/country/${country.tag}/page/1/limit/12") + val jsonCached = Gson().fromJson(responseCached.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { responseCached.status } + expect(12) { jsonCached.size } + } + } + + @Test + fun getByPageError() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + + // ERROR + + expect(HttpStatusCode.InternalServerError) { client.get("/episodes/country/${country.tag}/page/ae/limit/12").status } + expect(HttpStatusCode.InternalServerError) { client.get("/episodes/country/${country.tag}/page/1/limit/ae").status } + expect(HttpStatusCode.InternalServerError) { client.get("/episodes/country/${country.tag}/page/0/limit/12").status } + expect(HttpStatusCode.InternalServerError) { client.get("/episodes/country/${country.tag}/page/1/limit/0").status } + expect(HttpStatusCode.InternalServerError) { client.get("/episodes/country/${country.tag}/page/1/limit/31").status } + } + } + + @Test + fun getByPageWithAnime() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val anime = animeRepository.getAll().first() + + val response = + client.get("/episodes/anime/${anime.uuid}/page/1/limit/12") + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { response.status } + expect(10) { json.size } + } + } + + @Test + fun getByPageWithAnimeError() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val anime = animeRepository.getAll().first() + + // ERROR + + val responseError = + client.get("/episodes/anime/${anime.uuid}/page/ae/limit/12") + + expect(HttpStatusCode.InternalServerError) { responseError.status } + } + } + + @Test + fun getWatchlistByPage() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val anime = animeRepository.getAll().first() + val bodyRequest = Encoder.toGzip("[\"${anime.uuid}\"]") + + val response = client.post("/episodes/watchlist/page/1/limit/12") { + setBody(bodyRequest) + } + + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { response.status } + expect(10) { json.size } + } + } + + @Test + fun getWatchlistByPageError() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val anime = animeRepository.getAll().first() + val bodyRequest = Encoder.toGzip("[\"${anime.uuid}\"]") + + val responseError = client.post("/episodes/watchlist/page/ae/limit/12") { + setBody(bodyRequest) + } + + expect(HttpStatusCode.InternalServerError) { responseError.status } + } + } + + @Test + fun save() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val platform = platformRepository.getAll().first() + val anime = animeRepository.getAll().first() + val episodeType = episodeTypeRepository.getAll().first() + val langType = langTypeRepository.getAll().first() + + val response = client.post("/episodes/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Episode( + anime = anime, + platform = platform, + episodeType = episodeType, + langType = langType, + number = 1, + season = 1, + url = "https://www.google.com", + image = "https://www.google.com", + hash = "hash", + ), + Episode( + anime = anime, + platform = platform, + episodeType = episodeType, + langType = langType, + number = 2, + season = 1, + url = "https://www.google.com", + image = "https://www.google.com", + hash = "azertyuiop", + ) + ) + ) + } + + expect(HttpStatusCode.Created) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + expect(2) { json.size } + } + } + + @Test + fun saveError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + expect(HttpStatusCode.InternalServerError) { + client.post("/episodes/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Episode( + platform = Platform(), + anime = Anime(), + episodeType = EpisodeType(), + langType = LangType(), + number = 1, + season = 1, + url = "https://www.google.com", + image = "https://www.google.com", + ), + ) + ) + }.status + } + + val platform = platformRepository.getAll().first() + + expect(HttpStatusCode.InternalServerError) { + client.post("/episodes/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Episode( + platform = platform, + anime = Anime(), + episodeType = EpisodeType(), + langType = LangType(), + number = 1, + season = 1, + url = "https://www.google.com", + image = "https://www.google.com", + ), + ) + ) + }.status + } + + val anime = animeRepository.getAll().first() + + expect(HttpStatusCode.InternalServerError) { + client.post("/episodes/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Episode( + platform = platform, + anime = anime, + episodeType = EpisodeType(), + langType = LangType(), + number = 1, + season = 1, + url = "https://www.google.com", + image = "https://www.google.com", + ), + ) + ) + }.status + } + + val episodeType = episodeTypeRepository.getAll().first() + + expect(HttpStatusCode.InternalServerError) { + client.post("/episodes/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Episode( + platform = platform, + anime = anime, + episodeType = episodeType, + langType = LangType(), + number = 1, + season = 1, + url = "https://www.google.com", + image = "https://www.google.com", + ), + ) + ) + }.status + } + + val langType = langTypeRepository.getAll().first() + + expect(HttpStatusCode.InternalServerError) { + client.post("/episodes/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Episode( + platform = platform, + anime = anime, + episodeType = episodeType, + langType = langType, + number = 1, + season = 1, + url = "https://www.google.com", + image = "https://www.google.com", + ), + ) + ) + }.status + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/EpisodeTypeControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/EpisodeTypeControllerTest.kt new file mode 100644 index 0000000..3c112d9 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/EpisodeTypeControllerTest.kt @@ -0,0 +1,88 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.EpisodeType +import fr.ziedelth.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class EpisodeTypeControllerTest : AbstractAPITest() { + @Test + fun getAll() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.get("/episodetypes") + expect(HttpStatusCode.OK) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + expect(3) { json.size } + } + } + + @Test + fun create() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.post("/episodetypes") { + contentType(ContentType.Application.Json) + setBody(EpisodeType(name = "test")) + } + + expect(HttpStatusCode.Created) { response.status } + val json = Gson().fromJson(response.bodyAsText(), EpisodeType::class.java) + checkNotNull(json.uuid) + } + } + + @Test + fun createError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + expect(HttpStatusCode.BadRequest) { + client.post("/episodetypes") { + contentType(ContentType.Application.Json) + setBody(EpisodeType()) + }.status + } + + expect(HttpStatusCode.Conflict) { + client.post("/episodetypes") { + contentType(ContentType.Application.Json) + setBody(EpisodeType(name = "Episode")) + }.status + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/GenreControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/GenreControllerTest.kt new file mode 100644 index 0000000..3f15512 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/GenreControllerTest.kt @@ -0,0 +1,88 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Genre +import fr.ziedelth.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class GenreControllerTest : AbstractAPITest() { + @Test + fun getAll() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.get("/genres") + expect(HttpStatusCode.OK) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + expect(3) { json.size } + } + } + + @Test + fun create() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.post("/genres") { + contentType(ContentType.Application.Json) + setBody(Genre(name = "test")) + } + + expect(HttpStatusCode.Created) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Genre::class.java) + checkNotNull(json.uuid) + } + } + + @Test + fun createError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + expect(HttpStatusCode.BadRequest) { + client.post("/genres") { + contentType(ContentType.Application.Json) + setBody(Genre()) + }.status + } + + expect(HttpStatusCode.Conflict) { + client.post("/genres") { + contentType(ContentType.Application.Json) + setBody(Genre(name = "Action")) + }.status + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/LangTypeControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/LangTypeControllerTest.kt new file mode 100644 index 0000000..83c3dc6 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/LangTypeControllerTest.kt @@ -0,0 +1,88 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.LangType +import fr.ziedelth.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class LangTypeControllerTest : AbstractAPITest() { + @Test + fun getAll() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.get("/langtypes") + expect(HttpStatusCode.OK) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + expect(2) { json.size } + } + } + + @Test + fun create() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.post("/langtypes") { + contentType(ContentType.Application.Json) + setBody(LangType(name = "test")) + } + + expect(HttpStatusCode.Created) { response.status } + val json = Gson().fromJson(response.bodyAsText(), LangType::class.java) + checkNotNull(json.uuid) + } + } + + @Test + fun createError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + expect(HttpStatusCode.BadRequest) { + client.post("/langtypes") { + contentType(ContentType.Application.Json) + setBody(LangType()) + }.status + } + + expect(HttpStatusCode.Conflict) { + client.post("/langtypes") { + contentType(ContentType.Application.Json) + setBody(LangType(name = "SUBTITLES")) + }.status + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/MangaControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/MangaControllerTest.kt new file mode 100644 index 0000000..268ffd5 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/MangaControllerTest.kt @@ -0,0 +1,306 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.* +import fr.ziedelth.plugins.* +import fr.ziedelth.utils.Encoder +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class MangaControllerTest : AbstractAPITest() { + @Test + fun searchByEAN() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + val ean = mangaRepository.getAll().first().ean + + val responseNotCached = client.get("/mangas/country/${country.tag}/search/ean/$ean") + val json = Gson().fromJson(responseNotCached.bodyAsText(), Manga::class.java) + + expect(HttpStatusCode.OK) { responseNotCached.status } + checkNotNull(json.uuid) + } + } + + @Test + fun getByPage() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + + // NOT CACHED + + val responseNotCached = + client.get("/mangas/country/${country.tag}/page/1/limit/12") + val jsonNotCached = Gson().fromJson(responseNotCached.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { responseNotCached.status } + expect(12) { jsonNotCached.size } + + // CACHED + + val responseCached = + client.get("/mangas/country/${country.tag}/page/1/limit/12") + val jsonCached = Gson().fromJson(responseCached.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { responseCached.status } + expect(12) { jsonCached.size } + } + } + + @Test + fun getByPageError() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + + // ERROR + + val responseError = + client.get("/mangas/country/${country.tag}/page/ae/limit/12") + + expect(HttpStatusCode.InternalServerError) { responseError.status } + } + } + + @Test + fun getByPageWithAnime() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val anime = animeRepository.getAll().first() + + val response = + client.get("/mangas/anime/${anime.uuid}/page/1/limit/12") + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { response.status } + expect(10) { json.size } + } + } + + @Test + fun getByPageWithAnimeError() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val anime = animeRepository.getAll().first() + + // ERROR + + val responseError = + client.get("/mangas/anime/${anime.uuid}/page/ae/limit/12") + + expect(HttpStatusCode.InternalServerError) { responseError.status } + } + } + + @Test + fun getWatchlistByPage() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val manga = mangaRepository.getAll().first() + val bodyRequest = Encoder.toGzip("[\"${manga.uuid}\"]") + + val response = client.post("/mangas/watchlist/page/1/limit/12") { + setBody(bodyRequest) + } + + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { response.status } + expect(1) { json.size } + } + } + + @Test + fun getWatchlistByPageError() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val manga = mangaRepository.getAll().first() + val bodyRequest = Encoder.toGzip("[\"${manga.uuid}\"]") + + val responseError = client.post("/mangas/watchlist/page/ae/limit/12") { + setBody(bodyRequest) + } + + expect(HttpStatusCode.InternalServerError) { responseError.status } + } + } + + @Test + fun save() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val platform = platformRepository.getAll().first() + val anime = animeRepository.getAll().first() + + val response = client.post("/mangas/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Manga( + platform = platform, + anime = anime, + hash = "hash", + url = "test", + cover = "test", + editor = "test", + ref = "test", + ean = Math.random().toLong(), + age = 12, + price = 7.5, + ), + Manga( + platform = platform, + anime = anime, + hash = "hash2", + url = "test2", + cover = "test2", + editor = "test2", + ref = "test2", + ean = Math.random().toLong(), + age = 12, + price = 7.5, + ) + ) + ) + } + + expect(HttpStatusCode.Created) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + expect(2) { json.size } + } + } + + @Test + fun saveError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + expect(HttpStatusCode.InternalServerError) { + client.post("/mangas/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Manga( + platform = Platform(), + anime = Anime(), + hash = "hash2", + url = "test2", + cover = "test2", + editor = "test2", + ref = "test2", + ean = Math.random().toLong(), + age = 12, + price = 7.5, + ), + ) + ) + }.status + } + + val platform = platformRepository.getAll().first() + + expect(HttpStatusCode.InternalServerError) { + client.post("/mangas/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Manga( + platform = platform, + anime = Anime(), + hash = "hash2", + url = "test2", + cover = "test2", + editor = "test2", + ref = "test2", + ean = Math.random().toLong(), + age = 12, + price = 7.5, + ), + ) + ) + }.status + } + + val anime = animeRepository.getAll().first() + + expect(HttpStatusCode.InternalServerError) { + client.post("/episodes/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + Manga( + platform = platform, + anime = anime, + url = "test2", + cover = "test2", + editor = "test2", + ref = "test2", + ean = Math.random().toLong(), + age = 12, + price = 7.5, + ), + ) + ) + }.status + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/NewsControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/NewsControllerTest.kt new file mode 100644 index 0000000..5934290 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/NewsControllerTest.kt @@ -0,0 +1,187 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.* +import fr.ziedelth.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class NewsControllerTest : AbstractAPITest() { + @Test + fun getByPage() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + + // NOT CACHED + + val responseNotCached = + client.get("/news/country/${country.tag}/page/1/limit/12") + val jsonNotCached = Gson().fromJson(responseNotCached.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { responseNotCached.status } + expect(2) { jsonNotCached.size } + + // CACHED + + val responseCached = + client.get("/news/country/${country.tag}/page/1/limit/12") + val jsonCached = Gson().fromJson(responseCached.bodyAsText(), Array::class.java) + + expect(HttpStatusCode.OK) { responseCached.status } + expect(2) { jsonCached.size } + } + } + + @Test + fun getByPageError() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + + // ERROR + + val responseError = + client.get("/news/country/${country.tag}/page/ae/limit/12") + + expect(HttpStatusCode.InternalServerError) { responseError.status } + } + } + + @Test + fun save() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val platform = platformRepository.getAll().first() + val country = countryRepository.getAll().first() + + val response = client.post("/news/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + News( + country = country, + platform = platform, + title = "News 3", + description = "Content 3", + hash = "hello3", + url = "hello", + ), + News( + country = country, + platform = platform, + title = "News 4", + description = "Content 4", + hash = "hello4", + url = "hello", + ), + ) + ) + } + + expect(HttpStatusCode.Created) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + expect(2) { json.size } + } + } + + @Test + fun saveError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + expect(HttpStatusCode.InternalServerError) { + client.post("/news/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + News( + country = Country(), + platform = Platform(), + title = "News 3", + description = "Content 3", + hash = "hello3", + url = "hello", + ), + ) + ) + }.status + } + + val platform = platformRepository.getAll().first() + + expect(HttpStatusCode.InternalServerError) { + client.post("/news/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + News( + country = Country(), + platform = platform, + title = "News 3", + description = "Content 3", + hash = "hello3", + url = "hello", + ), + ) + ) + }.status + } + + val country = countryRepository.getAll().first() + + expect(HttpStatusCode.InternalServerError) { + client.post("/news/multiple") { + contentType(ContentType.Application.Json) + setBody( + listOf( + News( + country = country, + platform = platform, + title = "News 3", + description = "Content 3", + url = "hello", + ), + ) + ) + }.status + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/PlatformControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/PlatformControllerTest.kt new file mode 100644 index 0000000..d2c6975 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/PlatformControllerTest.kt @@ -0,0 +1,102 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Platform +import fr.ziedelth.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class PlatformControllerTest : AbstractAPITest() { + @Test + fun getAll() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.get("/platforms") + expect(HttpStatusCode.OK) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + expect(3) { json.size } + } + } + + @Test + fun create() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + val response = client.post("/platforms") { + contentType(ContentType.Application.Json) + setBody(Platform(name = "MangaNews", url = "hello", image = "hello")) + } + + expect(HttpStatusCode.Created) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Platform::class.java) + checkNotNull(json.uuid) + } + } + + @Test + fun createError() { + testApplication { + val client = createClient { + install(ContentNegotiation) { + gson() + } + } + + application { + configureHTTP() + configureRoutingTest() + } + + expect(HttpStatusCode.BadRequest) { + client.post("/platforms") { + contentType(ContentType.Application.Json) + setBody(Platform(name = "MangaNews")) + }.status + } + + expect(HttpStatusCode.BadRequest) { + client.post("/platforms") { + contentType(ContentType.Application.Json) + setBody(Platform(url = "hello")) + }.status + } + + expect(HttpStatusCode.BadRequest) { + client.post("/platforms") { + contentType(ContentType.Application.Json) + setBody(Platform(image = "hello")) + }.status + } + + expect(HttpStatusCode.Conflict) { + client.post("/platforms") { + contentType(ContentType.Application.Json) + setBody(Platform(name = "Netflix", url = "hello", image = "hello")) + }.status + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/controllers/SimulcastControllerTest.kt b/src/test/kotlin/fr/ziedelth/controllers/SimulcastControllerTest.kt new file mode 100644 index 0000000..96e27bd --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/controllers/SimulcastControllerTest.kt @@ -0,0 +1,35 @@ +package fr.ziedelth.controllers + +import com.google.gson.Gson +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Simulcast +import fr.ziedelth.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.serialization.gson.* +import io.ktor.server.testing.* +import io.ktor.util.* +import org.junit.jupiter.api.Test +import java.util.* +import kotlin.test.expect + +internal class SimulcastControllerTest : AbstractAPITest() { + @Test + fun getAll() { + testApplication { + application { + configureHTTP() + configureRoutingTest() + } + + val country = countryRepository.getAll().first() + + val response = client.get("/simulcasts/country/${country.tag}") + expect(HttpStatusCode.OK) { response.status } + val json = Gson().fromJson(response.bodyAsText(), Array::class.java) + expect(1) { json.size } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/entities/AnimeTest.kt b/src/test/kotlin/fr/ziedelth/entities/AnimeTest.kt new file mode 100644 index 0000000..36a93fe --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/entities/AnimeTest.kt @@ -0,0 +1,17 @@ +package fr.ziedelth.entities + +import org.junit.jupiter.api.Test +import kotlin.test.expect + +class AnimeTest { + @Test + fun hash() { + val anime = Anime( + name = "Do It Yourself!!", + image = "hello", + country = Country(tag = "fr", name = "France") + ) + + expect("do-it-yourself") { anime.hash() } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/entities/SimulcastTest.kt b/src/test/kotlin/fr/ziedelth/entities/SimulcastTest.kt new file mode 100644 index 0000000..beb9f1c --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/entities/SimulcastTest.kt @@ -0,0 +1,13 @@ +package fr.ziedelth.entities + +import org.junit.jupiter.api.Test +import kotlin.test.expect + +class SimulcastTest { + @Test + fun hash() { + val simulcast = Simulcast.getSimulcast(2022, 11) + expect(2022) { simulcast.year } + expect("AUTUMN") { simulcast.season } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/plugins/RoutingTest.kt b/src/test/kotlin/fr/ziedelth/plugins/RoutingTest.kt new file mode 100644 index 0000000..879aac3 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/plugins/RoutingTest.kt @@ -0,0 +1,40 @@ +package fr.ziedelth.plugins + +import fr.ziedelth.controllers.* +import fr.ziedelth.repositories.* +import fr.ziedelth.utils.DatabaseTest +import io.ktor.server.application.* +import io.ktor.server.routing.* + +val countryRepository = CountryRepository { DatabaseTest.getSession() } +val platformRepository = PlatformRepository { DatabaseTest.getSession() } +val simulcastRepository = SimulcastRepository { DatabaseTest.getSession() } +val genreRepository = GenreRepository { DatabaseTest.getSession() } +val animeRepository = AnimeRepository { DatabaseTest.getSession() } +val episodeTypeRepository = EpisodeTypeRepository { DatabaseTest.getSession() } +val langTypeRepository = LangTypeRepository { DatabaseTest.getSession() } +val episodeRepository = EpisodeRepository { DatabaseTest.getSession() } +val mangaRepository = MangaRepository { DatabaseTest.getSession() } +val newsRepository = NewsRepository { DatabaseTest.getSession() } + +fun Application.configureRoutingTest() { + routing { + CountryController(countryRepository).getRoutes(this) + PlatformController(platformRepository).getRoutes(this) + SimulcastController(simulcastRepository).getRoutes(this) + GenreController(genreRepository).getRoutes(this) + AnimeController(countryRepository, animeRepository, episodeRepository, mangaRepository).getRoutes(this) + EpisodeTypeController(episodeTypeRepository).getRoutes(this) + LangTypeController(langTypeRepository).getRoutes(this) + EpisodeController( + platformRepository, + animeRepository, + simulcastRepository, + episodeTypeRepository, + langTypeRepository, + episodeRepository + ).getRoutes(this) + NewsController(countryRepository, platformRepository, newsRepository).getRoutes(this) + MangaController(platformRepository, animeRepository, mangaRepository).getRoutes(this) + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/repositories/AnimeRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/AnimeRepositoryTest.kt new file mode 100644 index 0000000..4a6d15c --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/AnimeRepositoryTest.kt @@ -0,0 +1,173 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Anime +import fr.ziedelth.entities.Country +import fr.ziedelth.plugins.animeRepository +import fr.ziedelth.plugins.countryRepository +import fr.ziedelth.plugins.simulcastRepository +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.util.* +import kotlin.test.expect + +internal class AnimeRepositoryTest : AbstractAPITest() { + @Test + fun find() { + val anime = animeRepository.find(UUID.randomUUID()) + expect(null) { anime } + + val animes = animeRepository.getAll() + expect(animes.first().uuid) { animeRepository.find(animes.first().uuid)?.uuid } + } + + @Test + fun exists() { + expect(true) { animeRepository.exists("name", "One Piece") } + expect(false) { animeRepository.exists("uuid", UUID.randomUUID()) } + } + + @Test + fun findAll() { + expect(emptyList()) { animeRepository.findAll(listOf(UUID.randomUUID())) } + + val animes = animeRepository.getAll() + val list = animeRepository.findAll(listOf(animes.first().uuid, animes[1].uuid)) + + expect(true) { list.any { it.uuid == animes.first().uuid } && list.any { it.uuid == animes[1].uuid } } + expect(false) { list.any { it.uuid == animes[2].uuid } } + } + + @Test + fun getAll() { + expect(3) { animeRepository.getAll().size } + } + + @Test + fun getAllByCountry() { + val country = countryRepository.getAll().first() + expect(3) { animeRepository.getAllBy("country.tag", country.tag).size } + expect(0) { animeRepository.getAllBy("country.tag", "us").size } + } + + @Test + fun save() { + assertThrows { + animeRepository.save( + Anime( + country = Country(tag = "fr", name = "France"), + name = "Naruto", + image = "hello" + ) + ) + } + + val anime = Anime(country = Country(tag = "us", name = "United States"), name = "Naruto", image = "hello") + animeRepository.save(anime) + + expect(4) { animeRepository.getAll().size } + checkNotNull { anime.uuid } + expect("Naruto") { anime.name } + } + + @Test + fun saveAll() { + assertThrows { + animeRepository.saveAll( + listOf( + Anime( + country = Country(tag = "fr", name = "France"), + name = "Naruto", + image = "hello" + ) + ) + ) + } + + val anime1 = Anime(country = Country(tag = "us", name = "United States"), name = "Naruto", image = "hello") + val anime2 = Anime(country = Country(tag = "uk", name = "United Kingdom"), name = "Bleach", image = "hello") + animeRepository.saveAll(listOf(anime1, anime2)) + + expect(5) { animeRepository.getAll().size } + checkNotNull { anime1.uuid } + checkNotNull { anime2.uuid } + expect("Naruto") { anime1.name } + expect("Bleach") { anime2.name } + } + + @Test + fun delete() { + assertThrows { + animeRepository.delete( + Anime( + country = Country(tag = "fr", name = "France"), + name = "Naruto", + image = "hello" + ) + ) + } + + val animes = animeRepository.getAll() + animeRepository.delete(animes.first()) + expect(2) { animes.size - 1 } + } + + @Test + fun deleteAll() { + assertThrows { + animeRepository.deleteAll( + listOf( + Anime( + country = Country(tag = "fr", name = "France"), + name = "Naruto", + image = "hello" + ) + ) + ) + } + + val animes = animeRepository.getAll() + animeRepository.deleteAll(listOf(animes.first(), animes[1])) + expect(1) { animes.size - 2 } + } + + @Test + fun findByHash() { + val anime2 = animeRepository.findByHash("fr", "hello") + checkNotNull { anime2 } + } + + @Test + fun findByName() { + val animes = animeRepository.findByName("fr", "Naruto") + expect(1) { animes.size } + } + + @Test + fun getByPage() { + val simulcast = simulcastRepository.getAll().first() + + val page1 = animeRepository.getByPage("fr", simulcast.uuid, 1, 2) + expect(2) { page1.size } + val page2 = animeRepository.getByPage("fr", simulcast.uuid, 2, 2) + expect(1) { page2.size } + } + + @Test + fun findByPage() { + val animes = animeRepository.getAll() + val uuids = listOf(animes[0].uuid, animes[1].uuid, animes[2].uuid) + + val page1 = animeRepository.getByPageWithList(uuids, 1, 2) + expect(2) { page1.size } + val page2 = animeRepository.getByPageWithList(uuids, 2, 2) + expect(1) { page2.size } + } + +// @Test +// fun getDiary() { +// val country = countryRepository.getAll().first() +// val diary = animeRepository.getDiary(country.tag!!, Calendar.getInstance().get(Calendar.DAY_OF_WEEK)) +// expect(3) { diary.size } +// } +} diff --git a/src/test/kotlin/fr/ziedelth/repositories/CountryRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/CountryRepositoryTest.kt new file mode 100644 index 0000000..c9f28a8 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/CountryRepositoryTest.kt @@ -0,0 +1,93 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Country +import fr.ziedelth.plugins.countryRepository +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.util.* +import kotlin.test.expect + +internal class CountryRepositoryTest : AbstractAPITest() { + @Test + fun find() { + val country = countryRepository.find(UUID.randomUUID()) + expect(null) { country } + + val countries = countryRepository.getAll() + expect(countries.first().uuid) { countryRepository.find(countries.first().uuid)?.uuid } + } + + @Test + fun exists() { + expect(true) { countryRepository.exists("tag", "fr") } + expect(false) { countryRepository.exists("uuid", UUID.randomUUID()) } + } + + @Test + fun findAll() { + expect(emptyList()) { countryRepository.findAll(listOf(UUID.randomUUID())) } + + val countries = countryRepository.getAll() + val list = countryRepository.findAll(listOf(countries[0].uuid, countries[1].uuid)) + + expect(true) { list.any { it.uuid == countries.first().uuid } && list.any { it.uuid == countries[1].uuid } } + } + + @Test + fun getAll() { + expect(2) { countryRepository.getAll().size } + } + + @Test + fun save() { + assertThrows { + countryRepository.save(Country(tag = "fr", name = "France")) + } + + val country = Country(tag = "us", name = "United States") + countryRepository.save(country) + + expect(3) { countryRepository.getAll().size } + checkNotNull { country.uuid } + expect("United States") { country.name } + } + + @Test + fun saveAll() { + assertThrows { + countryRepository.saveAll( + listOf( + Country(tag = "fr", name = "France"), + Country(tag = "jp", name = "Japan") + ) + ) + } + + val country1 = Country(tag = "us", name = "United States") + val country2 = Country(tag = "uk", name = "United Kingdom") + countryRepository.saveAll(listOf(country1, country2)) + + expect(4) { countryRepository.getAll().size } + checkNotNull { country1.uuid } + checkNotNull { country2.uuid } + expect("United States") { country1.name } + expect("United Kingdom") { country2.name } + } + + @Test + fun delete() { + assertThrows { + countryRepository.delete( + Country( + tag = "fr", + name = "France" + ) + ) + } + + val countries = countryRepository.getAll() + countryRepository.delete(countries.first()) + expect(1) { countries.size - 1 } + } +} diff --git a/src/test/kotlin/fr/ziedelth/repositories/EpisodeRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/EpisodeRepositoryTest.kt new file mode 100644 index 0000000..42db2b0 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/EpisodeRepositoryTest.kt @@ -0,0 +1,37 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.plugins.animeRepository +import fr.ziedelth.plugins.episodeRepository +import org.junit.jupiter.api.Test +import kotlin.test.expect + +internal class EpisodeRepositoryTest : AbstractAPITest() { + @Test + fun getByPage() { + val page1 = episodeRepository.getByPage("fr", 1, 2) + expect(2) { page1.size } + val page2 = episodeRepository.getByPage("fr", 2, 2) + expect(2) { page2.size } + } + + @Test + fun getByPageWithAnime() { + val anime = animeRepository.getAll().first() + + val page1 = episodeRepository.getByPageWithAnime(anime.uuid, 1, 2) + expect(2) { page1.size } + val page2 = episodeRepository.getByPageWithAnime(anime.uuid, 2, 2) + expect(2) { page2.size } + } + + @Test + fun getByPageWithList() { + val animes = animeRepository.getAll().take(2).map { it.uuid } + + val page1 = episodeRepository.getByPageWithList(animes, 1, 2) + expect(2) { page1.size } + val page2 = episodeRepository.getByPageWithList(animes, 2, 2) + expect(2) { page2.size } + } +} diff --git a/src/test/kotlin/fr/ziedelth/repositories/EpisodeTypeRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/EpisodeTypeRepositoryTest.kt new file mode 100644 index 0000000..2239926 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/EpisodeTypeRepositoryTest.kt @@ -0,0 +1,92 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.EpisodeType +import fr.ziedelth.plugins.episodeTypeRepository +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.util.* +import kotlin.test.expect + +internal class EpisodeTypeRepositoryTest : AbstractAPITest() { + @Test + fun find() { + val episodeType = episodeTypeRepository.find(UUID.randomUUID()) + expect(null) { episodeType } + + val episodeTypes = episodeTypeRepository.getAll() + expect(episodeTypes.first().uuid) { episodeTypeRepository.find(episodeTypes.first().uuid)?.uuid } + } + + @Test + fun exists() { + expect(true) { episodeTypeRepository.exists("name", "Episode") } + expect(false) { episodeTypeRepository.exists("uuid", UUID.randomUUID()) } + } + + @Test + fun findAll() { + expect(emptyList()) { episodeTypeRepository.findAll(listOf(UUID.randomUUID())) } + + val episodeTypes = episodeTypeRepository.getAll() + val list = episodeTypeRepository.findAll(listOf(episodeTypes[0].uuid, episodeTypes[1].uuid)) + + expect(true) { list.any { it.uuid == episodeTypes.first().uuid } && list.any { it.uuid == episodeTypes[1].uuid } } + } + + @Test + fun getAll() { + expect(3) { episodeTypeRepository.getAll().size } + } + + @Test + fun save() { + assertThrows { + episodeTypeRepository.save(EpisodeType(name = "Episode")) + } + + val episodeType = EpisodeType(name = "Special") + episodeTypeRepository.save(episodeType) + + expect(4) { episodeTypeRepository.getAll().size } + checkNotNull { episodeType.uuid } + expect("Special") { episodeType.name } + } + + @Test + fun saveAll() { + assertThrows { + episodeTypeRepository.saveAll( + listOf( + EpisodeType(name = "Episode"), + EpisodeType(name = "OAV") + ) + ) + } + + val episodeType1 = EpisodeType(name = "Special") + val episodeType2 = EpisodeType(name = "Special 2") + episodeTypeRepository.saveAll(listOf(episodeType1, episodeType2)) + + expect(5) { episodeTypeRepository.getAll().size } + checkNotNull { episodeType1.uuid } + checkNotNull { episodeType2.uuid } + expect("Special") { episodeType1.name } + expect("Special 2") { episodeType2.name } + } + + @Test + fun delete() { + assertThrows { + episodeTypeRepository.delete( + EpisodeType( + name = "Episode" + ) + ) + } + + val episodeTypes = episodeTypeRepository.getAll() + episodeTypeRepository.delete(episodeTypes.first()) + expect(2) { episodeTypes.size - 1 } + } +} diff --git a/src/test/kotlin/fr/ziedelth/repositories/GenreRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/GenreRepositoryTest.kt new file mode 100644 index 0000000..5607f22 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/GenreRepositoryTest.kt @@ -0,0 +1,92 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Genre +import fr.ziedelth.plugins.genreRepository +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.util.* +import kotlin.test.expect + +internal class GenreRepositoryTest : AbstractAPITest() { + @Test + fun find() { + val genre = genreRepository.find(UUID.randomUUID()) + expect(null) { genre } + + val genres = genreRepository.getAll() + expect(genres.first().uuid) { genreRepository.find(genres.first().uuid)?.uuid } + } + + @Test + fun exists() { + expect(true) { genreRepository.exists("name", "Action") } + expect(false) { genreRepository.exists("uuid", UUID.randomUUID()) } + } + + @Test + fun findAll() { + expect(emptyList()) { genreRepository.findAll(listOf(UUID.randomUUID())) } + + val genres = genreRepository.getAll() + val list = genreRepository.findAll(listOf(genres[0].uuid, genres[1].uuid)) + + expect(true) { list.any { it.uuid == genres.first().uuid } && list.any { it.uuid == genres[1].uuid } } + } + + @Test + fun getAll() { + expect(3) { genreRepository.getAll().size } + } + + @Test + fun save() { + assertThrows { + genreRepository.save(Genre(name = "Action")) + } + + val genre = Genre(name = "Fantasy") + genreRepository.save(genre) + + expect(4) { genreRepository.getAll().size } + checkNotNull { genre.uuid } + expect("Fantasy") { genre.name } + } + + @Test + fun saveAll() { + assertThrows { + genreRepository.saveAll( + listOf( + Genre(name = "Action"), + Genre(name = "Drama") + ) + ) + } + + val genre1 = Genre(name = "Fantasy") + val genre2 = Genre(name = "Horror") + genreRepository.saveAll(listOf(genre1, genre2)) + + expect(5) { genreRepository.getAll().size } + checkNotNull { genre1.uuid } + checkNotNull { genre2.uuid } + expect("Fantasy") { genre1.name } + expect("Horror") { genre2.name } + } + + @Test + fun delete() { + assertThrows { + genreRepository.delete( + Genre( + name = "Action" + ) + ) + } + + val genres = genreRepository.getAll() + genreRepository.delete(genres.first()) + expect(2) { genres.size - 1 } + } +} diff --git a/src/test/kotlin/fr/ziedelth/repositories/LangTypeRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/LangTypeRepositoryTest.kt new file mode 100644 index 0000000..c972734 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/LangTypeRepositoryTest.kt @@ -0,0 +1,92 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.LangType +import fr.ziedelth.plugins.langTypeRepository +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.util.* +import kotlin.test.expect + +internal class LangTypeRepositoryTest : AbstractAPITest() { + @Test + fun find() { + val langType = langTypeRepository.find(UUID.randomUUID()) + expect(null) { langType } + + val langTypes = langTypeRepository.getAll() + expect(langTypes.first().uuid) { langTypeRepository.find(langTypes.first().uuid)?.uuid } + } + + @Test + fun exists() { + expect(true) { langTypeRepository.exists("name", "SUBTITLES") } + expect(false) { langTypeRepository.exists("uuid", UUID.randomUUID()) } + } + + @Test + fun findAll() { + expect(emptyList()) { langTypeRepository.findAll(listOf(UUID.randomUUID())) } + + val langTypes = langTypeRepository.getAll() + val list = langTypeRepository.findAll(listOf(langTypes[0].uuid, langTypes[1].uuid)) + + expect(true) { list.any { it.uuid == langTypes.first().uuid } && list.any { it.uuid == langTypes[1].uuid } } + } + + @Test + fun getAll() { + expect(2) { langTypeRepository.getAll().size } + } + + @Test + fun save() { + assertThrows { + langTypeRepository.save(LangType(name = "SUBTITLES")) + } + + val langType = LangType(name = "DUB") + langTypeRepository.save(langType) + + expect(3) { langTypeRepository.getAll().size } + checkNotNull { langType.uuid } + expect("DUB") { langType.name } + } + + @Test + fun saveAll() { + assertThrows { + langTypeRepository.saveAll( + listOf( + LangType(name = "SUBTITLES"), + LangType(name = "VOICE") + ) + ) + } + + val langType1 = LangType(name = "DUB") + val langType2 = LangType(name = "DUB2") + langTypeRepository.saveAll(listOf(langType1, langType2)) + + expect(4) { langTypeRepository.getAll().size } + checkNotNull { langType1.uuid } + checkNotNull { langType2.uuid } + expect("DUB") { langType1.name } + expect("DUB2") { langType2.name } + } + + @Test + fun delete() { + assertThrows { + langTypeRepository.delete( + LangType( + name = "SUBTITLES" + ) + ) + } + + val langTypes = langTypeRepository.getAll() + langTypeRepository.delete(langTypes.first()) + expect(1) { langTypes.size - 1 } + } +} diff --git a/src/test/kotlin/fr/ziedelth/repositories/MangaRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/MangaRepositoryTest.kt new file mode 100644 index 0000000..c86cf0c --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/MangaRepositoryTest.kt @@ -0,0 +1,37 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.plugins.animeRepository +import fr.ziedelth.plugins.mangaRepository +import org.junit.jupiter.api.Test +import kotlin.test.expect + +internal class MangaRepositoryTest : AbstractAPITest() { + @Test + fun getByPage() { + val page1 = mangaRepository.getByPage("fr", 1, 2) + expect(2) { page1.size } + val page2 = mangaRepository.getByPage("fr", 2, 2) + expect(2) { page2.size } + } + + @Test + fun getByPageWithAnime() { + val anime = animeRepository.getAll().first() + + val page1 = mangaRepository.getByPageWithAnime(anime.uuid, 1, 2) + expect(2) { page1.size } + val page2 = mangaRepository.getByPageWithAnime(anime.uuid, 2, 2) + expect(2) { page2.size } + } + + @Test + fun getByPageWithList() { + val mangas = mangaRepository.getAll().take(3).map { it.uuid } + + val page1 = mangaRepository.getByPageWithList(mangas, 1, 2) + expect(2) { page1.size } + val page2 = mangaRepository.getByPageWithList(mangas, 2, 2) + expect(1) { page2.size } + } +} diff --git a/src/test/kotlin/fr/ziedelth/repositories/NewsRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/NewsRepositoryTest.kt new file mode 100644 index 0000000..5abb952 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/NewsRepositoryTest.kt @@ -0,0 +1,16 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.plugins.newsRepository +import org.junit.jupiter.api.Test +import kotlin.test.expect + +internal class NewsRepositoryTest : AbstractAPITest() { + @Test + fun getByPage() { + val page1 = newsRepository.getByPage("fr", 1, 2) + expect(2) { page1.size } + val page2 = newsRepository.getByPage("fr", 2, 2) + expect(0) { page2.size } + } +} diff --git a/src/test/kotlin/fr/ziedelth/repositories/PlatformRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/PlatformRepositoryTest.kt new file mode 100644 index 0000000..b987714 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/PlatformRepositoryTest.kt @@ -0,0 +1,108 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Platform +import fr.ziedelth.plugins.platformRepository +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.util.* +import kotlin.test.expect + +internal class PlatformRepositoryTest : AbstractAPITest() { + @Test + fun find() { + val platform = platformRepository.find(UUID.randomUUID()) + expect(null) { platform } + + val platforms = platformRepository.getAll() + expect(platforms.first().uuid) { platformRepository.find(platforms.first().uuid)?.uuid } + } + + @Test + fun exists() { + expect(true) { platformRepository.exists("name", "Netflix") } + expect(false) { platformRepository.exists("uuid", UUID.randomUUID()) } + } + + @Test + fun findAll() { + expect(emptyList()) { platformRepository.findAll(listOf(UUID.randomUUID())) } + + val platforms = platformRepository.getAll() + val list = platformRepository.findAll(listOf(platforms[0].uuid, platforms[1].uuid)) + + expect(true) { list.any { it.uuid == platforms.first().uuid } && list.any { it.uuid == platforms[1].uuid } } + } + + @Test + fun getAll() { + expect(3) { platformRepository.getAll().size } + } + + @Test + fun save() { + assertThrows { + platformRepository.save( + Platform( + name = "Netflix", + image = "hello", + url = "hello" + ) + ) + } + + val platform = Platform(name = "Animation Digital Network", image = "hello", url = "hello") + platformRepository.save(platform) + + expect(4) { platformRepository.getAll().size } + checkNotNull { platform.uuid } + expect("Animation Digital Network") { platform.name } + } + + @Test + fun saveAll() { + assertThrows { + platformRepository.saveAll( + listOf( + Platform( + name = "Netflix", + image = "hello", + url = "hello" + ), + Platform( + name = "Crunchyroll", + image = "hello", + url = "hello" + ) + ) + ) + } + + val platform1 = Platform(name = "Animation Digital Network", image = "hello", url = "hello") + val platform2 = Platform(name = "MangaNews", image = "hello", url = "hello") + platformRepository.saveAll(listOf(platform1, platform2)) + + expect(5) { platformRepository.getAll().size } + checkNotNull { platform1.uuid } + checkNotNull { platform2.uuid } + expect("Animation Digital Network") { platform1.name } + expect("MangaNews") { platform2.name } + } + + @Test + fun delete() { + assertThrows { + platformRepository.delete( + Platform( + name = "Netflix", + image = "hello", + url = "hello" + ) + ) + } + + val platforms = platformRepository.getAll() + platformRepository.delete(platforms.first()) + expect(2) { platforms.size - 1 } + } +} diff --git a/src/test/kotlin/fr/ziedelth/repositories/SimulcastRepositoryTest.kt b/src/test/kotlin/fr/ziedelth/repositories/SimulcastRepositoryTest.kt new file mode 100644 index 0000000..8d6d7f0 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/repositories/SimulcastRepositoryTest.kt @@ -0,0 +1,109 @@ +package fr.ziedelth.repositories + +import fr.ziedelth.AbstractAPITest +import fr.ziedelth.entities.Simulcast +import fr.ziedelth.plugins.countryRepository +import fr.ziedelth.plugins.simulcastRepository +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.util.* +import kotlin.test.expect + +internal class SimulcastRepositoryTest : AbstractAPITest() { + @Test + fun find() { + val simulcast = simulcastRepository.find(UUID.randomUUID()) + expect(null) { simulcast } + + val simulcasts = simulcastRepository.getAll() + expect(simulcasts.first().uuid) { simulcastRepository.find(simulcasts.first().uuid)?.uuid } + } + + @Test + fun exists() { + expect(true) { simulcastRepository.exists("year", 2020) } + expect(false) { simulcastRepository.exists("uuid", UUID.randomUUID()) } + } + + @Test + fun findAll() { + expect(emptyList()) { simulcastRepository.findAll(listOf(UUID.randomUUID())) } + + val simulcasts = simulcastRepository.getAll() + val list = simulcastRepository.findAll(listOf(simulcasts[0].uuid, simulcasts[1].uuid)) + + expect(true) { list.any { it.uuid == simulcasts.first().uuid } && list.any { it.uuid == simulcasts[1].uuid } } + } + + @Test + fun getAll() { + expect(2) { simulcastRepository.getAll().size } + } + + @Test + fun save() { + assertThrows { + simulcastRepository.save(Simulcast(year = 2020, season = "WINTER")) + } + + val simulcast = Simulcast.getSimulcast(2021, 1) + simulcastRepository.save(simulcast) + + expect(3) { simulcastRepository.getAll().size } + checkNotNull { simulcast.uuid } + expect("WINTER") { simulcast.season } + } + + @Test + fun saveAll() { + assertThrows { + simulcastRepository.saveAll( + listOf( + Simulcast(year = 2020, season = "WINTER"), + Simulcast(year = 2020, season = "SPRING") + ) + ) + } + + val simulcast1 = Simulcast.getSimulcast(2019, 1) + val simulcast2 = Simulcast.getSimulcast(2019, 11) + simulcastRepository.saveAll(listOf(simulcast1, simulcast2)) + + expect(4) { simulcastRepository.getAll().size } + checkNotNull { simulcast1.uuid } + checkNotNull { simulcast2.uuid } + expect("WINTER") { simulcast1.season } + expect("AUTUMN") { simulcast2.season } + } + + @Test + fun delete() { + assertThrows { + simulcastRepository.delete( + Simulcast( + UUID.randomUUID(), + season = "WINTER", + year = 2020 + ) + ) + } + + val simulcasts = simulcastRepository.getAll() + simulcastRepository.delete(simulcasts.first()) + expect(1) { simulcasts.size - 1 } + } + + @Test + fun getAllByTag() { + val country = countryRepository.getAll().first() + val simulcasts = simulcastRepository.getAll(country.tag) + expect(1) { simulcasts.size } + expect("WINTER") { simulcasts.first().season } + } + + @Test + fun findBySeasonAndYear() { + val simulcast = simulcastRepository.findBySeasonAndYear("WINTER", 2020) + checkNotNull(simulcast?.uuid) + } +} diff --git a/src/test/kotlin/fr/ziedelth/utils/DatabaseTest.kt b/src/test/kotlin/fr/ziedelth/utils/DatabaseTest.kt new file mode 100644 index 0000000..0ed51c7 --- /dev/null +++ b/src/test/kotlin/fr/ziedelth/utils/DatabaseTest.kt @@ -0,0 +1,55 @@ +package fr.ziedelth.utils + +import org.hibernate.Session +import org.hibernate.SessionFactory +import org.hibernate.boot.registry.StandardServiceRegistryBuilder +import org.hibernate.cfg.Configuration +import org.reflections.Reflections +import java.io.File +import java.io.Serializable +import kotlin.system.exitProcess + +object DatabaseTest { + private var sessionFactory: SessionFactory + + init { + try { + val file = File( + javaClass.classLoader.getResource("hibernate.cfg.xml")?.file + ?: throw Exception("hibernate.cfg.xml not found") + ) + + Configuration().let { configuration -> + getEntities().forEach { configuration.addAnnotatedClass(it) } + + configuration.configure(file) + sessionFactory = configuration.buildSessionFactory( + StandardServiceRegistryBuilder().applySettings(configuration.properties).build() + ) + } + } catch (e: Exception) { + e.printStackTrace() + exitProcess(1) + } + } + + private fun getEntities(): MutableSet> = + Reflections("fr.ziedelth.entities").getSubTypesOf(Serializable::class.java) + + fun getSession(): Session = sessionFactory.openSession() + + fun clean() { + val session = getSession() + val transaction = session.beginTransaction() + + try { + getEntities().forEach { session.createQuery("DELETE FROM ${it.simpleName}").executeUpdate() } + transaction.commit() + } catch (e: Exception) { + transaction.rollback() + throw e + } finally { + session.close() + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/ziedelth/PluginManagerTest.kt b/src/test/kotlin/fr/ziedelth/utils/plugins/PluginManagerTest.kt similarity index 91% rename from src/test/kotlin/fr/ziedelth/PluginManagerTest.kt rename to src/test/kotlin/fr/ziedelth/utils/plugins/PluginManagerTest.kt index 50deb94..a1ed971 100644 --- a/src/test/kotlin/fr/ziedelth/PluginManagerTest.kt +++ b/src/test/kotlin/fr/ziedelth/utils/plugins/PluginManagerTest.kt @@ -1,7 +1,6 @@ -package fr.ziedelth +package fr.ziedelth.utils.plugins import fr.ziedelth.events.ExampleEvent -import fr.ziedelth.utils.plugins.PluginManager import fr.ziedelth.utils.plugins.events.EventHandler import fr.ziedelth.utils.plugins.events.Listener import org.junit.jupiter.api.Assertions.assertEquals diff --git a/src/test/resources/hibernate.cfg.xml b/src/test/resources/hibernate.cfg.xml new file mode 100644 index 0000000..7522471 --- /dev/null +++ b/src/test/resources/hibernate.cfg.xml @@ -0,0 +1,16 @@ + + + + + + org.h2.Driver + jdbc:h2:mem:test;DATABASE_TO_UPPER=false + + + org.hibernate.dialect.H2Dialect + false + create-drop + + \ No newline at end of file