Events model and SSE servlet ready
This commit is contained in:
@@ -4,9 +4,9 @@ import com.republicate.kson.Json
|
||||
import com.republicate.kson.toJsonArray
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.model.Pairing
|
||||
import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.model.toJson
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import org.jeudego.pairgoth.web.Event
|
||||
import org.jeudego.pairgoth.web.Event.*
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
object PairingHandler: PairgothApiHandler {
|
||||
@@ -42,6 +42,27 @@ object PairingHandler: PairgothApiHandler {
|
||||
} ?: badRequest("invalid pairable id: #$id")
|
||||
}
|
||||
val games = tournament.pair(round, pairables)
|
||||
return games.map { it.toJson() }.toJsonArray()
|
||||
val ret = games.map { it.toJson() }.toJsonArray()
|
||||
Event.dispatch(gamesAdded, Json.Object("tournament" to tournament.id, "round" to round, "data" to ret))
|
||||
return ret
|
||||
}
|
||||
|
||||
override fun delete(request: HttpServletRequest): Json {
|
||||
val tournament = getTournament(request)
|
||||
val round = getSubSelector(request)?.toIntOrNull() ?: badRequest("invalid round number")
|
||||
// only allow last round (if players have not been paired in the last round, it *may* be possible to be more laxist...)
|
||||
if (round != tournament.games.size) badRequest("cannot delete games in other rounds but the last")
|
||||
val payload = getArrayPayload(request)
|
||||
val allPlayers = payload.size == 1 && payload[0] == "all"
|
||||
if (allPlayers) {
|
||||
tournament.games.removeLast()
|
||||
} else {
|
||||
payload.forEach {
|
||||
val id = (it as Number).toInt()
|
||||
tournament.games[round].remove(id)
|
||||
}
|
||||
}
|
||||
Event.dispatch(gamesDeleted, Json.Object("tournament" to tournament.id, "round" to round, "data" to payload))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@ import com.republicate.kson.toJsonArray
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.model.Player
|
||||
import org.jeudego.pairgoth.model.fromJson
|
||||
import org.jeudego.pairgoth.web.Event
|
||||
import org.jeudego.pairgoth.web.Event.*
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
object PlayerHandler: PairgothApiHandler {
|
||||
@@ -23,7 +25,7 @@ object PlayerHandler: PairgothApiHandler {
|
||||
// player parsing (CB TODO - team handling, based on tournament type)
|
||||
val player = Player.fromJson(payload)
|
||||
tournament.pairables[player.id] = player
|
||||
// CB TODO - handle event broadcasting
|
||||
Event.dispatch(playerAdded, Json.Object("tournament" to tournament.id, "data" to player.toJson()))
|
||||
return Json.Object("success" to true, "id" to player.id)
|
||||
}
|
||||
|
||||
@@ -34,10 +36,16 @@ object PlayerHandler: PairgothApiHandler {
|
||||
val payload = getObjectPayload(request)
|
||||
val updated = Player.fromJson(payload, player as Player)
|
||||
tournament.pairables[updated.id] = updated
|
||||
Event.dispatch(playerUpdated, Json.Object("tournament" to tournament.id, "data" to player.toJson()))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
override fun delete(request: HttpServletRequest): Json {
|
||||
return super.delete(request)
|
||||
val tournament = getTournament(request) ?: badRequest("invalid tournament")
|
||||
val id = getSubSelector(request)?.toIntOrNull() ?: badRequest("missing or invalid player selector")
|
||||
val player = tournament.pairables[id] ?: badRequest("invalid player id")
|
||||
tournament.pairables.remove(id)
|
||||
Event.dispatch(playerDeleted, Json.Object("tournament" to tournament.id, "data" to id))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import org.jeudego.pairgoth.model.Game
|
||||
import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.model.toJson
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import org.jeudego.pairgoth.web.Event
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
object ResultsHandler: PairgothApiHandler {
|
||||
@@ -18,30 +19,13 @@ object ResultsHandler: PairgothApiHandler {
|
||||
return games.map { it.toJson() }.toJsonArray()
|
||||
}
|
||||
|
||||
override fun post(request: HttpServletRequest): Json {
|
||||
val tournament = getTournament(request)
|
||||
val round = getSubSelector(request)?.toIntOrNull() ?: badRequest("invalid round number")
|
||||
val payload = getObjectPayload(request)
|
||||
val game = tournament.games[round][payload.getInt("id")] ?: badRequest("invalid game id")
|
||||
game.result = Game.Result.valueOf(payload.getString("result") ?: badRequest("missing result"))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
override fun put(request: HttpServletRequest): Json {
|
||||
val tournament = getTournament(request)
|
||||
val round = getSubSelector(request)?.toIntOrNull() ?: badRequest("invalid round number")
|
||||
val payload = getObjectPayload(request)
|
||||
val game = tournament.games[round][payload.getInt("id")] ?: badRequest("invalid game id")
|
||||
game.result = Game.Result.valueOf(payload.getString("result") ?: badRequest("missing result"))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
override fun delete(request: HttpServletRequest): Json {
|
||||
val tournament = getTournament(request)
|
||||
val round = getSubSelector(request)?.toIntOrNull() ?: badRequest("invalid round number")
|
||||
val payload = getObjectPayload(request)
|
||||
val game = tournament.games[round][payload.getInt("id")] ?: badRequest("invalid game id")
|
||||
tournament.games[round].remove(payload.getInt("id") ?: badRequest("invalid game id")) ?: badRequest("invalid game id")
|
||||
Event.dispatch(Event.resultUpdated, Json.Object("tournament" to tournament.id, "round" to round, "data" to game))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,8 @@ 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 org.jeudego.pairgoth.web.Event
|
||||
import org.jeudego.pairgoth.web.Event.*
|
||||
import org.w3c.dom.Element
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
@@ -26,8 +28,8 @@ object TournamentHandler: PairgothApiHandler {
|
||||
is Element -> OpenGotha.import(payload)
|
||||
else -> badRequest("missing or invalid payload")
|
||||
}
|
||||
|
||||
Store.addTournament(tournament)
|
||||
Event.dispatch(tournamentAdded, tournament.toJson())
|
||||
return Json.Object("success" to true, "id" to tournament.id)
|
||||
}
|
||||
|
||||
@@ -42,12 +44,14 @@ object TournamentHandler: PairgothApiHandler {
|
||||
updated.games.addAll(tournament.games)
|
||||
updated.criteria.addAll(tournament.criteria)
|
||||
Store.replaceTournament(updated)
|
||||
Event.dispatch(tournamentUpdated, tournament.toJson())
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
override fun delete(request: HttpServletRequest): Json {
|
||||
val tournament = getTournament(request) ?: badRequest("missing or invalid tournament id")
|
||||
Store.deleteTournament(tournament)
|
||||
Event.dispatch(tournamentDeleted, tournament.id)
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
}
|
||||
|
27
webapp/src/main/kotlin/org/jeudego/pairgoth/web/Event.kt
Normal file
27
webapp/src/main/kotlin/org/jeudego/pairgoth/web/Event.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
|
||||
import info.macias.sse.events.MessageEvent
|
||||
|
||||
enum class Event {
|
||||
tournamentAdded,
|
||||
tournamentUpdated,
|
||||
tournamentDeleted,
|
||||
playerAdded,
|
||||
playerUpdated,
|
||||
playerDeleted,
|
||||
gamesAdded,
|
||||
gamesDeleted,
|
||||
resultUpdated,
|
||||
;
|
||||
|
||||
companion object {
|
||||
private val sse: SSEServlet by lazy { SSEServlet.getInstance() }
|
||||
private fun <T> buildEvent(event: Event, data: T) = MessageEvent.Builder()
|
||||
.setEvent(event.name)
|
||||
.setData(data.toString())
|
||||
.build()
|
||||
internal fun <T> dispatch(event: Event, data: T) {
|
||||
sse.broadcast(buildEvent(event, data))
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package org.jeudego.pairgoth.web.sse
|
||||
package org.jeudego.pairgoth.web
|
||||
|
||||
import info.macias.sse.EventBroadcast
|
||||
import info.macias.sse.events.MessageEvent
|
||||
import info.macias.sse.servlet3.ServletEventTarget
|
||||
import org.slf4j.LoggerFactory
|
||||
import javax.servlet.http.HttpServlet
|
||||
@@ -11,6 +12,12 @@ import javax.servlet.http.HttpServletResponse
|
||||
class SSEServlet: HttpServlet() {
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger("sse")
|
||||
private var zeInstance: SSEServlet? = null
|
||||
internal fun getInstance(): SSEServlet = zeInstance ?: throw Error("SSE servlet not ready")
|
||||
}
|
||||
init {
|
||||
if (zeInstance != null) throw Error("Multiple instances of SSE servlet found!")
|
||||
zeInstance = this
|
||||
}
|
||||
private val broadcast = EventBroadcast()
|
||||
|
||||
@@ -18,4 +25,6 @@ class SSEServlet: HttpServlet() {
|
||||
logger.trace("<< new channel")
|
||||
broadcast.addSubscriber(ServletEventTarget(req), req.getHeader("Last-Event-Id"))
|
||||
}
|
||||
|
||||
internal fun broadcast(message: MessageEvent) = broadcast.broadcast(message)
|
||||
}
|
@@ -33,7 +33,7 @@
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>sse</servlet-name>
|
||||
<servlet-class>org.jeudego.pairgoth.web.sse.SSEServlet</servlet-class>
|
||||
<servlet-class>org.jeudego.pairgoth.web.SSEServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
<async-supported>true</async-supported>
|
||||
</servlet>
|
||||
|
@@ -3,6 +3,7 @@ package org.jeudego.pairgoth.test
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.api.ApiHandler
|
||||
import org.jeudego.pairgoth.web.ApiServlet
|
||||
import org.jeudego.pairgoth.web.SSEServlet
|
||||
import org.jeudego.pairgoth.web.WebappManager
|
||||
import org.mockito.kotlin.argumentCaptor
|
||||
import org.mockito.kotlin.doAnswer
|
||||
@@ -31,6 +32,7 @@ object TestAPI {
|
||||
fun Any?.toUnit() = Unit
|
||||
|
||||
private val apiServlet = ApiServlet()
|
||||
private val sseServlet = SSEServlet()
|
||||
|
||||
private fun <T> testRequest(reqMethod: String, uri: String, payload: T? = null): Json {
|
||||
|
||||
|
Reference in New Issue
Block a user