Players registration
This commit is contained in:
@@ -38,16 +38,33 @@ interface ApiHandler {
|
||||
}
|
||||
|
||||
fun getPayload(request: HttpServletRequest): Json {
|
||||
return request.getAttribute(PAYLOAD_KEY) as Json ?: throw ApiException(HttpServletResponse.SC_BAD_REQUEST, "no payload")
|
||||
return request.getAttribute(PAYLOAD_KEY) as Json? ?: throw ApiException(HttpServletResponse.SC_BAD_REQUEST, "no payload")
|
||||
}
|
||||
|
||||
fun getObjectPayload(request: HttpServletRequest): Json.Object {
|
||||
val json = request.getAttribute(PAYLOAD_KEY) as Json? ?: throw ApiException(HttpServletResponse.SC_BAD_REQUEST, "no payload")
|
||||
if (!json.isObject) badRequest("expecting a json object")
|
||||
return json.asObject()
|
||||
}
|
||||
|
||||
fun getArrayPayload(request: HttpServletRequest): Json.Array {
|
||||
val json = request.getAttribute(PAYLOAD_KEY) as Json? ?: throw ApiException(HttpServletResponse.SC_BAD_REQUEST, "no payload")
|
||||
if (!json.isArray) badRequest("expecting a json array")
|
||||
return json.asArray()
|
||||
}
|
||||
|
||||
fun getSelector(request: HttpServletRequest): String? {
|
||||
return request.getAttribute(SELECTOR_KEY) as String?
|
||||
}
|
||||
|
||||
fun getSubSelector(request: HttpServletRequest): String? {
|
||||
return request.getAttribute(SUBSELECTOR_KEY) as String?
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PAYLOAD_KEY = "PAYLOAD"
|
||||
const val SELECTOR_KEY = "SELECTOR"
|
||||
const val SUBSELECTOR_KEY = "SUBSELECTOR"
|
||||
val logger = LoggerFactory.getLogger("api")
|
||||
fun badRequest(msg: String = "bad request"): Nothing = throw ApiException(HttpServletResponse.SC_BAD_REQUEST, msg)
|
||||
}
|
||||
|
@@ -4,15 +4,14 @@ import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.model.Player
|
||||
import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.model.fromJson
|
||||
import org.jeudego.pairgoth.model.toJson
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
class PlayerHandler: ApiHandler {
|
||||
object PlayerHandler: ApiHandler {
|
||||
|
||||
override fun post(request: HttpServletRequest): Json {
|
||||
val json = getPayload(request)
|
||||
if (!json.isObject) ApiHandler.badRequest("expecting a json object")
|
||||
val payload = json.asObject()
|
||||
val payload = getObjectPayload(request)
|
||||
|
||||
// player parsing
|
||||
val player = Player.fromJson(payload)
|
||||
@@ -20,4 +19,17 @@ class PlayerHandler: ApiHandler {
|
||||
Store.addPlayer(player)
|
||||
return Json.Object("success" to true, "id" to player.id)
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(request: HttpServletRequest): Json {
|
||||
return when (val id = getSelector(request)?.toIntOrNull()) {
|
||||
null -> Json.Array(Store.getPlayersIDs())
|
||||
else -> Store.getPlayer(id)?.toJson() ?: ApiHandler.badRequest("no player with id #${id}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun put(request: HttpServletRequest): Json {
|
||||
val id = getSelector(request)?.toIntOrNull() ?: ApiHandler.badRequest("missing or invalid player selector")
|
||||
TODO()
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,43 @@
|
||||
package org.jeudego.pairgoth.api
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
object RegistrationHandler: ApiHandler {
|
||||
|
||||
private fun getTournament(request: HttpServletRequest): Tournament {
|
||||
val tournamentId = getSelector(request)?.toIntOrNull() ?: badRequest("invalid tournament id")
|
||||
return Store.getTournament(tournamentId) ?: badRequest("unknown tournament id")
|
||||
}
|
||||
|
||||
override fun get(request: HttpServletRequest): Json {
|
||||
val tournament = getTournament(request)
|
||||
return when (val pairableId = getSubSelector(request)?.toIntOrNull()) {
|
||||
null -> when (val round = request.getParameter("round")?.toIntOrNull()) {
|
||||
null -> Json.Array(tournament.pairables.map {
|
||||
Json.Object(
|
||||
"id" to it.key,
|
||||
"skip" to Json.Array(it.value)
|
||||
)
|
||||
})
|
||||
else -> Json.Array(tournament.pairables.filter { !it.value.contains(round) }.keys)
|
||||
}
|
||||
else -> Json.Array(tournament.pairables[pairableId])
|
||||
}
|
||||
}
|
||||
|
||||
override fun post(request: HttpServletRequest): Json {
|
||||
val tournament = getTournament(request)
|
||||
val payload = getObjectPayload(request)
|
||||
val pairableId = payload.getInt("id") ?: badRequest("missing player id")
|
||||
val skip = ( payload.getArray("skip") ?: Json.Array() ).map { Json.TypeUtils.toInt(it) ?: badRequest("invalid round number") }
|
||||
if (tournament.pairables.contains(pairableId)) badRequest("already registered player: $pairableId")
|
||||
/* CB TODO - update action for SSE channel */
|
||||
tournament.pairables[pairableId] = skip.toMutableSet()
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
}
|
@@ -16,12 +16,10 @@ import org.jeudego.pairgoth.model.toJson
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
class TournamentHandler(): ApiHandler {
|
||||
object TournamentHandler: ApiHandler {
|
||||
|
||||
override fun post(request: HttpServletRequest): Json {
|
||||
val json = getPayload(request)
|
||||
if (!json.isObject) badRequest("expecting a json object")
|
||||
val payload = json.asObject()
|
||||
val payload = getObjectPayload(request)
|
||||
|
||||
// tournament parsing
|
||||
val tournament = Tournament.fromJson(payload)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package org.jeudego.pairgoth.model
|
||||
|
||||
sealed class Pairable(val id: Int, val name: String, val rating: Double, val rank: Int)
|
||||
sealed class Pairable(val id: Int, val name: String, val rating: Double, val rank: Int) {
|
||||
}
|
||||
|
||||
fun Pairable.displayRank(): String = when {
|
||||
rank < 0 -> "${-rank}k"
|
||||
@@ -22,3 +23,4 @@ fun Pairable.setRank(rankStr: String): Int {
|
||||
else -> throw Error("impossible")
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -33,8 +33,9 @@ data class Tournament(
|
||||
var gobanSize: Int = 19,
|
||||
var komi: Double = 7.5
|
||||
) {
|
||||
companion object {}
|
||||
val pairables = mutableMapOf<Int, Pairable>()
|
||||
companion object
|
||||
// player/team id -> set of skipped rounds
|
||||
val pairables = mutableMapOf<Int, MutableSet<Int>>()
|
||||
}
|
||||
|
||||
// Serialization
|
||||
|
@@ -22,8 +22,13 @@ object Store {
|
||||
fun getTournament(id: Int) = tournaments[id]
|
||||
|
||||
fun getTournamentsIDs(): Set<Int> = tournaments.keys
|
||||
|
||||
fun addPlayer(player: Player) {
|
||||
if (players.containsKey(player.id)) throw Error("player id #${player.id} already exists")
|
||||
players[player.id] = player
|
||||
}
|
||||
|
||||
fun getPlayer(id: Int) = players[id]
|
||||
|
||||
fun getPlayersIDs(): Set<Int> = players.keys
|
||||
}
|
||||
|
@@ -2,28 +2,22 @@ package org.jeudego.pairgoth.web
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.api.ApiHandler
|
||||
import org.jeudego.pairgoth.api.RegistrationHandler
|
||||
import org.jeudego.pairgoth.api.PlayerHandler
|
||||
import org.jeudego.pairgoth.api.TournamentHandler
|
||||
import org.jeudego.pairgoth.util.Colorizer
|
||||
import org.jeudego.pairgoth.util.Colorizer.green
|
||||
import org.jeudego.pairgoth.util.Colorizer.red
|
||||
import org.jeudego.pairgoth.util.parse
|
||||
import org.jeudego.pairgoth.util.toString
|
||||
import org.jeudego.pairgoth.web.ApiException
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.io.StringWriter
|
||||
import java.util.*
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.http.HttpServlet
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
class ApiServlet : HttpServlet() {
|
||||
|
||||
val tournamentHandler = TournamentHandler()
|
||||
val playerHandler = PlayerHandler()
|
||||
|
||||
public override fun doGet(request: HttpServletRequest, response: HttpServletResponse) {
|
||||
doRequest(request, response)
|
||||
}
|
||||
@@ -47,29 +41,47 @@ class ApiServlet : HttpServlet() {
|
||||
var payload: Json? = null
|
||||
var reason = "OK"
|
||||
try {
|
||||
|
||||
// validate request
|
||||
|
||||
if ("dev" == WebappManager.getProperty("webapp.env")) {
|
||||
response.addHeader("Access-Control-Allow-Origin", "*")
|
||||
}
|
||||
validateContentType(request)
|
||||
validateAccept(request);
|
||||
|
||||
val parts = uri.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
if (parts.size < 3 || parts.size > 5) throw ApiException(HttpServletResponse.SC_BAD_REQUEST)
|
||||
if (parts.size >= 4) {
|
||||
request.setAttribute(ApiHandler.SELECTOR_KEY, parts[3])
|
||||
}
|
||||
val entity = parts[2]
|
||||
// parse request URI
|
||||
|
||||
val parts = uri.split("/").filter { !it.isEmpty() }
|
||||
if (parts.size !in 2..5 || parts[0] != "api") throw ApiException(HttpServletResponse.SC_BAD_REQUEST)
|
||||
|
||||
val entity = parts[1]
|
||||
val selector = parts.getOrNull(2)?.also { request.setAttribute(ApiHandler.SELECTOR_KEY, it) }
|
||||
val subEntity = parts.getOrNull(3)
|
||||
val subSelector = parts.getOrNull(4)?.also { request.setAttribute(ApiHandler.SUBSELECTOR_KEY, it) }
|
||||
|
||||
// choose handler
|
||||
|
||||
val handler = when (entity) {
|
||||
"tournament" -> tournamentHandler
|
||||
"player" -> playerHandler
|
||||
else -> ApiHandler.badRequest("unknown entity")
|
||||
"tournament" ->
|
||||
when (subEntity) {
|
||||
null -> TournamentHandler
|
||||
"registration" -> RegistrationHandler
|
||||
else -> ApiHandler.badRequest("unknown sub-entity: $subEntity")
|
||||
}
|
||||
"player" -> PlayerHandler
|
||||
else -> ApiHandler.badRequest("unknown entity: $entity")
|
||||
}
|
||||
|
||||
// call handler
|
||||
|
||||
payload = handler.route(request, response)
|
||||
// if payload is null, it means the handler already sent the response
|
||||
if (payload != null) {
|
||||
setContentType(response)
|
||||
payload.toString(response.writer)
|
||||
}
|
||||
|
||||
} catch (apiException: ApiException) {
|
||||
reason = apiException.message ?: "unknown API error"
|
||||
if (reason == null) error(response, apiException.code) else error(
|
||||
|
@@ -1,2 +1,2 @@
|
||||
format = %date [%level] %ip [%logger] %message (@%file:%line:%column)
|
||||
format = [%level] %ip [%logger] %message (@%file:%line:%column)
|
||||
level = trace
|
||||
|
Reference in New Issue
Block a user