Stonks/app/src/main/kotlin/xyz/etztech/stonks/Api.kt

183 lines
7.1 KiB
Kotlin

package xyz.etztech.stonks.api
import io.javalin.Javalin
import java.time.Duration
import java.time.Instant
import kotlinx.coroutines.*
import kotlinx.serialization.*
import kotlinx.serialization.json.Json
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.*
import xyz.etztech.stonks.dsl.LiveStatistics
import xyz.etztech.stonks.dsl.Players
import xyz.etztech.stonks.dsl.Statistics
private var statisticsCache = ""
private var statisticsCacheTimestamp = Instant.now()
private var playersCache = ""
private var playersCacheTimestamp = Instant.now()
fun initApiServer(apiServerPort: Int, database: Database) {
val app = Javalin.create().start(apiServerPort)
println("Javalin web server started")
app.get("api/statistics") { ctx ->
run {
if (statisticsCache.isEmpty() or
(Duration.between(Instant.now(), statisticsCacheTimestamp) >
Duration.ofHours(1))) {
transaction(database) {
addLogger(StdOutSqlLogger)
val statistics =
LiveStatistics.slice(LiveStatistics.type, LiveStatistics.name)
.selectAll()
.groupBy(LiveStatistics.type, LiveStatistics.name)
.map {
Statistic(it[LiveStatistics.type], it[LiveStatistics.name])
}
statisticsCache = Json.encodeToString(statistics)
}
}
ctx.result(statisticsCache)
}
}
app.get("api/statistics/:type/:name") { ctx ->
run {
transaction(database) {
addLogger(StdOutSqlLogger)
val statistics =
LiveStatistics.slice(
LiveStatistics.type,
LiveStatistics.name,
LiveStatistics.playerId,
LiveStatistics.value,
LiveStatistics.rank)
.select {
LiveStatistics.type.eq(ctx.pathParam("type")) and
LiveStatistics.name.eq(ctx.pathParam("name"))
}
.orderBy(LiveStatistics.rank, SortOrder.ASC)
.map {
StatisticValue(
it[LiveStatistics.playerId],
it[LiveStatistics.type],
it[LiveStatistics.name],
it[LiveStatistics.value],
it[LiveStatistics.rank])
}
ctx.result(Json { prettyPrint = true }.encodeToString(statistics))
}
}
}
app.get("api/players") { ctx ->
run {
if (playersCache.isEmpty() or
(Duration.between(Instant.now(), playersCacheTimestamp) >
Duration.ofHours(1))) {
transaction(database) {
addLogger(StdOutSqlLogger)
val players =
LiveStatistics.leftJoin(Players, { playerId }, { id })
.slice(LiveStatistics.playerId, Players.name)
.selectAll()
.groupBy(LiveStatistics.playerId)
.map { Player(it[LiveStatistics.playerId], it[Players.name]) }
playersCache = Json.encodeToString(players)
}
}
ctx.result(playersCache)
}
}
app.get("api/players/:playerId") { ctx ->
run {
transaction(database) {
addLogger(StdOutSqlLogger)
val statistics =
LiveStatistics.slice(
LiveStatistics.type,
LiveStatistics.name,
LiveStatistics.playerId,
LiveStatistics.value,
LiveStatistics.rank)
.select { LiveStatistics.playerId.eq(ctx.pathParam("playerId")) }
.map {
StatisticValue(
it[LiveStatistics.playerId],
it[LiveStatistics.type],
it[LiveStatistics.name],
it[LiveStatistics.value],
it[LiveStatistics.rank])
}
ctx.result(Json { prettyPrint = true }.encodeToString(statistics))
}
}
}
app.get("api/players/:playerId/:type/:name") { ctx ->
run {
transaction(database) {
addLogger(StdOutSqlLogger)
val statistics =
Statistics.slice(
Statistics.type,
Statistics.name,
Statistics.playerId,
Statistics.timestamp,
Statistics.value)
.select {
Statistics.playerId.eq(ctx.pathParam("playerId")) and
Statistics.type.eq(ctx.pathParam("type")) and
Statistics.name.eq(ctx.pathParam("name"))
}
.orderBy(Statistics.timestamp, SortOrder.DESC)
.map {
HistoricalStatisticValue(
it[Statistics.playerId],
it[Statistics.type],
it[Statistics.name],
it[Statistics.timestamp].toString(),
it[Statistics.value])
}
ctx.result(Json { prettyPrint = true }.encodeToString(statistics))
}
}
}
}
@Serializable data class Statistic(val type: String, val name: String)
@Serializable data class Player(val id: String, val name: String?)
@Serializable
data class StatisticValue(
val playerId: String,
val type: String,
val name: String,
val value: Long,
val rank: Int
)
@Serializable
data class HistoricalStatisticValue(
val playerId: String,
val type: String,
val name: String,
val timestamp: String,
val value: Long
)