From 30a9bad2593b8cdcafcb62caccd345ccff5729eb Mon Sep 17 00:00:00 2001 From: Claude Brisson Date: Mon, 15 May 2023 10:50:51 +0200 Subject: [PATCH] API in progress: tournament and registration ok --- test.sh | 18 +--- .../pairgoth/api/PairgothApiHandler.kt | 11 ++ .../jeudego/pairgoth/api/PairingHandler.kt | 21 ++++ .../org/jeudego/pairgoth/api/PlayerHandler.kt | 48 +++++---- .../pairgoth/api/RegistrationHandler.kt | 43 -------- .../jeudego/pairgoth/api/ResultsHandler.kt | 4 + .../jeudego/pairgoth/api/StandingsHandler.kt | 4 + .../jeudego/pairgoth/api/TournamentHandler.kt | 40 ++++--- .../kotlin/org/jeudego/pairgoth/model/Game.kt | 5 + .../org/jeudego/pairgoth/model/Pairable.kt | 72 ++++++++++++- .../org/jeudego/pairgoth/model/Player.kt | 39 ------- .../kotlin/org/jeudego/pairgoth/model/Team.kt | 4 - .../org/jeudego/pairgoth/model/Tournament.kt | 100 ++++++++++-------- .../org/jeudego/pairgoth/store/Store.kt | 19 ++-- .../org/jeudego/pairgoth/web/ApiServlet.kt | 11 +- 15 files changed, 248 insertions(+), 191 deletions(-) create mode 100644 webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt create mode 100644 webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt delete mode 100644 webapp/src/main/kotlin/org/jeudego/pairgoth/api/RegistrationHandler.kt create mode 100644 webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt create mode 100644 webapp/src/main/kotlin/org/jeudego/pairgoth/api/StandingsHandler.kt create mode 100644 webapp/src/main/kotlin/org/jeudego/pairgoth/model/Game.kt delete mode 100644 webapp/src/main/kotlin/org/jeudego/pairgoth/model/Player.kt diff --git a/test.sh b/test.sh index 155e247..d205924 100755 --- a/test.sh +++ b/test.sh @@ -3,25 +3,17 @@ curl -s --header "Accept: application/json" --header "Content-Type: application/json" \ --request POST \ --data '{ "type":"INDIVIDUAL","name":"Mon Tournoi", "shortName": "mon-tournoi", "startDate": "2023-05-10", "endDate": "2023-05-12", "country": "FR", "location": "Marseille", "online": false, "timeSystem": { "type": "fisher", "mainTime": "1200", "increment": "10" }, "pairing": { "type": "ROUNDROBIN" } }' \ - http://localhost:8080/api/tournament + http://localhost:8080/api/tour -curl -s --header "Accept: application/json" http://localhost:8080/api/tournament +curl -s --header "Accept: application/json" http://localhost:8080/api/tour -curl -s --header "Accept: application/json" http://localhost:8080/api/tournament/1 +curl -s --header "Accept: application/json" http://localhost:8080/api/tour/1 curl -s --header "Accept: application/json" --header "Content-Type: application/json" \ --request POST \ --data '{ "name": "Burma", "firstname": "Nestor", "rating": 1600, "rank": -2, "country": "FR", "club": "13Ma" }' \ - http://localhost:8080/api/player - -curl -s --header "Accept: application/json" http://localhost:8080/api/player - -curl -s --header "Accept: application/json" --header "Content-Type: application/json" \ - --request POST \ - --data '{ "id": 1 }' \ - http://localhost:8080/api/tournament/1/registration - -curl -s --header "Accept: application/json" http://localhost:8080/api/tournament/1/registration + http://localhost:8080/api/tour/1/part +curl -s --header "Accept: application/json" http://localhost:8080/api/tour/1/part diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt new file mode 100644 index 0000000..fc5297c --- /dev/null +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt @@ -0,0 +1,11 @@ +package org.jeudego.pairgoth.api + +import org.jeudego.pairgoth.model.Tournament +import org.jeudego.pairgoth.store.Store +import javax.servlet.http.HttpServletRequest + +interface PairgothApiHandler: ApiHandler { + + fun getTournament(request: HttpServletRequest): Tournament? = getSelector(request)?.toIntOrNull()?.let { Store.getTournament(it) } + +} \ No newline at end of file diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt new file mode 100644 index 0000000..1e99ef5 --- /dev/null +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt @@ -0,0 +1,21 @@ +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 PairingHandler: ApiHandler { + + private fun getTournament(request: HttpServletRequest): Tournament { + val tournamentId = getSelector(request)?.toIntOrNull() ?: ApiHandler.badRequest("invalid tournament id") + return Store.getTournament(tournamentId) ?: ApiHandler.badRequest("unknown tournament id") + } + + override fun get(request: HttpServletRequest): Json { + val tournament = getTournament(request) + val round = request.getParameter("round")?.toIntOrNull() ?: badRequest("invalid round number") + return Json.Object(); + } +} \ No newline at end of file diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt index c8a168a..5dc9d2d 100644 --- a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt @@ -1,35 +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.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 -object PlayerHandler: ApiHandler { - - override fun post(request: HttpServletRequest): Json { - val payload = getObjectPayload(request) - - // player parsing - val player = Player.fromJson(payload) - - Store.addPlayer(player) - return Json.Object("success" to true, "id" to player.id) - } +object PlayerHandler: PairgothApiHandler { 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}") + val tournament = getTournament(request) ?: badRequest("invalid tournament") + return when (val pid = getSubSelector(request)?.toIntOrNull()) { + null -> Json.Array(tournament.pairables.values.map { it.toJson() }) + else -> tournament.pairables[pid]?.toJson() ?: badRequest("no player with id #${pid}") } } - override fun put(request: HttpServletRequest): Json { - val id = getSelector(request)?.toIntOrNull() ?: ApiHandler.badRequest("missing or invalid player selector") - TODO() + override fun post(request: HttpServletRequest): Json { + val tournament = getTournament(request) ?: badRequest("invalid tournament") + val payload = getObjectPayload(request) + // player parsing (CB TODO - team handling, based on tournament type) + val player = Player.fromJson(payload) + // CB TODO - handle concurrency + tournament.pairables[player.id] = player + // CB TODO - handle event broadcasting + return Json.Object("success" to true, "id" to player.id) } + override fun put(request: HttpServletRequest): Json { + 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") + val payload = getObjectPayload(request) + val updated = Player.fromJson(payload, player as Player) + tournament.pairables[updated.id] = updated + return Json.Object("success" to true) + } + + override fun delete(request: HttpServletRequest): Json { + return super.delete(request) + } } diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/RegistrationHandler.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/RegistrationHandler.kt deleted file mode 100644 index 6e26cc5..0000000 --- a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/RegistrationHandler.kt +++ /dev/null @@ -1,43 +0,0 @@ -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) - } - -} diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt new file mode 100644 index 0000000..b87a022 --- /dev/null +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt @@ -0,0 +1,4 @@ +package org.jeudego.pairgoth.api + +class ResultsHandler: ApiHandler { +} \ No newline at end of file diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/StandingsHandler.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/StandingsHandler.kt new file mode 100644 index 0000000..9dbc249 --- /dev/null +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/StandingsHandler.kt @@ -0,0 +1,4 @@ +package org.jeudego.pairgoth.api + +class StandingsHandler: ApiHandler { +} \ No newline at end of file diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt index 7f93c69..e2028f1 100644 --- a/webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt @@ -2,21 +2,20 @@ package org.jeudego.pairgoth.api import com.republicate.kson.Json import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest -import org.jeudego.pairgoth.model.CanadianByoyomi -import org.jeudego.pairgoth.model.FisherTime -import org.jeudego.pairgoth.model.MacMahon -import org.jeudego.pairgoth.model.Rules -import org.jeudego.pairgoth.model.StandardByoyomi -import org.jeudego.pairgoth.model.SuddenDeath -import org.jeudego.pairgoth.model.TimeSystem import org.jeudego.pairgoth.model.Tournament -import org.jeudego.pairgoth.model.TournamentType import org.jeudego.pairgoth.model.fromJson import org.jeudego.pairgoth.model.toJson import org.jeudego.pairgoth.store.Store import javax.servlet.http.HttpServletRequest -object TournamentHandler: ApiHandler { +object TournamentHandler: PairgothApiHandler { + + override fun get(request: HttpServletRequest): Json { + return when (val id = getSelector(request)?.toIntOrNull()) { + null -> Json.Array(Store.getTournamentsIDs()) + else -> Store.getTournament(id)?.toJson() ?: badRequest("no tournament with id #${id}") + } + } override fun post(request: HttpServletRequest): Json { val payload = getObjectPayload(request) @@ -28,15 +27,22 @@ object TournamentHandler: ApiHandler { return Json.Object("success" to true, "id" to tournament.id) } - override fun get(request: HttpServletRequest): Json { - return when (val id = getSelector(request)?.toIntOrNull()) { - null -> Json.Array(Store.getTournamentsIDs()) - else -> Store.getTournament(id)?.toJson() ?: badRequest("no tournament with id #${id}") - } + override fun put(request: HttpServletRequest): Json { + val tournament = getTournament(request) ?: badRequest("missing or invalid tournament id") + val payload = getObjectPayload(request) + // disallow changing type + if (payload.getString("type")?.let { it != tournament.type.name } == true) badRequest("tournament type cannot be changed") + val updated = Tournament.fromJson(payload, tournament) + updated.pairables.putAll(tournament.pairables) + updated.games.addAll(tournament.games) + updated.criteria.addAll(tournament.criteria) + Store.replaceTournament(updated) + return Json.Object("success" to true) } - override fun put(request: HttpServletRequest): Json { - val id = getSelector(request)?.toIntOrNull() ?: badRequest("missing or invalid tournament selector") - TODO() + override fun delete(request: HttpServletRequest): Json { + val tournament = getTournament(request) ?: badRequest("missing or invalid tournament id") + Store.deleteTournament(tournament) + return Json.Object("success" to true) } } diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Game.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Game.kt new file mode 100644 index 0000000..cdc149f --- /dev/null +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Game.kt @@ -0,0 +1,5 @@ +package org.jeudego.pairgoth.model + +import org.jeudego.pairgoth.store.Store + +data class Game(val white: Int, val black: Int, var result: Char = '?', val id: Int = Store.nextGameId) diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairable.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairable.kt index c4ab39a..0b03575 100644 --- a/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairable.kt +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairable.kt @@ -1,6 +1,16 @@ package org.jeudego.pairgoth.model -sealed class Pairable(val id: Int, val name: String, val rating: Double, val rank: Int) { +import com.republicate.kson.Json +import org.jeudego.pairgoth.api.ApiHandler +import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest +import org.jeudego.pairgoth.store.Store +import kotlin.math.roundToInt + +// Pairable + +sealed class Pairable(val id: Int, val name: String, open val rating: Double, open val rank: Int) { + abstract fun toJson(): Json.Object + val skip = mutableSetOf() // skipped rounds } fun Pairable.displayRank(): String = when { @@ -24,3 +34,63 @@ fun Pairable.setRank(rankStr: String): Int { } } +// Player + +class Player( + id: Int, + name: String, + var firstname: String, + rating: Double, + rank: Int, + var country: String, + var club: String +): Pairable(id, name, rating, rank) { + companion object + // used to store external IDs ("FFG" => FFG ID, "EGF" => EGF PIN, "AGA" => AGA ID ...) + val externalIds = mutableMapOf() + override fun toJson() = Json.Object( + "id" to id, + "name" to name, + "firstname" to firstname, + "rating" to rating, + "rank" to rank, + "country" to country, + "club" to club + ) +} + +fun Player.Companion.fromJson(json: Json.Object, default: Player? = null) = Player( + id = json.getInt("id") ?: default?.id ?: Store.nextPlayerId, + name = json.getString("name") ?: default?.name ?: badRequest("missing name"), + firstname = json.getString("firstname") ?: default?.firstname ?: badRequest("missing firstname"), + rating = json.getDouble("rating") ?: default?.rating ?: badRequest("missing rating"), + rank = json.getInt("rank") ?: default?.rank ?: badRequest("missing rank"), + country = json.getString("country") ?: default?.country ?: badRequest("missing country"), + club = json.getString("club") ?: default?.club ?: badRequest("missing club") +) + +// Team + +class Team(id: Int, name: String): Pairable(id, name, 0.0, 0) { + companion object {} + val players = mutableSetOf() + override val rating: Double get() = if (players.isEmpty()) super.rating else players.sumOf { player -> player.rating } / players.size + override val rank: Int get() = if (players.isEmpty()) super.rank else (players.sumOf { player -> player.rank.toDouble() } / players.size).roundToInt() + override fun toJson() = Json.Object( + "id" to id, + "name" to name, + "players" to Json.Array(players.map { it.toJson() }) + ) +} + +fun Team.Companion.fromJson(json: Json.Object) = Team( + id = json.getInt("id") ?: Store.nextPlayerId, + name = json.getString("name") ?: badRequest("missing name") +).apply { + json.getArray("players")?.let { arr -> + arr.map { + if (it != null && it is Json.Object) Player.fromJson(it) + else badRequest("invalid players array") + } + } ?: badRequest("missing players") +} diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Player.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Player.kt deleted file mode 100644 index 8fd625a..0000000 --- a/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Player.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.jeudego.pairgoth.model - -import com.republicate.kson.Json -import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest -import org.jeudego.pairgoth.store.Store - -class Player( - id: Int, - name: String, - var firstname: String, - rating: Double, - rank: Int, - var country: String, - var club: String -): Pairable(id, name, rating, rank) { - companion object - // used to store external IDs ("FFG" => FFG ID, "EGF" => EGF PIN, "AGA" => AGA ID ...) - val externalIds = mutableMapOf() -} - -fun Player.Companion.fromJson(json: Json.Object) = Player( - id = json.getInt("id") ?: Store.nextPlayerId, - name = json.getString("name") ?: badRequest("missing name"), - firstname = json.getString("firstname") ?: badRequest("missing firstname"), - rating = json.getDouble("rating") ?: badRequest("missing rating"), - rank = json.getInt("rank") ?: badRequest("missing rank"), - country = json.getString("country") ?: badRequest("missing country"), - club = json.getString("club") ?: "" -) - -fun Player.toJson() = Json.Object( - "id" to id, - "name" to name, - "firstname" to firstname, - "rating" to rating, - "rank" to rank, - "country" to country, - "club" to club -) diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Team.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Team.kt index d1bcac3..0c4f25a 100644 --- a/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Team.kt +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Team.kt @@ -1,6 +1,2 @@ package org.jeudego.pairgoth.model -class Team(id: Int, name: String, rating: Double, rank: Int): Pairable(id, name, rating, rank) { - companion object {} - val players = mutableSetOf() -} diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt index 44ce84f..7d61cbb 100644 --- a/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt @@ -6,55 +6,69 @@ import org.jeudego.pairgoth.api.ApiHandler import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest import org.jeudego.pairgoth.store.Store -enum class TournamentType(val playersNumber: Int) { - INDIVIDUAL(1), - PAIRGO(2), - RENGO2(2), - RENGO3(3), - TEAM2(2), - TEAM3(3), - TEAM4(4), - TEAM5(5); -} - data class Tournament( - var id: Int, - var type: TournamentType, - var name: String, - var shortName: String, - var startDate: LocalDate, - var endDate: LocalDate, - var country: String, - var location: String, - var online: Boolean, - var timeSystem: TimeSystem, - var pairing: Pairing, - var rules: Rules = Rules.FRENCH, - var gobanSize: Int = 19, - var komi: Double = 7.5 + val id: Int, + val type: Type, + val name: String, + val shortName: String, + val startDate: LocalDate, + val endDate: LocalDate, + val country: String, + val location: String, + val online: Boolean, + val timeSystem: TimeSystem, + val pairing: Pairing, + val rules: Rules = Rules.FRENCH, + val gobanSize: Int = 19, + val komi: Double = 7.5 ) { - companion object - // player/team id -> set of skipped rounds - val pairables = mutableMapOf>() + companion object {} + enum class Type(val playersNumber: Int) { + INDIVIDUAL(1), + PAIRGO(2), + RENGO2(2), + RENGO3(3), + TEAM2(2), + TEAM3(3), + TEAM4(4), + TEAM5(5); + } + + enum class Criterion { + NBW, MMS, SOS, SOSOS, SODOS + } + + // pairables + val pairables = mutableMapOf() + + // games per round + val games = mutableListOf>() + + // standings criteria + val criteria = mutableListOf( + if (pairing.type == Pairing.PairingType.MACMAHON) Criterion.MMS else Criterion.NBW, + Criterion.SOS, + Criterion.SOSOS + ) } // Serialization -fun Tournament.Companion.fromJson(json: Json.Object) = Tournament( - id = json.getInt("id") ?: Store.nextTournamentId, - type = json.getString("type")?.uppercase()?.let { TournamentType.valueOf(it) } ?: badRequest("missing type"), - name = json.getString("name") ?: ApiHandler.badRequest("missing name"), - shortName = json.getString("shortName") ?: ApiHandler.badRequest("missing shortName"), - startDate = json.getLocalDate("startDate") ?: ApiHandler.badRequest("missing startDate"), - endDate = json.getLocalDate("endDate") ?: ApiHandler.badRequest("missing endDate"), - country = json.getString("country") ?: ApiHandler.badRequest("missing country"), - location = json.getString("location") ?: ApiHandler.badRequest("missing location"), - online = json.getBoolean("online") ?: false, - komi = json.getDouble("komi") ?: 7.5, - rules = json.getString("rules")?.let { Rules.valueOf(it) } ?: Rules.FRENCH, - gobanSize = json.getInt("gobanSize") ?: 19, - timeSystem = TimeSystem.fromJson(json.getObject("timeSystem") ?: badRequest("missing timeSystem")), - pairing = MacMahon() +fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament? = null) = Tournament( + id = json.getInt("id") ?: default?.id ?: Store.nextTournamentId, + type = json.getString("type")?.uppercase()?.let { Tournament.Type.valueOf(it) } ?: default?.type ?: badRequest("missing type"), + name = json.getString("name") ?: default?.name ?: badRequest("missing name"), + shortName = json.getString("shortName") ?: default?.shortName ?: badRequest("missing shortName"), + startDate = json.getLocalDate("startDate") ?: default?.startDate ?: badRequest("missing startDate"), + endDate = json.getLocalDate("endDate") ?: default?.endDate ?: badRequest("missing endDate"), + country = json.getString("country") ?: default?.country ?: badRequest("missing country"), + location = json.getString("location") ?: default?.location ?: badRequest("missing location"), + online = json.getBoolean("online") ?: default?.online ?: false, + komi = json.getDouble("komi") ?: default?.komi ?: 7.5, + rules = json.getString("rules")?.let { Rules.valueOf(it) } ?: default?.rules ?: Rules.FRENCH, + gobanSize = json.getInt("gobanSize") ?: default?.gobanSize ?: 19, + timeSystem = json.getObject("timeSystem")?.let { TimeSystem.fromJson(it) } ?: default?.timeSystem ?: badRequest("missing timeSystem"), + pairing = json.getObject("pairing")?.let { Pairing.fromJson(it) } ?: default?.pairing ?: badRequest("missing pairing") ) fun Tournament.toJson() = Json.Object( diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/store/Store.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/store/Store.kt index d4267f9..826a613 100644 --- a/webapp/src/main/kotlin/org/jeudego/pairgoth/store/Store.kt +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/store/Store.kt @@ -5,14 +5,19 @@ import org.jeudego.pairgoth.model.Tournament import java.util.concurrent.atomic.AtomicInteger import kotlin.math.E +// CB TODO - handle concurrency: +// - either with concurrent maps +// - or with a thread isolation (better, covers more operations) + object Store { private val _nextTournamentId = AtomicInteger() private val _nextPlayerId = AtomicInteger() + private val _nextGameId = AtomicInteger() val nextTournamentId get() = _nextTournamentId.incrementAndGet() val nextPlayerId get() = _nextPlayerId.incrementAndGet() + val nextGameId get() = _nextGameId.incrementAndGet() private val tournaments = mutableMapOf() - private val players = mutableMapOf() fun addTournament(tournament: Tournament) { if (tournaments.containsKey(tournament.id)) throw Error("tournament id #${tournament.id} already exists") @@ -23,12 +28,14 @@ object Store { fun getTournamentsIDs(): Set = tournaments.keys - fun addPlayer(player: Player) { - if (players.containsKey(player.id)) throw Error("player id #${player.id} already exists") - players[player.id] = player + fun replaceTournament(tournament: Tournament) { + if (!tournaments.containsKey(tournament.id)) throw Error("tournament id #${tournament.id} not known") + tournaments[tournament.id] = tournament } - fun getPlayer(id: Int) = players[id] + fun deleteTournament(tournament: Tournament) { + if (!tournaments.containsKey(tournament.id)) throw Error("tournament id #${tournament.id} not known") + tournaments.remove(tournament.id) - fun getPlayersIDs(): Set = players.keys + } } diff --git a/webapp/src/main/kotlin/org/jeudego/pairgoth/web/ApiServlet.kt b/webapp/src/main/kotlin/org/jeudego/pairgoth/web/ApiServlet.kt index e1470dc..64e0063 100644 --- a/webapp/src/main/kotlin/org/jeudego/pairgoth/web/ApiServlet.kt +++ b/webapp/src/main/kotlin/org/jeudego/pairgoth/web/ApiServlet.kt @@ -2,7 +2,7 @@ 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.PairingHandler import org.jeudego.pairgoth.api.PlayerHandler import org.jeudego.pairgoth.api.TournamentHandler import org.jeudego.pairgoth.util.Colorizer.green @@ -63,10 +63,11 @@ class ApiServlet : HttpServlet() { // choose handler val handler = when (entity) { - "tournament" -> + "tour" -> when (subEntity) { null -> TournamentHandler - "registration" -> RegistrationHandler + "part" -> PlayerHandler + "pair" -> PairingHandler else -> ApiHandler.badRequest("unknown sub-entity: $subEntity") } "player" -> PlayerHandler @@ -223,8 +224,8 @@ class ApiServlet : HttpServlet() { } companion object { - protected var logger = LoggerFactory.getLogger("api") - protected const val EXPECTED_CHARSET = "utf8" + private var logger = LoggerFactory.getLogger("api") + private const val EXPECTED_CHARSET = "utf8" const val AUTH_HEADER = "Authorization" const val AUTH_PREFIX = "Bearer" private fun isJson(mimeType: String): Boolean {