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

196 lines
7.3 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.transaction
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("/") { ctx ->
run {
transaction(database) {
addLogger(StdOutSqlLogger)
val dataPoints =
Statistics.slice(Statistics.playerId.count()).selectAll().limit(1).map {
it[Statistics.playerId.count()]
}[0]
val playerCount =
Statistics.slice(Statistics.playerId.countDistinct())
.selectAll()
.limit(1)
.map { it[Statistics.playerId.countDistinct()] }[0]
ctx.result("$dataPoints data points from $playerCount players!")
}
}
}
app.get("/statistics") { ctx ->
run {
if (statisticsCache.isEmpty() or
(Duration.between(Instant.now(), statisticsCacheTimestamp) >
Duration.ofHours(1))) {
transaction(database) {
addLogger(StdOutSqlLogger)
val statistics =
Statistics.slice(Statistics.type, Statistics.name)
.selectAll()
.groupBy(Statistics.type, Statistics.name)
.map { Statistic(it[Statistics.type], it[Statistics.name]) }
statisticsCache = Json.encodeToString(statistics)
}
}
ctx.result(statisticsCache)
}
}
app.get("/statistics/:type/:name") { ctx ->
run {
transaction(database) {
addLogger(StdOutSqlLogger)
val maxExpr = Statistics.value.max()
val statistics =
Statistics.slice(
Statistics.type,
Statistics.name,
Statistics.playerId,
maxExpr)
.select {
Statistics.type.eq(ctx.pathParam("type")) and
Statistics.name.eq(ctx.pathParam("name"))
}
.groupBy(Statistics.playerId)
.orderBy(maxExpr, SortOrder.DESC)
.map {
StatisticValue(
it[Statistics.playerId],
it[Statistics.type],
it[Statistics.name],
it[maxExpr]!!)
}
ctx.result(Json { prettyPrint = true }.encodeToString(statistics))
}
}
}
app.get("/players") { ctx ->
run {
if (playersCache.isEmpty() or
(Duration.between(Instant.now(), playersCacheTimestamp) >
Duration.ofHours(1))) {
transaction(database) {
addLogger(StdOutSqlLogger)
val players =
Statistics.slice(Statistics.playerId)
.selectAll()
.groupBy(Statistics.playerId)
.map { it[Statistics.playerId] }
playersCache = Json.encodeToString(players)
}
}
ctx.result(playersCache)
}
}
app.get("/players/:playerId") { ctx ->
run {
transaction(database) {
addLogger(StdOutSqlLogger)
val maxExpr = Statistics.value.max()
val statistics =
Statistics.slice(
Statistics.type,
Statistics.name,
Statistics.playerId,
maxExpr)
.select { Statistics.playerId.eq(ctx.pathParam("playerId")) }
.groupBy(Statistics.type, Statistics.name)
.map {
StatisticValue(
it[Statistics.playerId],
it[Statistics.type],
it[Statistics.name],
it[maxExpr]!!)
}
ctx.result(Json { prettyPrint = true }.encodeToString(statistics))
}
}
}
app.get("/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 StatisticValue(
val playerId: String,
val type: String,
val name: String,
val value: Long
)
@Serializable
data class HistoricalStatisticValue(
val playerId: String,
val type: String,
val name: String,
val timestamp: String,
val value: Long
)