├── settings.gradle.kts ├── gradle.properties ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── src ├── main │ ├── resources │ │ ├── images │ │ │ ├── amado.jpg │ │ │ ├── code.jpg │ │ │ ├── koji.jpg │ │ │ ├── boruto.png │ │ │ ├── ishiki.jpg │ │ │ ├── kakashi.png │ │ │ ├── kawaki.jpg │ │ │ ├── mitsuki.jpg │ │ │ ├── naruto.jpg │ │ │ ├── sakura.jpg │ │ │ ├── sarada.jpg │ │ │ ├── sasuke.jpg │ │ │ ├── urashiki.jpg │ │ │ ├── momoshiki.jpg │ │ │ └── orochimaru.jpg │ │ ├── application.conf │ │ └── logback.xml │ └── kotlin │ │ └── com │ │ └── aarh │ │ ├── utils │ │ ├── Const.kt │ │ ├── HeroPag5Object.kt │ │ ├── HeroPag3Object.kt │ │ ├── HeroPag2Object.kt │ │ ├── HeroPag4Object.kt │ │ ├── HeroPag1Object.kt │ │ └── AllHeroesObject.kt │ │ ├── plugins │ │ ├── Monitoring.kt │ │ ├── Koin.kt │ │ ├── Serialization.kt │ │ ├── StatusPages.kt │ │ ├── DefaultHeaders.kt │ │ └── Routing.kt │ │ ├── routes │ │ ├── Root.kt │ │ ├── SearchHero.kt │ │ ├── AllHeroes.kt │ │ └── AllHeroesAlternative.kt │ │ ├── repository │ │ ├── HeroRepositoryAlternative.kt │ │ ├── HeroRepository.kt │ │ ├── HeroRepositoryImplAlternative.kt │ │ └── HeroRepositoryImpl.kt │ │ ├── models │ │ ├── ApiResponse.kt │ │ └── Hero.kt │ │ ├── Application.kt │ │ └── di │ │ └── KoinModule.kt └── test │ └── kotlin │ └── com │ └── aarh │ └── ApplicationTest.kt ├── Dockerfile ├── .gitignore ├── gradlew.bat └── gradlew /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "com.aarh.borutoserver" 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | org.gradle.caching=true 3 | org.gradle.configuration-cache=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/images/amado.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/amado.jpg -------------------------------------------------------------------------------- /src/main/resources/images/code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/code.jpg -------------------------------------------------------------------------------- /src/main/resources/images/koji.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/koji.jpg -------------------------------------------------------------------------------- /src/main/resources/images/boruto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/boruto.png -------------------------------------------------------------------------------- /src/main/resources/images/ishiki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/ishiki.jpg -------------------------------------------------------------------------------- /src/main/resources/images/kakashi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/kakashi.png -------------------------------------------------------------------------------- /src/main/resources/images/kawaki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/kawaki.jpg -------------------------------------------------------------------------------- /src/main/resources/images/mitsuki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/mitsuki.jpg -------------------------------------------------------------------------------- /src/main/resources/images/naruto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/naruto.jpg -------------------------------------------------------------------------------- /src/main/resources/images/sakura.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/sakura.jpg -------------------------------------------------------------------------------- /src/main/resources/images/sarada.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/sarada.jpg -------------------------------------------------------------------------------- /src/main/resources/images/sasuke.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/sasuke.jpg -------------------------------------------------------------------------------- /src/main/resources/images/urashiki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/urashiki.jpg -------------------------------------------------------------------------------- /src/main/resources/images/momoshiki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/momoshiki.jpg -------------------------------------------------------------------------------- /src/main/resources/images/orochimaru.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanRH14/BorutoServer/HEAD/src/main/resources/images/orochimaru.jpg -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/utils/Const.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.utils 2 | 3 | const val PREVIOUS_PAGE_KEY = "prevPage" 4 | const val NEXT_PAGE_KEY = "nextPage" -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/plugins/Monitoring.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.plugins 2 | 3 | import io.ktor.server.application.* 4 | import io.ktor.server.plugins.calllogging.* 5 | 6 | fun Application.configureMonitoring() { 7 | install(CallLogging) 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | development = true 3 | deployment { 4 | port = 8080 5 | port = ${?PORT} 6 | watch = [ classes, resources ] 7 | } 8 | application { 9 | modules = [ com.aarh.ApplicationKt.module ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/plugins/Koin.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.plugins 2 | 3 | import com.aarh.di.koinModule 4 | import io.ktor.server.application.* 5 | import org.koin.core.context.startKoin 6 | 7 | fun Application.configurationKoin() { 8 | startKoin { 9 | modules(koinModule) 10 | } 11 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Usamos imagen base de OpenJDK con Gradle 2 | FROM gradle:8.4-jdk17 AS build 3 | WORKDIR /app 4 | COPY . . 5 | RUN gradle build --no-daemon 6 | 7 | # Imagen final con JRE 8 | FROM eclipse-temurin:17-jre 9 | WORKDIR /app 10 | COPY --from=build /app/build/libs/*.jar app.jar 11 | EXPOSE 8080 12 | ENTRYPOINT ["java", "-jar", "app.jar"] -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/plugins/Serialization.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.plugins 2 | 3 | import io.ktor.serialization.kotlinx.json.* 4 | import io.ktor.server.application.* 5 | import io.ktor.server.plugins.contentnegotiation.* 6 | 7 | fun Application.configureSerialization() { 8 | install(ContentNegotiation) { 9 | json() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/routes/Root.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.routes 2 | 3 | import io.ktor.http.* 4 | import io.ktor.server.response.* 5 | import io.ktor.server.routing.* 6 | 7 | fun Route.root() { 8 | get("/") { 9 | call.respond( 10 | message = "Welcome to Boruto API!", 11 | status = HttpStatusCode.OK, 12 | ) 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/repository/HeroRepositoryAlternative.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.repository 2 | 3 | import com.aarh.models.ApiResponse 4 | import com.aarh.models.Hero 5 | 6 | interface HeroRepositoryAlternative { 7 | 8 | val heroes: List 9 | 10 | suspend fun getAllHeroes(page: Int = 1, limit: Int = 3): ApiResponse 11 | suspend fun searchHeroes(name: String?): ApiResponse 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/models/ApiResponse.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.models 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class ApiResponse( 7 | val success: Boolean, 8 | val message: String? = null, 9 | val prevPage: Int? = null, 10 | val nextPage: Int? = null, 11 | val heroes: List = emptyList(), 12 | val lastUpdated: Long? = null, 13 | ) 14 | -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/models/Hero.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.models 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Hero( 7 | val id: Int, 8 | val name: String, 9 | val image: String, 10 | val about: String, 11 | val rating: Double, 12 | val power: Int, 13 | val month: String, 14 | val day: String, 15 | val family: List, 16 | val abilities: List, 17 | val natureTypes: List, 18 | ) 19 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/Application.kt: -------------------------------------------------------------------------------- 1 | package com.aarh 2 | 3 | import com.aarh.plugins.* 4 | import io.ktor.server.application.Application 5 | 6 | fun main(args: Array) { 7 | io.ktor.server.netty.EngineMain.main(args) 8 | } 9 | 10 | @Suppress("unused") 11 | fun Application.module() { 12 | configurationKoin() 13 | configureSerialization() 14 | configureMonitoring() 15 | configureRouting() 16 | configurationDefaultHeader() 17 | configurationStatusPages() 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/repository/HeroRepository.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.repository 2 | 3 | import com.aarh.models.ApiResponse 4 | import com.aarh.models.Hero 5 | 6 | interface HeroRepository { 7 | val heroes: Map> 8 | val page1: List 9 | val page2: List 10 | val page3: List 11 | val page4: List 12 | val page5: List 13 | 14 | suspend fun getAllHeroes(page: Int = 1): ApiResponse 15 | 16 | suspend fun searchHeroes(name: String?): ApiResponse 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/plugins/StatusPages.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.plugins 2 | 3 | import io.ktor.http.* 4 | import io.ktor.server.application.* 5 | import io.ktor.server.plugins.statuspages.* 6 | import io.ktor.server.response.* 7 | 8 | fun Application.configurationStatusPages() { 9 | install(StatusPages) { 10 | status(HttpStatusCode.NotFound) { call, status -> 11 | call.respond( 12 | message = "404: Page not Found.", 13 | status = status, 14 | ) 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/plugins/DefaultHeaders.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.plugins 2 | 3 | import io.ktor.http.* 4 | import io.ktor.server.application.* 5 | import io.ktor.server.plugins.defaultheaders.* 6 | import java.time.Duration 7 | 8 | fun Application.configurationDefaultHeader() { 9 | install(DefaultHeaders) { 10 | val oneYearInSeconds = Duration.ofDays(365).seconds 11 | header( 12 | name = HttpHeaders.CacheControl, 13 | value = "public, max-age=$oneYearInSeconds, immutable", 14 | ) 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/di/KoinModule.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.di 2 | 3 | import com.aarh.repository.HeroRepository 4 | import com.aarh.repository.HeroRepositoryAlternative 5 | import com.aarh.repository.HeroRepositoryImplAlternative 6 | import com.aarh.repository.HeroRepositoryImpl 7 | import org.koin.core.module.dsl.bind 8 | import org.koin.core.module.dsl.singleOf 9 | import org.koin.dsl.module 10 | 11 | val koinModule = module { 12 | singleOf(::HeroRepositoryImpl) { bind() } 13 | singleOf(::HeroRepositoryImplAlternative) { bind() } 14 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !site/src/main/**/build/ 5 | !site/src/test/**/build/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | bin/ 16 | !site/src/main/**/bin/ 17 | !site/src/test/**/bin/ 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | out/ 25 | !site/src/main/**/out/ 26 | !site/src/test/**/out/ 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | 35 | ### VS Code ### 36 | .vscode/ -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/plugins/Routing.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.plugins 2 | 3 | import com.aarh.routes.getAllHeroesAlternative 4 | import com.aarh.routes.root 5 | import com.aarh.routes.searchHeroes 6 | import io.ktor.server.application.* 7 | import io.ktor.server.http.content.* 8 | import io.ktor.server.routing.* 9 | 10 | fun Application.configureRouting() { 11 | routing { 12 | root() 13 | //getAllHeroes() 14 | getAllHeroesAlternative() 15 | searchHeroes() 16 | staticResources(remotePath = "/", basePackage = "", index = "image") { 17 | enableAutoHeadResponse() 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/routes/SearchHero.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.routes 2 | 3 | import com.aarh.repository.HeroRepository 4 | import io.ktor.http.* 5 | import io.ktor.server.response.* 6 | import io.ktor.server.routing.* 7 | import org.koin.java.KoinJavaComponent.inject 8 | 9 | fun Route.searchHeroes() { 10 | val heroRepository: HeroRepository by inject(HeroRepository::class.java) 11 | 12 | get("/boruto/heroes/search") { 13 | val name = call.request.queryParameters["name"] 14 | val apiResponse = heroRepository.searchHeroes(name = name) 15 | 16 | call.respond( 17 | message = apiResponse, 18 | status = HttpStatusCode.OK, 19 | ) 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/routes/AllHeroes.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.routes 2 | 3 | import com.aarh.models.ApiResponse 4 | import com.aarh.repository.HeroRepository 5 | import io.ktor.http.* 6 | import io.ktor.server.response.* 7 | import io.ktor.server.routing.* 8 | import org.koin.java.KoinJavaComponent.inject 9 | import java.lang.IllegalArgumentException 10 | import java.lang.NumberFormatException 11 | 12 | fun Route.getAllHeroes() { 13 | val heroRepository: HeroRepository by inject(HeroRepository::class.java) 14 | 15 | get("/boruto/heroes") { 16 | try { 17 | val page = call.request.queryParameters["page"]?.toInt() ?: 1 18 | require(page in 1..5) 19 | 20 | val apiResponse = heroRepository.getAllHeroes(page) 21 | call.respond( 22 | message = apiResponse, 23 | status = HttpStatusCode.OK, 24 | ) 25 | } catch (e: NumberFormatException) { 26 | call.respond( 27 | message = ApiResponse( 28 | success = false, 29 | message = "Only Numbers Allowed.", 30 | ), 31 | status = HttpStatusCode.BadRequest, 32 | ) 33 | } catch (e: IllegalArgumentException) { 34 | call.respond( 35 | message = ApiResponse( 36 | success = false, 37 | message = "Heroes not Found.", 38 | ), 39 | status = HttpStatusCode.NotFound, 40 | ) 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/routes/AllHeroesAlternative.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.routes 2 | 3 | import com.aarh.models.ApiResponse 4 | import com.aarh.repository.HeroRepositoryAlternative 5 | import io.ktor.http.* 6 | import io.ktor.server.response.* 7 | import io.ktor.server.routing.* 8 | import org.koin.java.KoinJavaComponent.inject 9 | 10 | fun Route.getAllHeroesAlternative() { 11 | val heroRepositoryAlternative: HeroRepositoryAlternative by inject(HeroRepositoryAlternative::class.java) 12 | 13 | get("/boruto/heroes") { 14 | try { 15 | val page = call.request.queryParameters["page"]?.toInt() ?: 1 16 | val limit = call.request.queryParameters["limit"]?.toInt() ?: 3 17 | 18 | val apiResponse = heroRepositoryAlternative.getAllHeroes(page = page, limit = limit) 19 | call.respond( 20 | message = apiResponse, 21 | status = HttpStatusCode.OK 22 | ) 23 | } catch (e: NumberFormatException) { 24 | call.respond( 25 | message = ApiResponse( 26 | success = false, 27 | message = "Only Numbers Allowed." 28 | ), 29 | status = HttpStatusCode.BadRequest 30 | ) 31 | } catch (e: IllegalArgumentException) { 32 | call.respond( 33 | message = ApiResponse( 34 | success = false, 35 | message = "Heroes not Found." 36 | ), 37 | status = HttpStatusCode.NotFound 38 | ) 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | kotlin-version = "2.1.0" 3 | ktor-version = "3.0.2" 4 | logback-version = "1.4.14" 5 | koin-bom = "4.0.0" 6 | 7 | [libraries] 8 | ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation-jvm", version.ref = "ktor-version" } 9 | ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor-version" } 10 | ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json-jvm", version.ref = "ktor-version" } 11 | ktor-server-call-logging = { module = "io.ktor:ktor-server-call-logging-jvm", version.ref = "ktor-version" } 12 | ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor-version" } 13 | logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback-version" } 14 | ktor-server-test-host = { module = "io.ktor:ktor-server-test-host-jvm", version.ref = "ktor-version" } 15 | kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin-version" } 16 | koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" } 17 | koin-core = { module = "io.insert-koin:koin-core" } 18 | ktor-server-defaultheaders = { module = "io.ktor:ktor-server-default-headers", version.ref = "ktor-version" } 19 | koin-logger-slf4j = { module = "io.insert-koin:koin-logger-slf4j", version.ref = "ktor-version" } 20 | ktor-server-status-pages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor-version" } 21 | 22 | [plugins] 23 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-version" } 24 | ktor = { id = "io.ktor.plugin", version.ref = "ktor-version" } 25 | kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-version" } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/repository/HeroRepositoryImplAlternative.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.repository 2 | 3 | import com.aarh.models.ApiResponse 4 | import com.aarh.models.Hero 5 | import com.aarh.utils.AllHeroesObject 6 | 7 | class HeroRepositoryImplAlternative : HeroRepositoryAlternative { 8 | 9 | override val heroes: List 10 | get() = AllHeroesObject.heroes 11 | 12 | override suspend fun getAllHeroes(page: Int, limit: Int): ApiResponse { 13 | return ApiResponse( 14 | success = true, 15 | message = "Ok", 16 | prevPage = calculatePage( 17 | heroes = heroes, 18 | page = page, 19 | limit = limit 20 | )["prevPage"], 21 | nextPage = calculatePage( 22 | heroes = heroes, 23 | page = page, 24 | limit = limit 25 | )["nextPage"], 26 | heroes = provideHeroes( 27 | heroes = heroes, 28 | page = page, 29 | limit = limit 30 | ), 31 | lastUpdated = System.currentTimeMillis() 32 | ) 33 | } 34 | 35 | override suspend fun searchHeroes(name: String?): ApiResponse { 36 | return ApiResponse( 37 | success = true, 38 | message = "Ok", 39 | heroes = findHeroes(query = name) 40 | ) 41 | } 42 | 43 | private fun calculatePage( 44 | heroes: List, 45 | page: Int, 46 | limit: Int 47 | ): Map { 48 | val allHeroes = heroes.windowed( 49 | size = limit, 50 | step = limit, 51 | partialWindows = true, 52 | ) 53 | require(page <= allHeroes.size) 54 | val prevPage = if (page == 1) null else page - 1 55 | val nextPage = if (page >= allHeroes.size) null else page + 1 56 | 57 | return mapOf( 58 | "prevPage" to prevPage, 59 | "nextPage" to nextPage 60 | ) 61 | } 62 | 63 | private fun provideHeroes( 64 | heroes: List, 65 | page: Int, 66 | limit: Int, 67 | ): List { 68 | val allHeroes = heroes.windowed( 69 | size = limit, 70 | step = limit, 71 | partialWindows = true 72 | ) 73 | require(page > 0 && page <= allHeroes.size) 74 | return allHeroes[page - 1] 75 | } 76 | 77 | private fun findHeroes(query: String?): List { 78 | val founded = mutableListOf() 79 | return if (!query.isNullOrEmpty()) { 80 | heroes.forEach { hero -> 81 | if (hero.name.lowercase().contains(query.lowercase())) { 82 | founded.add(hero) 83 | } 84 | } 85 | founded 86 | } else { 87 | emptyList() 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/utils/HeroPag5Object.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.utils 2 | 3 | import com.aarh.models.Hero 4 | 5 | object HeroPag5Object { 6 | val hero13 = Hero( 7 | id = 13, 8 | name = "Code", 9 | image = "/images/code.jpg", 10 | about = "Code (コード, Kōdo) is the last active Inner from Kara. Carrying Isshiki Ōtsutsuki's legacy within him, he inherits the Ōtsutsuki Clan's will to become a Celestial Being and continually evolve. At the time Kawaki was brought to Kara, Code was one of fifteen candidates in Jigen and Amado's Ōtsutsuki ritual to screen for a Kāma vessel for Isshiki. Only Kawaki survived to become an actual vessel.", 11 | rating = 4.8, 12 | power = 99, 13 | month = "Jan", 14 | day = "1st", 15 | family = listOf( 16 | "Unknown", 17 | ), 18 | abilities = listOf( 19 | "White Karma", 20 | "Transformation", 21 | "Genjutsu", 22 | ), 23 | natureTypes = listOf( 24 | "Unknown", 25 | ), 26 | ) 27 | 28 | val hero14 = Hero( 29 | id = 14, 30 | name = "Amado", 31 | image = "/images/amado.jpg", 32 | about = "Amado (アマド, Amado) is a former Inner from the organisation Kara and the head of its research and development division. He has since defected to Konohagakure, where he used a mix of bluffs and gifts to gain official citizenship for the Hokage's protection. Amado had a daughter who died twelve years prior to the reign of the Seventh Hokage. In his quest to kill Isshiki Ōtsutsuki, Amado joined Kara and was granted the rank of Inner, serving as the head of its research and development division.", 33 | rating = 5.0, 34 | power = 90, 35 | month = "Jan", 36 | day = "1st", 37 | family = listOf( 38 | "Unknown", 39 | ), 40 | abilities = listOf( 41 | "Science", 42 | "Intelligence", 43 | "Trickery", 44 | ), 45 | natureTypes = listOf( 46 | "Unknown", 47 | ), 48 | ) 49 | 50 | val hero15 = Hero( 51 | id = 15, 52 | name = "Koji", 53 | image = "/images/koji.jpg", 54 | about = "Koji Kashin (果心居士, Kashin Koji) is a clone of Jiraiya that was created by Amado for the purpose of killing Isshiki Ōtsutsuki. A former Inner of Kara, he was in charge of the sector on the outskirts of the Land of Fire. An enigmatic man, Koji has a very stoic and straightforward nature that follows a no-nonsense view. Arrogant as he may appear, he has consistently shown himself to be a very rational and perceptive man.", 55 | rating = 4.5, 56 | power = 90, 57 | month = "Jan", 58 | day = "1st", 59 | family = listOf( 60 | "Jiraiya", 61 | ), 62 | abilities = listOf( 63 | "Senin Mode", 64 | "Rasengan", 65 | "Shadow Clone", 66 | ), 67 | natureTypes = listOf( 68 | "Fire", 69 | "Earth", 70 | ), 71 | ) 72 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/utils/HeroPag3Object.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.utils 2 | 3 | import com.aarh.models.Hero 4 | 5 | object HeroPag3Object { 6 | val hero7 = Hero( 7 | id = 7, 8 | name = "Kawaki", 9 | image = "/images/kawaki.jpg", 10 | about = "Kawaki (カワキ, Kawaki) is a child raised by Kara to be the future vessel for Isshiki Ōtsutsuki and the key to the fulfilment of their greatest wish.[1] After being brought to Konohagakure by Team 7, he is taken in by Naruto Uzumaki who raises him as his own, during which he develops a brotherly bond with Boruto Uzumaki to solve the mystery of the Kāma.", 11 | rating = 4.2, 12 | power = 92, 13 | month = "Jan", 14 | day = "1st", 15 | family = listOf( 16 | "Kokatsu", 17 | ), 18 | abilities = listOf( 19 | "Karma", 20 | "Transformation", 21 | "Strength", 22 | ), 23 | natureTypes = listOf( 24 | "Fire", 25 | ), 26 | ) 27 | 28 | val hero8 = Hero( 29 | id = 8, 30 | name = "Orochimaru", 31 | image = "/images/orochimaru.jpg", 32 | about = "Orochimaru (大蛇丸, Orochimaru) is one of Konohagakure's legendary Sannin. With a life-ambition to learn all of the world's secrets, Orochimaru seeks immortality so that he might live all of the lives necessary to accomplish his task. After being caught red-handed performing unethical experiments on his fellow citizens for the sake of this immortality, Orochimaru defected from Konoha.", 33 | rating = 4.5, 34 | power = 97, 35 | month = "Oct", 36 | day = "27th", 37 | family = listOf( 38 | "Mitsuki", 39 | "Log", 40 | ), 41 | abilities = listOf( 42 | "Senin Mode", 43 | "Transformation", 44 | "Science", 45 | ), 46 | natureTypes = listOf( 47 | "Lightning", 48 | "Wind", 49 | "Fire", 50 | "Earth", 51 | "Water", 52 | ), 53 | ) 54 | 55 | val hero9 = Hero( 56 | id = 9, 57 | name = "Kakashi", 58 | image = "/images/kakashi.png", 59 | about = "Kakashi Hatake (はたけカカシ, Hatake Kakashi) is a shinobi of Konohagakure's Hatake clan. Famed as Kakashi of the Sharingan (写輪眼のカカシ, Sharingan no Kakashi), he is one of Konoha's most talented ninja, regularly looked to for advice and leadership despite his personal dislike of responsibility. To his students on Team 7, Kakashi emphasises the importance of teamwork; he himself received this lesson, along with the Sharingan, from his childhood friend, Obito Uchiha.", 60 | rating = 4.5, 61 | power = 96, 62 | month = "Sep", 63 | day = "15th", 64 | family = listOf( 65 | "Sakumo", 66 | ), 67 | abilities = listOf( 68 | "Intelligence", 69 | "Strength", 70 | ), 71 | natureTypes = listOf( 72 | "Lightning", 73 | "Wind", 74 | "Fire", 75 | "Earth", 76 | "Water", 77 | ), 78 | ) 79 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/utils/HeroPag2Object.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.utils 2 | 3 | import com.aarh.models.Hero 4 | 5 | object HeroPag2Object { 6 | val hero4 = Hero( 7 | id = 4, 8 | name = "Boruto", 9 | image = "/images/boruto.png", 10 | about = "Boruto Uzumaki (うずまきボルト, Uzumaki Boruto) is a shinobi from Konohagakure's Uzumaki clan and a direct descendant of the Hyūga clan through his mother. While initially resentful of his father and his absence since becoming Hokage, Boruto eventually comes to respect his father and duties.", 11 | rating = 4.9, 12 | power = 95, 13 | month = "Mar", 14 | day = "27th", 15 | family = listOf( 16 | "Naruto", 17 | "Hinata", 18 | "Hanabi", 19 | "Himawari", 20 | "Kawaki", 21 | ), 22 | abilities = listOf( 23 | "Karma", 24 | "Jogan", 25 | "Rasengan", 26 | "Intelligence", 27 | ), 28 | natureTypes = listOf( 29 | "Lightning", 30 | "Wind", 31 | "Water", 32 | ), 33 | ) 34 | 35 | val hero5 = Hero( 36 | id = 5, 37 | name = "Sarada", 38 | image = "/images/sarada.jpg", 39 | about = "Sarada Uchiha (うちはサラダ, Uchiha Sarada) is a kunoichi from Konohagakure's Uchiha clan. Because she was raised only by her mother without having her father around, Sarada initially struggles to understand who she is or what she's supposed to be. After meeting him with the help of Naruto Uzumaki, Sarada comes to believe that she is defined by the connections she has with others, and as a member of Team Konohamaru, she seeks to someday become Hokage so that she can connect with as many people as possible.", 40 | rating = 4.9, 41 | power = 95, 42 | month = "Mar", 43 | day = "31st", 44 | family = listOf( 45 | "Sasuke Uchiha", 46 | "Sakura Uchiha", 47 | ), 48 | abilities = listOf( 49 | "Sharingan", 50 | "Strength", 51 | "Intelligence", 52 | ), 53 | natureTypes = listOf( 54 | "Lightning", 55 | "Wind", 56 | "Fire", 57 | ), 58 | ) 59 | 60 | val hero6 = Hero( 61 | id = 6, 62 | name = "Mitsuki", 63 | image = "/images/mitsuki.jpg", 64 | about = "Mitsuki (ミツキ, Mitsuki) is a synthetic human that was created as a partial clone of Orochimaru. Immigrating to Konohagakure to confirm whether or not Boruto Uzumaki was his \"sun\", he became a shinobi and was placed on Team Konohamaru. Mitsuki was created as a clone of Orochimaru, being cultivated from the same embryo as at least one older \"Mitsuki\", and raised in a test tube.", 65 | rating = 4.9, 66 | power = 95, 67 | month = "Jul", 68 | day = "25th", 69 | family = listOf( 70 | "Orochimaru", 71 | "Log", 72 | ), 73 | abilities = listOf( 74 | "Senin Mode", 75 | "Transformation", 76 | "Intelligence", 77 | ), 78 | natureTypes = listOf( 79 | "Lightning", 80 | "Wind", 81 | ), 82 | ) 83 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/utils/HeroPag4Object.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.utils 2 | 3 | import com.aarh.models.Hero 4 | 5 | object HeroPag4Object { 6 | val hero10 = Hero( 7 | id = 10, 8 | name = "Isshiki", 9 | image = "/images/ishiki.jpg", 10 | about = "A thousand years ago, Isshiki came to Earth alongside Kaguya with the objective to plant a Tree to harvest its Chakra Fruit. While Kaguya, being lower-ranked, was planned to be sacrificed to create the Chakra Fruit, she instead turned on Isshiki, leaving him on the verge of death after destroying Isshiki's lower half. Encountering Jigen and not having the strength to implant a Kāma on him, Isshiki devised a desperate plan and shrunk himself to enter the monk's ear in order to survive his injury by absorbing Jigen's nutrients.", 11 | rating = 5.0, 12 | power = 100, 13 | month = "Jan", 14 | day = "1st", 15 | family = listOf( 16 | "Otsutsuki Clan", 17 | ), 18 | abilities = listOf( 19 | "Sukunahikona", 20 | "Daikokuten", 21 | "Byakugan", 22 | "Space–Time", 23 | "Intelligence", 24 | ), 25 | natureTypes = listOf( 26 | "Fire", 27 | ), 28 | ) 29 | 30 | val hero11 = Hero( 31 | id = 11, 32 | name = "Momoshiki", 33 | image = "/images/momoshiki.jpg", 34 | about = "Momoshiki Ōtsutsuki (大筒木モモシキ, Ōtsutsuki Momoshiki) was a member of the Ōtsutsuki clan's main family, sent to investigate the whereabouts of Kaguya and her God Tree and then attempting to cultivate a new one out of the chakra of the Seventh Hokage. In the process of being killed by Boruto Uzumaki, Momoshiki placed a Kāma on him, allowing his spirit to remain intact through the mark.", 35 | rating = 3.9, 36 | power = 98, 37 | month = "Jan", 38 | day = "1st", 39 | family = listOf( 40 | "Otsutsuki Clan", 41 | ), 42 | abilities = listOf( 43 | "Rinnegan", 44 | "Byakugan", 45 | "Strength", 46 | ), 47 | natureTypes = listOf( 48 | "Fire", 49 | "Lightning", 50 | "Wind", 51 | "Water", 52 | "Earth", 53 | ), 54 | ) 55 | 56 | val hero12 = Hero( 57 | id = 12, 58 | name = "Urashiki", 59 | image = "/images/urashiki.jpg", 60 | about = "Urashiki Ōtsutsuki (大筒木ウラシキ, Ōtsutsuki Urashiki) was a low-ranking member of the Ōtsutsuki clan's main family, sent to assist Momoshiki and Kinshiki on their mission to investigate Kaguya's whereabouts and gather the chakra of the God Tree on Earth. Compared to his comrades, Urashiki had been shown to have a rather laid-back and jovial personality. He was quite willing to joke along with Momoshiki and Kinshiki, and disparaged on how serious they are.", 61 | rating = 3.4, 62 | power = 95, 63 | month = "Jan", 64 | day = "1st", 65 | family = listOf( 66 | "Otsutsuki Clan", 67 | ), 68 | abilities = listOf( 69 | "Space–Time", 70 | "Rinnegan", 71 | "Byakugan", 72 | ), 73 | natureTypes = listOf( 74 | "Fire", 75 | "Lightning", 76 | "Wind", 77 | "Earth", 78 | ), 79 | ) 80 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/utils/HeroPag1Object.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.utils 2 | 3 | import com.aarh.models.Hero 4 | 5 | object HeroPag1Object { 6 | val hero1: Hero = Hero( 7 | id = 1, 8 | name = "Sasuke", 9 | image = "/images/sasuke.jpg", 10 | about = "Sasuke Uchiha (うちはサスケ, Uchiha Sasuke) is one of the last surviving members of Konohagakure's Uchiha clan. After his older brother, Itachi, slaughtered their clan, Sasuke made it his mission in life to avenge them by killing Itachi. He is added to Team 7 upon becoming a ninja and, through competition with his rival and best friend, Naruto Uzumaki.", 11 | rating = 5.0, 12 | power = 98, 13 | month = "July", 14 | day = "23rd", 15 | family = listOf( 16 | "Fugaku", 17 | "Mikoto", 18 | "Itachi", 19 | "Sarada", 20 | "Sakura", 21 | ), 22 | abilities = listOf( 23 | "Sharingan", 24 | "Rinnegan", 25 | "Sussano", 26 | "Amateratsu", 27 | "Intelligence", 28 | ), 29 | natureTypes = listOf( 30 | "Lightning", 31 | "Fire", 32 | "Wind", 33 | "Earth", 34 | "Water", 35 | ), 36 | ) 37 | 38 | val hero2: Hero = Hero( 39 | id = 2, 40 | name = "Naruto", 41 | image = "/images/naruto.jpg", 42 | about = "Naruto Uzumaki (うずまきナルト, Uzumaki Naruto) is a shinobi of Konohagakure's Uzumaki clan. He became the jinchūriki of the Nine-Tails on the day of his birth — a fate that caused him to be shunned by most of Konoha throughout his childhood. After joining Team Kakashi, Naruto worked hard to gain the village's acknowledgement all the while chasing his dream to become Hokage.", 43 | rating = 5.0, 44 | power = 98, 45 | month = "Oct", 46 | day = "10th", 47 | family = listOf( 48 | "Minato", 49 | "Kushina", 50 | "Boruto", 51 | "Himawari", 52 | "Hinata", 53 | ), 54 | abilities = listOf( 55 | "Rasengan", 56 | "Rasen-Shuriken", 57 | "Shadow Clone", 58 | "Senin Mode", 59 | ), 60 | natureTypes = listOf( 61 | "Wind", 62 | "Earth", 63 | "Lava", 64 | "Fire", 65 | ), 66 | ) 67 | 68 | val hero3 = Hero( 69 | id = 3, 70 | name = "Sakura", 71 | image = "/images/sakura.jpg", 72 | about = "Sakura Uchiha (うちはサクラ, Uchiha Sakura, née Haruno (春野)) is a kunoichi of Konohagakure. When assigned to Team 7, Sakura quickly finds herself ill-prepared for the duties of a shinobi. However, after training under the Sannin Tsunade, she overcomes this, and becomes recognised as one of the greatest medical-nin in the world.", 73 | rating = 4.5, 74 | power = 92, 75 | month = "Mar", 76 | day = "28th", 77 | family = listOf( 78 | "Kizashi", 79 | "Mebuki", 80 | "Sarada", 81 | "Sasuke", 82 | ), 83 | abilities = listOf( 84 | "Chakra Control", 85 | "Medical Ninjutsu", 86 | "Strength", 87 | "Intelligence", 88 | ), 89 | natureTypes = listOf( 90 | "Earth", 91 | "Water", 92 | "Fire", 93 | ), 94 | ) 95 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/repository/HeroRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.repository 2 | 3 | import com.aarh.models.ApiResponse 4 | import com.aarh.models.Hero 5 | import com.aarh.utils.HeroPag1Object.hero1 6 | import com.aarh.utils.HeroPag1Object.hero2 7 | import com.aarh.utils.HeroPag1Object.hero3 8 | import com.aarh.utils.HeroPag2Object.hero4 9 | import com.aarh.utils.HeroPag2Object.hero5 10 | import com.aarh.utils.HeroPag2Object.hero6 11 | import com.aarh.utils.HeroPag3Object.hero7 12 | import com.aarh.utils.HeroPag3Object.hero8 13 | import com.aarh.utils.HeroPag3Object.hero9 14 | import com.aarh.utils.HeroPag4Object.hero10 15 | import com.aarh.utils.HeroPag4Object.hero11 16 | import com.aarh.utils.HeroPag4Object.hero12 17 | import com.aarh.utils.HeroPag5Object.hero13 18 | import com.aarh.utils.HeroPag5Object.hero14 19 | import com.aarh.utils.HeroPag5Object.hero15 20 | import com.aarh.utils.NEXT_PAGE_KEY 21 | import com.aarh.utils.PREVIOUS_PAGE_KEY 22 | 23 | class HeroRepositoryImpl : HeroRepository { 24 | 25 | override val heroes: Map> by lazy { 26 | mapOf( 27 | 1 to page1, 28 | 2 to page2, 29 | 3 to page3, 30 | 4 to page4, 31 | 5 to page5, 32 | ) 33 | } 34 | 35 | override val page1: List = listOf( 36 | hero1, 37 | hero2, 38 | hero3, 39 | ) 40 | 41 | override val page2: List = listOf( 42 | hero4, 43 | hero5, 44 | hero6, 45 | ) 46 | 47 | override val page3: List = listOf( 48 | hero7, 49 | hero8, 50 | hero9, 51 | ) 52 | override val page4: List = listOf( 53 | hero10, 54 | hero11, 55 | hero12, 56 | ) 57 | override val page5: List = listOf( 58 | hero13, 59 | hero14, 60 | hero15, 61 | ) 62 | 63 | override suspend fun getAllHeroes(page: Int): ApiResponse { 64 | return ApiResponse( 65 | success = true, 66 | message = "Ok", 67 | prevPage = calculatePage(page = page)[PREVIOUS_PAGE_KEY], 68 | nextPage = calculatePage(page = page)[NEXT_PAGE_KEY], 69 | heroes = heroes[page] ?: emptyList(), 70 | lastUpdated = System.currentTimeMillis(), 71 | ) 72 | } 73 | 74 | private fun calculatePage(page: Int): Map = 75 | mapOf( 76 | PREVIOUS_PAGE_KEY to if (page in 2..5) page.minus(1) else null, 77 | NEXT_PAGE_KEY to if (page in 1..4) page.plus(1) else null 78 | ) 79 | 80 | override suspend fun searchHeroes(name: String?): ApiResponse { 81 | return ApiResponse( 82 | success = true, 83 | message = "OK", 84 | heroes = findHeroes(name), 85 | ) 86 | } 87 | 88 | private fun findHeroes(query: String?): List { 89 | val founded = mutableListOf() 90 | 91 | return if (!query.isNullOrEmpty()) { 92 | heroes.forEach { (_, heroes) -> 93 | heroes.forEach { hero -> 94 | if (hero.name.lowercase().contains(query.lowercase())) { 95 | founded.add(hero) 96 | } 97 | } 98 | } 99 | founded 100 | } else { 101 | emptyList() 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/test/kotlin/com/aarh/ApplicationTest.kt: -------------------------------------------------------------------------------- 1 | package com.aarh 2 | 3 | import com.aarh.models.ApiResponse 4 | import com.aarh.repository.HeroRepositoryImpl 5 | import com.aarh.utils.NEXT_PAGE_KEY 6 | import com.aarh.utils.PREVIOUS_PAGE_KEY 7 | import io.ktor.client.request.* 8 | import io.ktor.client.statement.* 9 | import io.ktor.http.* 10 | import io.ktor.server.testing.* 11 | import kotlinx.serialization.json.Json 12 | import org.koin.core.context.stopKoin 13 | import kotlin.test.* 14 | 15 | class ApplicationTest { 16 | @Test 17 | fun `access root endpoint, assert correct information`() = testApplication { 18 | application { 19 | module() 20 | } 21 | val response = client.get("/") 22 | 23 | assertEquals( 24 | expected = HttpStatusCode.OK, 25 | actual = response.status, 26 | ) 27 | assertEquals( 28 | expected = "Welcome to Boruto API!", 29 | actual = response.bodyAsText(), 30 | ) 31 | } 32 | 33 | @Test 34 | fun `access all heroes endpoint, query all pages, assert correct information`() = testApplication { 35 | application { 36 | module() 37 | } 38 | val heroRepository = HeroRepositoryImpl() 39 | val pages = 1..5 40 | val heroes = listOf( 41 | heroRepository.page1, 42 | heroRepository.page2, 43 | heroRepository.page3, 44 | heroRepository.page4, 45 | heroRepository.page5, 46 | ) 47 | 48 | pages.forEach { page -> 49 | val response = client.get("/boruto/heroes?page=$page") 50 | val actual = Json.decodeFromString(response.bodyAsText()) 51 | val expected = ApiResponse( 52 | success = true, 53 | message = "Ok", 54 | prevPage = calculatePage(page = page)[PREVIOUS_PAGE_KEY], 55 | nextPage = calculatePage(page = page)[NEXT_PAGE_KEY], 56 | heroes = heroes[page - 1], 57 | lastUpdated = actual.lastUpdated, 58 | ) 59 | println("CURRENT PAGE: $page") 60 | println("PREVIOUS PAGE: ${calculatePage(page)[PREVIOUS_PAGE_KEY]}") 61 | println("NEXT PAGE: ${calculatePage(page)[NEXT_PAGE_KEY]}") 62 | println("HEROES: ${heroes[page - 1]}") 63 | 64 | assertEquals( 65 | expected = HttpStatusCode.OK, 66 | actual = response.status, 67 | ) 68 | assertEquals( 69 | expected = expected, 70 | actual = actual, 71 | ) 72 | } 73 | } 74 | 75 | @Test 76 | fun `access all heroes endpoint, query non existing page number, assert error`() = testApplication { 77 | application { 78 | module() 79 | } 80 | val response = client.get("/boruto/heroes?page=6") 81 | assertEquals( 82 | expected = HttpStatusCode.NotFound, 83 | actual = response.status, 84 | ) 85 | assertEquals( 86 | expected = "404: Page not Found.", 87 | actual = response.bodyAsText(), 88 | ) 89 | } 90 | 91 | @Test 92 | fun `access all heroes endpoint, query invalid page number, assert error`() = testApplication { 93 | application { 94 | module() 95 | } 96 | val response = client.get("/boruto/heroes?page=invalid") 97 | val expected = ApiResponse( 98 | success = false, 99 | message = "Only Numbers Allowed.", 100 | ) 101 | val actual = Json.decodeFromString(response.bodyAsText()) 102 | assertEquals( 103 | expected = HttpStatusCode.BadRequest, 104 | actual = response.status, 105 | ) 106 | assertEquals( 107 | expected = expected, 108 | actual = actual, 109 | ) 110 | } 111 | 112 | @Test 113 | fun `assert search heroes endpoint, query hero name, assert single hero result`() = testApplication { 114 | application { 115 | module() 116 | } 117 | val response = client.get("/boruto/heroes/search?name=sas") 118 | val actual = Json.decodeFromString(response.bodyAsText()).heroes.size 119 | 120 | assertEquals( 121 | expected = HttpStatusCode.OK, 122 | actual = response.status, 123 | ) 124 | assertEquals( 125 | expected = 1, 126 | actual = actual, 127 | ) 128 | } 129 | 130 | @Test 131 | fun `assert search heroes endpoint, query hero name, assert multiple heroes result`() = testApplication { 132 | application { 133 | module() 134 | } 135 | val response = client.get("/boruto/heroes/search?name=sa") 136 | val actual = Json.decodeFromString(response.bodyAsText()).heroes.size 137 | 138 | assertEquals( 139 | expected = HttpStatusCode.OK, 140 | actual = response.status, 141 | ) 142 | assertEquals( 143 | expected = 3, 144 | actual = actual, 145 | ) 146 | } 147 | 148 | @Test 149 | fun `assert search heroes endpoint, query an empty text, assert empty list as a result`() = testApplication { 150 | application { 151 | module() 152 | } 153 | val response = client.get("/boruto/heroes/search?name=") 154 | val actual = Json.decodeFromString(response.bodyAsText()).heroes 155 | 156 | assertEquals( 157 | expected = HttpStatusCode.OK, 158 | actual = response.status, 159 | ) 160 | assertEquals( 161 | expected = emptyList(), 162 | actual = actual, 163 | ) 164 | } 165 | 166 | @Test 167 | fun `assert search heroes endpoint, query non existing hero, assert empty list as a result`() = testApplication { 168 | application { 169 | module() 170 | } 171 | val response = client.get("/boruto/heroes/search?name=unknown") 172 | val actual = Json.decodeFromString(response.bodyAsText()).heroes 173 | 174 | assertEquals( 175 | expected = HttpStatusCode.OK, 176 | actual = response.status, 177 | ) 178 | assertEquals( 179 | expected = emptyList(), 180 | actual = actual, 181 | ) 182 | } 183 | 184 | @Test 185 | fun `access non existing endpoint, assert not found`() = testApplication { 186 | application { 187 | module() 188 | } 189 | val response = client.get("/unknown") 190 | assertEquals( 191 | expected = HttpStatusCode.NotFound, 192 | actual = response.status, 193 | ) 194 | assertEquals( 195 | expected = "404: Page not Found.", 196 | actual = response.bodyAsText(), 197 | ) 198 | } 199 | 200 | private fun calculatePage(page: Int): Map = 201 | mapOf( 202 | PREVIOUS_PAGE_KEY to if (page in 2..5) page.minus(1) else null, 203 | NEXT_PAGE_KEY to if (page in 1..4) page.plus(1) else null 204 | ) 205 | 206 | @AfterTest 207 | fun `after init`() { 208 | stopKoin() 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH="\\\"\\\"" 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /src/main/kotlin/com/aarh/utils/AllHeroesObject.kt: -------------------------------------------------------------------------------- 1 | package com.aarh.utils 2 | 3 | import com.aarh.models.Hero 4 | 5 | object AllHeroesObject { 6 | val heroes = listOf( 7 | Hero( 8 | id = 1, 9 | name = "Sasuke", 10 | image = "/images/sasuke.jpg", 11 | about = "Sasuke Uchiha (うちはサスケ, Uchiha Sasuke) is one of the last surviving members of Konohagakure's Uchiha clan. After his older brother, Itachi, slaughtered their clan, Sasuke made it his mission in life to avenge them by killing Itachi. He is added to Team 7 upon becoming a ninja and, through competition with his rival and best friend, Naruto Uzumaki.", 12 | rating = 5.0, 13 | power = 98, 14 | month = "July", 15 | day = "23rd", 16 | family = listOf( 17 | "Fugaku", 18 | "Mikoto", 19 | "Itachi", 20 | "Sarada", 21 | "Sakura", 22 | ), 23 | abilities = listOf( 24 | "Sharingan", 25 | "Rinnegan", 26 | "Sussano", 27 | "Amateratsu", 28 | "Intelligence", 29 | ), 30 | natureTypes = listOf( 31 | "Lightning", 32 | "Fire", 33 | "Wind", 34 | "Earth", 35 | "Water", 36 | ), 37 | ), 38 | Hero( 39 | id = 2, 40 | name = "Naruto", 41 | image = "/images/naruto.jpg", 42 | about = "Naruto Uzumaki (うずまきナルト, Uzumaki Naruto) is a shinobi of Konohagakure's Uzumaki clan. He became the jinchūriki of the Nine-Tails on the day of his birth — a fate that caused him to be shunned by most of Konoha throughout his childhood. After joining Team Kakashi, Naruto worked hard to gain the village's acknowledgement all the while chasing his dream to become Hokage.", 43 | rating = 5.0, 44 | power = 98, 45 | month = "Oct", 46 | day = "10th", 47 | family = listOf( 48 | "Minato", 49 | "Kushina", 50 | "Boruto", 51 | "Himawari", 52 | "Hinata", 53 | ), 54 | abilities = listOf( 55 | "Rasengan", 56 | "Rasen-Shuriken", 57 | "Shadow Clone", 58 | "Senin Mode", 59 | ), 60 | natureTypes = listOf( 61 | "Wind", 62 | "Earth", 63 | "Lava", 64 | "Fire", 65 | ), 66 | ), 67 | Hero( 68 | id = 3, 69 | name = "Sakura", 70 | image = "/images/sakura.jpg", 71 | about = "Sakura Uchiha (うちはサクラ, Uchiha Sakura, née Haruno (春野)) is a kunoichi of Konohagakure. When assigned to Team 7, Sakura quickly finds herself ill-prepared for the duties of a shinobi. However, after training under the Sannin Tsunade, she overcomes this, and becomes recognised as one of the greatest medical-nin in the world.", 72 | rating = 4.5, 73 | power = 92, 74 | month = "Mar", 75 | day = "28th", 76 | family = listOf( 77 | "Kizashi", 78 | "Mebuki", 79 | "Sarada", 80 | "Sasuke", 81 | ), 82 | abilities = listOf( 83 | "Chakra Control", 84 | "Medical Ninjutsu", 85 | "Strength", 86 | "Intelligence", 87 | ), 88 | natureTypes = listOf( 89 | "Earth", 90 | "Water", 91 | "Fire", 92 | ), 93 | ), 94 | Hero( 95 | id = 4, 96 | name = "Boruto", 97 | image = "/images/boruto.png", 98 | about = "Boruto Uzumaki (うずまきボルト, Uzumaki Boruto) is a shinobi from Konohagakure's Uzumaki clan and a direct descendant of the Hyūga clan through his mother. While initially resentful of his father and his absence since becoming Hokage, Boruto eventually comes to respect his father and duties.", 99 | rating = 4.9, 100 | power = 95, 101 | month = "Mar", 102 | day = "27th", 103 | family = listOf( 104 | "Naruto", 105 | "Hinata", 106 | "Hanabi", 107 | "Himawari", 108 | "Kawaki", 109 | ), 110 | abilities = listOf( 111 | "Karma", 112 | "Jogan", 113 | "Rasengan", 114 | "Intelligence", 115 | ), 116 | natureTypes = listOf( 117 | "Lightning", 118 | "Wind", 119 | "Water", 120 | ), 121 | ), 122 | Hero( 123 | id = 5, 124 | name = "Sarada", 125 | image = "/images/sarada.jpg", 126 | about = "Sarada Uchiha (うちはサラダ, Uchiha Sarada) is a kunoichi from Konohagakure's Uchiha clan. Because she was raised only by her mother without having her father around, Sarada initially struggles to understand who she is or what she's supposed to be. After meeting him with the help of Naruto Uzumaki, Sarada comes to believe that she is defined by the connections she has with others, and as a member of Team Konohamaru, she seeks to someday become Hokage so that she can connect with as many people as possible.", 127 | rating = 4.9, 128 | power = 95, 129 | month = "Mar", 130 | day = "31st", 131 | family = listOf( 132 | "Sasuke Uchiha", 133 | "Sakura Uchiha", 134 | ), 135 | abilities = listOf( 136 | "Sharingan", 137 | "Strength", 138 | "Intelligence", 139 | ), 140 | natureTypes = listOf( 141 | "Lightning", 142 | "Wind", 143 | "Fire", 144 | ), 145 | ), 146 | Hero( 147 | id = 6, 148 | name = "Mitsuki", 149 | image = "/images/mitsuki.jpg", 150 | about = "Mitsuki (ミツキ, Mitsuki) is a synthetic human that was created as a partial clone of Orochimaru. Immigrating to Konohagakure to confirm whether or not Boruto Uzumaki was his \"sun\", he became a shinobi and was placed on Team Konohamaru. Mitsuki was created as a clone of Orochimaru, being cultivated from the same embryo as at least one older \"Mitsuki\", and raised in a test tube.", 151 | rating = 4.9, 152 | power = 95, 153 | month = "Jul", 154 | day = "25th", 155 | family = listOf( 156 | "Orochimaru", 157 | "Log", 158 | ), 159 | abilities = listOf( 160 | "Senin Mode", 161 | "Transformation", 162 | "Intelligence", 163 | ), 164 | natureTypes = listOf( 165 | "Lightning", 166 | "Wind", 167 | ), 168 | ), 169 | Hero( 170 | id = 7, 171 | name = "Kawaki", 172 | image = "/images/kawaki.jpg", 173 | about = "Kawaki (カワキ, Kawaki) is a child raised by Kara to be the future vessel for Isshiki Ōtsutsuki and the key to the fulfilment of their greatest wish.[1] After being brought to Konohagakure by Team 7, he is taken in by Naruto Uzumaki who raises him as his own, during which he develops a brotherly bond with Boruto Uzumaki to solve the mystery of the Kāma.", 174 | rating = 4.2, 175 | power = 92, 176 | month = "Jan", 177 | day = "1st", 178 | family = listOf( 179 | "Kokatsu", 180 | ), 181 | abilities = listOf( 182 | "Karma", 183 | "Transformation", 184 | "Strength", 185 | ), 186 | natureTypes = listOf( 187 | "Fire", 188 | ), 189 | ), 190 | Hero( 191 | id = 8, 192 | name = "Orochimaru", 193 | image = "/images/orochimaru.jpg", 194 | about = "Orochimaru (大蛇丸, Orochimaru) is one of Konohagakure's legendary Sannin. With a life-ambition to learn all of the world's secrets, Orochimaru seeks immortality so that he might live all of the lives necessary to accomplish his task. After being caught red-handed performing unethical experiments on his fellow citizens for the sake of this immortality, Orochimaru defected from Konoha.", 195 | rating = 4.5, 196 | power = 97, 197 | month = "Oct", 198 | day = "27th", 199 | family = listOf( 200 | "Mitsuki", 201 | "Log", 202 | ), 203 | abilities = listOf( 204 | "Senin Mode", 205 | "Transformation", 206 | "Science", 207 | ), 208 | natureTypes = listOf( 209 | "Lightning", 210 | "Wind", 211 | "Fire", 212 | "Earth", 213 | "Water", 214 | ), 215 | ), 216 | Hero( 217 | id = 9, 218 | name = "Kakashi", 219 | image = "/images/kakashi.png", 220 | about = "Kakashi Hatake (はたけカカシ, Hatake Kakashi) is a shinobi of Konohagakure's Hatake clan. Famed as Kakashi of the Sharingan (写輪眼のカカシ, Sharingan no Kakashi), he is one of Konoha's most talented ninja, regularly looked to for advice and leadership despite his personal dislike of responsibility. To his students on Team 7, Kakashi emphasises the importance of teamwork; he himself received this lesson, along with the Sharingan, from his childhood friend, Obito Uchiha.", 221 | rating = 4.5, 222 | power = 96, 223 | month = "Sep", 224 | day = "15th", 225 | family = listOf( 226 | "Sakumo", 227 | ), 228 | abilities = listOf( 229 | "Intelligence", 230 | "Strength", 231 | ), 232 | natureTypes = listOf( 233 | "Lightning", 234 | "Wind", 235 | "Fire", 236 | "Earth", 237 | "Water", 238 | ), 239 | ), 240 | Hero( 241 | id = 10, 242 | name = "Isshiki", 243 | image = "/images/ishiki.jpg", 244 | about = "A thousand years ago, Isshiki came to Earth alongside Kaguya with the objective to plant a Tree to harvest its Chakra Fruit. While Kaguya, being lower-ranked, was planned to be sacrificed to create the Chakra Fruit, she instead turned on Isshiki, leaving him on the verge of death after destroying Isshiki's lower half. Encountering Jigen and not having the strength to implant a Kāma on him, Isshiki devised a desperate plan and shrunk himself to enter the monk's ear in order to survive his injury by absorbing Jigen's nutrients.", 245 | rating = 5.0, 246 | power = 100, 247 | month = "Jan", 248 | day = "1st", 249 | family = listOf( 250 | "Otsutsuki Clan", 251 | ), 252 | abilities = listOf( 253 | "Sukunahikona", 254 | "Daikokuten", 255 | "Byakugan", 256 | "Space–Time", 257 | "Intelligence", 258 | ), 259 | natureTypes = listOf( 260 | "Fire", 261 | ), 262 | ), 263 | Hero( 264 | id = 11, 265 | name = "Momoshiki", 266 | image = "/images/momoshiki.jpg", 267 | about = "Momoshiki Ōtsutsuki (大筒木モモシキ, Ōtsutsuki Momoshiki) was a member of the Ōtsutsuki clan's main family, sent to investigate the whereabouts of Kaguya and her God Tree and then attempting to cultivate a new one out of the chakra of the Seventh Hokage. In the process of being killed by Boruto Uzumaki, Momoshiki placed a Kāma on him, allowing his spirit to remain intact through the mark.", 268 | rating = 3.9, 269 | power = 98, 270 | month = "Jan", 271 | day = "1st", 272 | family = listOf( 273 | "Otsutsuki Clan", 274 | ), 275 | abilities = listOf( 276 | "Rinnegan", 277 | "Byakugan", 278 | "Strength", 279 | ), 280 | natureTypes = listOf( 281 | "Fire", 282 | "Lightning", 283 | "Wind", 284 | "Water", 285 | "Earth", 286 | ), 287 | ), 288 | Hero( 289 | id = 12, 290 | name = "Urashiki", 291 | image = "/images/urashiki.jpg", 292 | about = "Urashiki Ōtsutsuki (大筒木ウラシキ, Ōtsutsuki Urashiki) was a low-ranking member of the Ōtsutsuki clan's main family, sent to assist Momoshiki and Kinshiki on their mission to investigate Kaguya's whereabouts and gather the chakra of the God Tree on Earth. Compared to his comrades, Urashiki had been shown to have a rather laid-back and jovial personality. He was quite willing to joke along with Momoshiki and Kinshiki, and disparaged on how serious they are.", 293 | rating = 3.4, 294 | power = 95, 295 | month = "Jan", 296 | day = "1st", 297 | family = listOf( 298 | "Otsutsuki Clan", 299 | ), 300 | abilities = listOf( 301 | "Space–Time", 302 | "Rinnegan", 303 | "Byakugan", 304 | ), 305 | natureTypes = listOf( 306 | "Fire", 307 | "Lightning", 308 | "Wind", 309 | "Earth", 310 | ), 311 | ), 312 | Hero( 313 | id = 13, 314 | name = "Code", 315 | image = "/images/code.jpg", 316 | about = "Code (コード, Kōdo) is the last active Inner from Kara. Carrying Isshiki Ōtsutsuki's legacy within him, he inherits the Ōtsutsuki Clan's will to become a Celestial Being and continually evolve. At the time Kawaki was brought to Kara, Code was one of fifteen candidates in Jigen and Amado's Ōtsutsuki ritual to screen for a Kāma vessel for Isshiki. Only Kawaki survived to become an actual vessel.", 317 | rating = 4.8, 318 | power = 99, 319 | month = "Jan", 320 | day = "1st", 321 | family = listOf( 322 | "Unknown", 323 | ), 324 | abilities = listOf( 325 | "White Karma", 326 | "Transformation", 327 | "Genjutsu", 328 | ), 329 | natureTypes = listOf( 330 | "Unknown", 331 | ), 332 | ), 333 | Hero( 334 | id = 14, 335 | name = "Amado", 336 | image = "/images/amado.jpg", 337 | about = "Amado (アマド, Amado) is a former Inner from the organisation Kara and the head of its research and development division. He has since defected to Konohagakure, where he used a mix of bluffs and gifts to gain official citizenship for the Hokage's protection. Amado had a daughter who died twelve years prior to the reign of the Seventh Hokage. In his quest to kill Isshiki Ōtsutsuki, Amado joined Kara and was granted the rank of Inner, serving as the head of its research and development division.", 338 | rating = 5.0, 339 | power = 90, 340 | month = "Jan", 341 | day = "1st", 342 | family = listOf( 343 | "Unknown", 344 | ), 345 | abilities = listOf( 346 | "Science", 347 | "Intelligence", 348 | "Trickery", 349 | ), 350 | natureTypes = listOf( 351 | "Unknown", 352 | ), 353 | ), 354 | Hero( 355 | id = 15, 356 | name = "Koji", 357 | image = "/images/koji.jpg", 358 | about = "Koji Kashin (果心居士, Kashin Koji) is a clone of Jiraiya that was created by Amado for the purpose of killing Isshiki Ōtsutsuki. A former Inner of Kara, he was in charge of the sector on the outskirts of the Land of Fire. An enigmatic man, Koji has a very stoic and straightforward nature that follows a no-nonsense view. Arrogant as he may appear, he has consistently shown himself to be a very rational and perceptive man.", 359 | rating = 4.5, 360 | power = 90, 361 | month = "Jan", 362 | day = "1st", 363 | family = listOf( 364 | "Jiraiya", 365 | ), 366 | abilities = listOf( 367 | "Senin Mode", 368 | "Rasengan", 369 | "Shadow Clone", 370 | ), 371 | natureTypes = listOf( 372 | "Fire", 373 | "Earth", 374 | ), 375 | ) 376 | ) 377 | } --------------------------------------------------------------------------------