mirror of https://github.com/hykilpikonna/AquaDX
[O] Make code less verbose
parent
70466d0c94
commit
ebafb4c05e
|
@ -5,9 +5,6 @@ import io.ktor.client.*
|
||||||
import io.ktor.client.engine.cio.*
|
import io.ktor.client.engine.cio.*
|
||||||
import io.ktor.client.plugins.contentnegotiation.*
|
import io.ktor.client.plugins.contentnegotiation.*
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
import io.micrometer.core.instrument.*
|
|
||||||
import io.micrometer.core.instrument.Timer
|
|
||||||
import io.micrometer.core.instrument.Metrics as Micrometer
|
|
||||||
import jakarta.persistence.Query
|
import jakarta.persistence.Query
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -25,7 +22,6 @@ import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import kotlin.reflect.KCallable
|
import kotlin.reflect.KCallable
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
|
@ -167,6 +163,7 @@ val Any?.truthy get() = when (this) {
|
||||||
|
|
||||||
// Collections
|
// Collections
|
||||||
fun <T> ls(vararg args: T) = args.toList()
|
fun <T> ls(vararg args: T) = args.toList()
|
||||||
|
inline fun <reified T> arr(vararg args: T) = arrayOf(*args)
|
||||||
operator fun <K, V> Map<K, V>.plus(map: Map<K, V>) =
|
operator fun <K, V> Map<K, V>.plus(map: Map<K, V>) =
|
||||||
(if (this is MutableMap) this else toMutableMap()).apply { putAll(map) }
|
(if (this is MutableMap) this else toMutableMap()).apply { putAll(map) }
|
||||||
operator fun <K, V> MutableMap<K, V>.plusAssign(map: Map<K, V>) { putAll(map) }
|
operator fun <K, V> MutableMap<K, V>.plusAssign(map: Map<K, V>) { putAll(map) }
|
||||||
|
@ -218,45 +215,3 @@ val <S> Pair<*, S>.r get() = component2()
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
val Query.exec get() = resultList.map { (it as Array<*>).toList() }
|
val Query.exec get() = resultList.map { (it as Array<*>).toList() }
|
||||||
|
|
||||||
// Metrics
|
|
||||||
object Metrics {
|
|
||||||
@PublishedApi
|
|
||||||
internal inline fun <reified T : Any> expandLabels(labels: T): Array<String> {
|
|
||||||
return T::class.memberProperties.flatMap { prop ->
|
|
||||||
listOf(prop.name, prop.get(labels)?.toString() ?: throw IllegalArgumentException("Missing value for label ${prop.name}"))
|
|
||||||
}.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal data class MetricCacheKey(val metricName: String, val labels: Any)
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal val counterCache = ConcurrentHashMap<MetricCacheKey, Counter>()
|
|
||||||
|
|
||||||
inline fun <reified T : Any> counter(metricName: String): (T) -> Counter {
|
|
||||||
return { labels ->
|
|
||||||
counterCache.computeIfAbsent(MetricCacheKey(metricName, labels)) {
|
|
||||||
Counter
|
|
||||||
.builder(metricName)
|
|
||||||
.tags(*expandLabels(labels))
|
|
||||||
.register(Micrometer.globalRegistry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal val timerCache = ConcurrentHashMap<MetricCacheKey, Timer>()
|
|
||||||
|
|
||||||
inline fun <reified T : Any> timer(metricName: String): (T) -> Timer {
|
|
||||||
return { labels ->
|
|
||||||
timerCache.computeIfAbsent(MetricCacheKey(metricName, labels)) {
|
|
||||||
Timer
|
|
||||||
.builder(metricName)
|
|
||||||
.publishPercentiles(0.5, 0.75, 0.90, 0.95, 0.99)
|
|
||||||
.tags(*expandLabels(labels))
|
|
||||||
.register(Micrometer.globalRegistry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package icu.samnyan.aqua.net.db
|
||||||
|
|
||||||
|
import ext.arr
|
||||||
|
import icu.samnyan.aqua.net.utils.ApiException
|
||||||
|
import io.micrometer.core.instrument.Counter
|
||||||
|
import io.micrometer.core.instrument.Timer
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import kotlin.time.TimeSource
|
||||||
|
import kotlin.time.toJavaDuration
|
||||||
|
import io.micrometer.core.instrument.Metrics as MMetrics
|
||||||
|
|
||||||
|
operator fun Counter.unaryPlus() = increment()
|
||||||
|
|
||||||
|
class APICounter(val api: String, val metrics: APIMetrics) {
|
||||||
|
operator fun unaryPlus() = +metrics["api_count", arr("api", api)]
|
||||||
|
|
||||||
|
operator fun rem(err: Exception) = also {
|
||||||
|
val e = if (err is ApiException) err.code.toString() else err.javaClass.simpleName
|
||||||
|
+metrics["api_error_count", arr("api", api, "error", e)]
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun <T> invoke(fn: () -> T): T {
|
||||||
|
val start = TimeSource.Monotonic.markNow()
|
||||||
|
try { return fn().also { +this } }
|
||||||
|
catch (e: Exception) { throw e.also { this % e } }
|
||||||
|
finally { metrics.timer("api_latency", arr("api", api)).record(start.elapsedNow().toJavaDuration()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class APIMetrics(val domain: String) {
|
||||||
|
val cache = ConcurrentHashMap<Array<String>, Any>()
|
||||||
|
val reg = MMetrics.globalRegistry
|
||||||
|
|
||||||
|
operator fun get(name: String, vararg pairs: Pair<String, Any>) =
|
||||||
|
get(name, pairs.flatMap { listOf(it.first, it.second.toString()) }.toTypedArray())
|
||||||
|
|
||||||
|
operator fun get(name: String, tag: Array<String>) = cache.computeIfAbsent(tag) {
|
||||||
|
Counter.builder("aquadx_${domain}_$name").tags(*tag).register(reg)
|
||||||
|
} as Counter
|
||||||
|
|
||||||
|
fun timer(name: String, tag: Array<String>) = cache.computeIfAbsent(tag) {
|
||||||
|
Timer.builder("aquadx_${domain}_$name").tags(*tag).publishPercentiles(0.5, 0.75, 0.90, 0.95, 0.99).register(reg)
|
||||||
|
} as Timer
|
||||||
|
|
||||||
|
operator fun get(api: String) = APICounter(api, this)
|
||||||
|
operator fun set(api: String, value: APICounter) {}
|
||||||
|
}
|
|
@ -1,15 +1,13 @@
|
||||||
package icu.samnyan.aqua.sega.chusan
|
package icu.samnyan.aqua.sega.chusan
|
||||||
|
|
||||||
import ext.*
|
import ext.*
|
||||||
|
import icu.samnyan.aqua.net.db.APIMetrics
|
||||||
import icu.samnyan.aqua.sega.chunithm.handler.impl.GetGameIdlistHandler
|
import icu.samnyan.aqua.sega.chunithm.handler.impl.GetGameIdlistHandler
|
||||||
import icu.samnyan.aqua.sega.chusan.handler.*
|
import icu.samnyan.aqua.sega.chusan.handler.*
|
||||||
import icu.samnyan.aqua.sega.general.BaseHandler
|
import icu.samnyan.aqua.sega.general.BaseHandler
|
||||||
import io.micrometer.core.instrument.Timer
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import kotlin.reflect.full.declaredMemberProperties
|
import kotlin.reflect.full.declaredMemberProperties
|
||||||
import kotlin.time.TimeSource
|
|
||||||
import kotlin.time.toJavaDuration
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,11 +71,7 @@ class ChusanServletController(
|
||||||
val getUserNetBattleRankingInfo: GetUserNetBattleRankingInfoHandler,
|
val getUserNetBattleRankingInfo: GetUserNetBattleRankingInfoHandler,
|
||||||
val getGameMapAreaCondition: GetGameMapAreaConditionHandler
|
val getGameMapAreaCondition: GetGameMapAreaConditionHandler
|
||||||
) {
|
) {
|
||||||
data class ApiLabel(val api: String)
|
val metrics = APIMetrics("chusan")
|
||||||
data class ApiErrorLabel(val api: String, val error: String)
|
|
||||||
val apiCountMetric = Metrics.counter<ApiLabel>("aquadx_chusan_api_count")
|
|
||||||
val apiErrorCountMetric = Metrics.counter<ApiErrorLabel>("aquadx_chusan_api_error_count")
|
|
||||||
val apiLatencyMetric = Metrics.timer<ApiLabel>("aquadx_chusan_api_latency")
|
|
||||||
|
|
||||||
val logger = LoggerFactory.getLogger(ChusanServletController::class.java)
|
val logger = LoggerFactory.getLogger(ChusanServletController::class.java)
|
||||||
|
|
||||||
|
@ -119,34 +113,25 @@ class ChusanServletController(
|
||||||
@API("/{endpoint}")
|
@API("/{endpoint}")
|
||||||
fun handle(@PV endpoint: Str, @RB request: MutableMap<Str, Any>, @PV version: Str): Any {
|
fun handle(@PV endpoint: Str, @RB request: MutableMap<Str, Any>, @PV version: Str): Any {
|
||||||
var api = endpoint
|
var api = endpoint
|
||||||
val startTime = TimeSource.Monotonic.markNow()
|
request["version"] = version
|
||||||
var timer: Timer? = null
|
|
||||||
try {
|
|
||||||
request["version"] = version
|
|
||||||
|
|
||||||
// Export version
|
// Export version
|
||||||
if (api.endsWith("C3Exp")) {
|
if (api.endsWith("C3Exp")) {
|
||||||
api = api.removeSuffix("C3Exp")
|
api = api.removeSuffix("C3Exp")
|
||||||
request["c3exp"] = true
|
request["c3exp"] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
apiCountMetric(ApiLabel(api)).increment()
|
logger.info("Chu3 $api : $request")
|
||||||
logger.info("Chu3 $api : $request")
|
|
||||||
|
|
||||||
if (api in noopEndpoint) {
|
if (api in noopEndpoint) {
|
||||||
return """{"returnCode":"1"}"""
|
return """{"returnCode":"1"}"""
|
||||||
}
|
}
|
||||||
|
|
||||||
timer = apiLatencyMetric(ApiLabel(api))
|
return metrics[api] {
|
||||||
return handlers[api]?.handle(request) ?: {
|
handlers[api]?.handle(request) ?: {
|
||||||
logger.warn("Chu3 $api not found")
|
logger.warn("Chu3 $api not found")
|
||||||
"""{"returnCode":"1","apiName":"$api"}"""
|
"""{"returnCode":"1","apiName":"$api"}"""
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
apiErrorCountMetric(ApiErrorLabel(api, e.javaClass.name)).increment()
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
timer?.record(startTime.elapsedNow().toJavaDuration())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package icu.samnyan.aqua.sega.general
|
package icu.samnyan.aqua.sega.general
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException
|
import com.fasterxml.jackson.core.JsonProcessingException
|
||||||
import icu.samnyan.aqua.sega.allnet.KeychipSession
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author samnyan (privateamusement@protonmail.com)
|
* @author samnyan (privateamusement@protonmail.com)
|
||||||
|
|
|
@ -1,22 +1,17 @@
|
||||||
package icu.samnyan.aqua.sega.maimai2
|
package icu.samnyan.aqua.sega.maimai2
|
||||||
|
|
||||||
import ext.*
|
import ext.*
|
||||||
|
import icu.samnyan.aqua.net.db.APIMetrics
|
||||||
import icu.samnyan.aqua.net.utils.ApiException
|
import icu.samnyan.aqua.net.utils.ApiException
|
||||||
import icu.samnyan.aqua.sega.allnet.KeychipSession
|
|
||||||
import icu.samnyan.aqua.sega.general.BaseHandler
|
import icu.samnyan.aqua.sega.general.BaseHandler
|
||||||
import icu.samnyan.aqua.sega.maimai2.handler.*
|
import icu.samnyan.aqua.sega.maimai2.handler.*
|
||||||
import icu.samnyan.aqua.sega.maimai2.model.Mai2Repos
|
import icu.samnyan.aqua.sega.maimai2.model.Mai2Repos
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.micrometer.core.instrument.Timer
|
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import kotlin.reflect.full.declaredMemberProperties
|
import kotlin.reflect.full.declaredMemberProperties
|
||||||
import kotlin.time.TimeSource
|
|
||||||
import kotlin.time.toJavaDuration
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author samnyan (privateamusement@protonmail.com)
|
* @author samnyan (privateamusement@protonmail.com)
|
||||||
|
@ -46,11 +41,7 @@ class Maimai2ServletController(
|
||||||
private val GAME_SETTING_TIME_FMT = DateTimeFormatter.ofPattern("HH:mm:00")
|
private val GAME_SETTING_TIME_FMT = DateTimeFormatter.ofPattern("HH:mm:00")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ApiLabel(val api: String)
|
val metrics = APIMetrics("maimai2")
|
||||||
data class ApiErrorLabel(val api: String, val error: String)
|
|
||||||
val apiCountMetric = Metrics.counter<ApiLabel>("aquadx_maimai2_api_count")
|
|
||||||
val apiErrorCountMetric = Metrics.counter<ApiErrorLabel>("aquadx_maimai2_api_error_count")
|
|
||||||
val apiLatencyMetric = Metrics.timer<ApiLabel>("aquadx_maimai2_api_latency")
|
|
||||||
|
|
||||||
val getUserExtend = UserReqHandler { _, userId -> mapOf(
|
val getUserExtend = UserReqHandler { _, userId -> mapOf(
|
||||||
"userId" to userId,
|
"userId" to userId,
|
||||||
|
@ -351,41 +342,32 @@ class Maimai2ServletController(
|
||||||
|
|
||||||
@API("/{api}")
|
@API("/{api}")
|
||||||
fun handle(@PathVariable api: String, @RequestBody request: Map<String, Any>): Any {
|
fun handle(@PathVariable api: String, @RequestBody request: Map<String, Any>): Any {
|
||||||
val startTime = TimeSource.Monotonic.markNow()
|
logger.info("Mai2 < $api : ${request.toJson()}") // TODO: Optimize logging
|
||||||
var timer: Timer? = null
|
|
||||||
try {
|
|
||||||
apiCountMetric(ApiLabel(api)).increment()
|
|
||||||
logger.info("Mai2 < $api : ${request.toJson()}") // TODO: Optimize logging
|
|
||||||
|
|
||||||
if (api in noopEndpoint) {
|
if (api in noopEndpoint) {
|
||||||
logger.info("Mai2 > $api no-op")
|
logger.info("Mai2 > $api no-op")
|
||||||
return """{"returnCode":1,"apiName":"com.sega.maimai2servlet.api.$api"}"""
|
return """{"returnCode":1,"apiName":"com.sega.maimai2servlet.api.$api"}"""
|
||||||
}
|
}
|
||||||
|
|
||||||
if (api in staticEndpoint) {
|
if (api in staticEndpoint) {
|
||||||
logger.info("Mai2 > $api static")
|
logger.info("Mai2 > $api static")
|
||||||
return staticEndpoint[api]!!
|
return staticEndpoint[api]!!
|
||||||
}
|
}
|
||||||
|
|
||||||
timer = apiLatencyMetric(ApiLabel(api))
|
if (!handlers.containsKey(api)) {
|
||||||
return handlers[api]?.handle(request)?.let { if (it is String) it else it.toJson() }?.also {
|
logger.warn("Mai2 > $api not found")
|
||||||
|
return """{"returnCode":1,"apiName":"com.sega.maimai2servlet.api.$api"}"""
|
||||||
|
}
|
||||||
|
|
||||||
|
return try { metrics[api] {
|
||||||
|
handlers[api]!!.handle(request).let { if (it is String) it else it.toJson() }.also {
|
||||||
if (api !in setOf("GetUserItemApi", "GetGameEventApi"))
|
if (api !in setOf("GetUserItemApi", "GetGameEventApi"))
|
||||||
logger.info("Mai2 > $api : $it")
|
logger.info("Mai2 > $api : $it")
|
||||||
} ?: {
|
|
||||||
logger.warn("Mai2 > $api not found")
|
|
||||||
"""{"returnCode":1,"apiName":"com.sega.maimai2servlet.api.$api"}"""
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} }
|
||||||
if (e is ApiException) {
|
catch (e: ApiException) {
|
||||||
apiErrorCountMetric(ApiErrorLabel(api, e.code.toString())).increment()
|
// It's a bad practice to return 200 ok on error, but this is what maimai does so we have to follow
|
||||||
// It's a bad practice to return 200 ok on error, but this is what maimai does so we have to follow
|
return ResponseEntity.ok().body("""{"returnCode":0,"apiName":"com.sega.maimai2servlet.api.$api","message":"${e.message?.replace("\"", "\\\"")} - ${e.code}"}""")
|
||||||
return ResponseEntity.ok().body("""{"returnCode":0,"apiName":"com.sega.maimai2servlet.api.$api","message":"${e.message?.replace("\"", "\\\"")} - ${e.code}"}""")
|
|
||||||
} else {
|
|
||||||
apiErrorCountMetric(ApiErrorLabel(api, e.javaClass.name)).increment()
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
timer?.record(startTime.elapsedNow().toJavaDuration())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package icu.samnyan.aqua.sega.maimai2.handler
|
package icu.samnyan.aqua.sega.maimai2.handler
|
||||||
|
|
||||||
import ext.Metrics
|
|
||||||
import ext.millis
|
import ext.millis
|
||||||
|
import icu.samnyan.aqua.net.db.APIMetrics
|
||||||
|
import icu.samnyan.aqua.net.db.unaryPlus
|
||||||
import icu.samnyan.aqua.sega.allnet.TokenChecker
|
import icu.samnyan.aqua.sega.allnet.TokenChecker
|
||||||
|
import icu.samnyan.aqua.sega.general.BaseHandler
|
||||||
import icu.samnyan.aqua.sega.maimai2.model.Mai2UserDataRepo
|
import icu.samnyan.aqua.sega.maimai2.model.Mai2UserDataRepo
|
||||||
import icu.samnyan.aqua.sega.maimai2.model.Mai2UserPlaylogRepo
|
import icu.samnyan.aqua.sega.maimai2.model.Mai2UserPlaylogRepo
|
||||||
import icu.samnyan.aqua.sega.general.BaseHandler
|
|
||||||
import icu.samnyan.aqua.sega.maimai2.Maimai2ServletController.ApiLabel
|
|
||||||
import icu.samnyan.aqua.sega.maimai2.model.request.UploadUserPlaylog
|
import icu.samnyan.aqua.sega.maimai2.model.request.UploadUserPlaylog
|
||||||
import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2UserPlaylog
|
import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2UserPlaylog
|
||||||
import icu.samnyan.aqua.sega.util.jackson.BasicMapper
|
import icu.samnyan.aqua.sega.util.jackson.BasicMapper
|
||||||
|
@ -31,8 +31,7 @@ class UploadUserPlaylogHandler(
|
||||||
val VALID_GAME_IDS = setOf("SDEZ", "SDGA", "SDGB")
|
val VALID_GAME_IDS = setOf("SDEZ", "SDGA", "SDGB")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class GameIdVersionLabel(val gameId: String, val version: String)
|
val metrics = APIMetrics("maimai2")
|
||||||
val gameVersionCountMetric = Metrics.counter<GameIdVersionLabel>("aquadx_maimai2_game_version_count")
|
|
||||||
|
|
||||||
override fun handle(request: Map<String, Any>): String {
|
override fun handle(request: Map<String, Any>): String {
|
||||||
val req = mapper.convert(request, UploadUserPlaylog::class.java)
|
val req = mapper.convert(request, UploadUserPlaylog::class.java)
|
||||||
|
@ -41,7 +40,7 @@ class UploadUserPlaylogHandler(
|
||||||
if (version != null) {
|
if (version != null) {
|
||||||
val session = TokenChecker.getCurrentSession()
|
val session = TokenChecker.getCurrentSession()
|
||||||
val gameId = if (session?.gameId in VALID_GAME_IDS) session!!.gameId else ""
|
val gameId = if (session?.gameId in VALID_GAME_IDS) session!!.gameId else ""
|
||||||
gameVersionCountMetric(GameIdVersionLabel(gameId, version)).increment()
|
+metrics["game_version_count", "game_id" to gameId, "version" to version]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save if the user is registered
|
// Save if the user is registered
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package icu.samnyan.aqua.sega.wacca
|
package icu.samnyan.aqua.sega.wacca
|
||||||
|
|
||||||
import ext.*
|
import ext.*
|
||||||
|
import icu.samnyan.aqua.net.db.APIMetrics
|
||||||
import icu.samnyan.aqua.net.db.AquaGameOptions
|
import icu.samnyan.aqua.net.db.AquaGameOptions
|
||||||
import icu.samnyan.aqua.net.games.wacca.Wacca
|
import icu.samnyan.aqua.net.games.wacca.Wacca
|
||||||
import icu.samnyan.aqua.net.utils.ApiException
|
import icu.samnyan.aqua.net.utils.ApiException
|
||||||
|
@ -13,7 +14,6 @@ import icu.samnyan.aqua.sega.wacca.WaccaOptionType.*
|
||||||
import icu.samnyan.aqua.sega.wacca.model.BaseRequest
|
import icu.samnyan.aqua.sega.wacca.model.BaseRequest
|
||||||
import icu.samnyan.aqua.sega.wacca.model.db.*
|
import icu.samnyan.aqua.sega.wacca.model.db.*
|
||||||
import io.ktor.client.utils.*
|
import io.ktor.client.utils.*
|
||||||
import io.micrometer.core.instrument.Timer
|
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
|
@ -21,8 +21,6 @@ import org.springframework.web.bind.annotation.RestController
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.time.TimeSource
|
|
||||||
import kotlin.time.toJavaDuration
|
|
||||||
|
|
||||||
val empty = emptyList<Any>()
|
val empty = emptyList<Any>()
|
||||||
|
|
||||||
|
@ -35,11 +33,7 @@ class WaccaServer {
|
||||||
@Autowired lateinit var rp: WaccaRepos
|
@Autowired lateinit var rp: WaccaRepos
|
||||||
@Autowired lateinit var wacca: Wacca
|
@Autowired lateinit var wacca: Wacca
|
||||||
|
|
||||||
data class ApiLabel(val api: String)
|
val metrics = APIMetrics("wacca")
|
||||||
data class ApiErrorLabel(val api: String, val error: String)
|
|
||||||
val apiCountMetric = Metrics.counter<ApiLabel>("aquadx_wacca_api_count")
|
|
||||||
val apiErrorCountMetric = Metrics.counter<ApiErrorLabel>("aquadx_wacca_api_error_count")
|
|
||||||
val apiLatencyMetric = Metrics.timer<ApiLabel>("aquadx_wacca_api_latency")
|
|
||||||
|
|
||||||
val handlerMap = mutableMapOf<String, (BaseRequest, List<Any>) -> Any>()
|
val handlerMap = mutableMapOf<String, (BaseRequest, List<Any>) -> Any>()
|
||||||
val cacheMap = mutableMapOf<String, String>()
|
val cacheMap = mutableMapOf<String, String>()
|
||||||
|
@ -88,38 +82,28 @@ class WaccaServer {
|
||||||
/** Handle all requests */
|
/** Handle all requests */
|
||||||
@API("/api/**")
|
@API("/api/**")
|
||||||
fun handle(req: HttpServletRequest, @RB body: String): Any {
|
fun handle(req: HttpServletRequest, @RB body: String): Any {
|
||||||
val startTime = TimeSource.Monotonic.markNow()
|
val path = req.requestURI.removePrefix("/g/wacca").removePrefix("/WaccaServlet")
|
||||||
var timer: Timer? = null
|
.removePrefix("/api").removePrefix("/").lowercase()
|
||||||
var api = ""
|
|
||||||
return try {
|
|
||||||
val path = req.requestURI.removePrefix("/g/wacca").removePrefix("/WaccaServlet")
|
|
||||||
.removePrefix("/api").removePrefix("/").lowercase()
|
|
||||||
|
|
||||||
if (path !in cacheMap && path !in handlerMap) return resp("[]", 1, "Not Found")
|
if (path in cacheMap) return resp(cacheMap[path]!!)
|
||||||
api = path
|
else if (path !in handlerMap) return resp("[]", 1, "Not Found")
|
||||||
apiCountMetric(ApiLabel(api)).increment()
|
|
||||||
if (path in cacheMap) return resp(cacheMap[path]!!)
|
|
||||||
|
|
||||||
log.info("Wacca < $path : $body")
|
log.info("Wacca < $path : $body")
|
||||||
|
|
||||||
timer = apiLatencyMetric(ApiLabel(api))
|
return try { metrics[path] {
|
||||||
val br = JACKSON.parse<BaseRequest>(body)
|
val br = JACKSON.parse<BaseRequest>(body)
|
||||||
handlerMap[path]!!(br, br.params).let { when (it) {
|
handlerMap[path]!!(br, br.params).let { when (it) {
|
||||||
is String -> resp(it)
|
is String -> resp(it)
|
||||||
is List<*> -> resp(it.toJson())
|
is List<*> -> resp(it.toJson())
|
||||||
else -> error("Invalid response type ${it.javaClass}")
|
else -> error("Invalid response type ${it.javaClass}")
|
||||||
} }.also { log.info("Wacca > $path : ${it.body}") }
|
} }.also { log.info("Wacca > $path : ${it.body}") }
|
||||||
}
|
} }
|
||||||
catch (e: ApiException) {
|
catch (e: ApiException) {
|
||||||
apiErrorCountMetric(ApiErrorLabel(api, e.code.toString())).increment()
|
|
||||||
resp("[]", e.code, e.message ?: "")
|
resp("[]", e.code, e.message ?: "")
|
||||||
}
|
}
|
||||||
catch (e: Exception) {
|
catch (e: Exception) {
|
||||||
apiErrorCountMetric(ApiErrorLabel(api, e.javaClass.name)).increment()
|
|
||||||
log.error("Wacca > Error", e)
|
log.error("Wacca > Error", e)
|
||||||
resp("[]", 500, e.message ?: "")
|
resp("[]", 500, e.message ?: "")
|
||||||
} finally {
|
|
||||||
timer?.record(startTime.elapsedNow().toJavaDuration())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue