I'm new to Kotlin and wanted to give this mini project a try. Please let me know if you have any improvements in mind. I'm thinking error handling, structuring the module, is there too much going on in the main
function that I should handle elsewhere, is the logging handled correctly (it works, but it's a bit odd to put it into the constructor, no?).
The project:
- calls an API
- iterates over the json data
- saves each map in a separate json file and assigns a unique name
Main.kt
import ApiCall.ApiCall
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import ApiCall.MonsterData
import ApiCall.Utils
import java.io.IOException
fun main() {
val api = ApiCall()
val utils = Utils()
try {
val result = api.callApi("https://botw-compendium.herokuapp.com/api/v3/compendium/category/monsters")
if (api.handleResponse(result)) {
val monsters: String = result.body()
val mapper = ObjectMapper()
val monsterData: MonsterData = mapper.readValue(monsters)
val path = "/Users/me"
utils.createDirectory(path)
for (monster in monsterData.data) {
val monsterName = monster.name
.replace(" ", "_")
// Convert the monster object to JSON string
val monsterJson = mapper.writeValueAsString(monster)
// Write the JSON data to a file named after the monster
val fileName = "$path/$monsterName.json"
utils.writeFiles(fileName, monsterJson)
}
}
} catch (e: IOException) {
api.logger.error("$e")
}
}
ApiCall.kt
package ApiCall
import org.slf4j.Logger
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.net.http.HttpResponse.BodyHandlers
import org.slf4j.LoggerFactory
class ApiCall(private val client: HttpClient = HttpClient.newHttpClient(),
val logger: Logger = LoggerFactory.getLogger(ApiCall::class.java)) {
fun callApi(url: String): HttpResponse<String> {
val request = HttpRequest.newBuilder(URI.create(url))
.header("accept", "application/json")
.build()
return client.send(request, BodyHandlers.ofString())
}
fun handleResponse(response: HttpResponse<String>): Boolean {
val statusCode = response.statusCode()
return when (statusCode) {
200 -> {
logger.info("Status 200. API is available")
true
}
in 400..499 -> {
logger.warn("Status $statusCode. Client Error – client sent an invalid request")
false
}
in 500..599 -> {
logger.error("Status $statusCode. Protocol Error – a generic error occurred on the server")
false
}
else -> {
logger.warn("Unknown status code: $statusCode")
false
}
}
}
}
Monsters.kt
package ApiCall
data class Monster(
val category: String = "",
val common_locations: List<String>? = emptyList(),
val description: String = "",
val dlc: Boolean = false,
val drops: List<String>? = emptyList(),
val id: Int = 0,
val image: String = "",
val name: String = ""
)
data class MonsterData(
val data: List<Monster> = emptyList()
)
Utils.kt
(those are just wrappers for existing functions -> maybe a bit of an overkill?)
package ApiCall
import java.io.FileWriter
import java.io.PrintWriter
import java.nio.charset.Charset
import java.nio.file.Files
import java.nio.file.Paths
class Utils {
fun createDirectory(path: String) {
Files.createDirectories(Paths.get(path));
}
fun writeFiles(fileName: String, jsonString: String) {
PrintWriter(FileWriter(fileName, Charset.defaultCharset()))
.use { it.write(jsonString) }
}
}