From b7508a85f88633c81390ebd94f4c94bac3ea4edf Mon Sep 17 00:00:00 2001 From: Claude Brisson Date: Mon, 4 Mar 2024 09:08:11 +0100 Subject: [PATCH] One tournament files directory per user for oauth --- .../pairgoth/api/PairgothApiHandler.kt | 9 ++- .../jeudego/pairgoth/api/PairingHandler.kt | 16 ++++-- .../org/jeudego/pairgoth/api/PlayerHandler.kt | 6 +- .../jeudego/pairgoth/api/ResultsHandler.kt | 2 +- .../org/jeudego/pairgoth/api/TeamHandler.kt | 6 +- .../jeudego/pairgoth/api/TournamentHandler.kt | 18 +++--- .../org/jeudego/pairgoth/ext/OpenGotha.kt | 9 ++- .../org/jeudego/pairgoth/model/Pairable.kt | 3 +- .../org/jeudego/pairgoth/model/Tournament.kt | 8 ++- .../pairgoth/pairing/solver/BaseSolver.kt | 5 +- .../org/jeudego/pairgoth/store/FileStore.kt | 24 ++++++-- .../org/jeudego/pairgoth/store/IStore.kt | 22 -------- .../org/jeudego/pairgoth/store/MemoryStore.kt | 2 +- .../org/jeudego/pairgoth/store/Store.kt | 55 +++++++++++++++---- 14 files changed, 112 insertions(+), 73 deletions(-) delete mode 100644 api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/IStore.kt diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt index f28139c..cfed928 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt @@ -2,22 +2,21 @@ package org.jeudego.pairgoth.api import com.republicate.kson.Json import org.jeudego.pairgoth.model.Tournament -import org.jeudego.pairgoth.store.Store import org.jeudego.pairgoth.server.Event +import org.jeudego.pairgoth.store.getStore import javax.servlet.http.HttpServletRequest interface PairgothApiHandler: ApiHandler { fun getTournament(request: HttpServletRequest): Tournament<*> { val tournamentId = getSelector(request)?.toIntOrNull() ?: ApiHandler.badRequest("invalid tournament id") - return Store.getTournament(tournamentId) ?: ApiHandler.badRequest("unknown tournament id") + return getStore(request).getTournament(tournamentId) ?: ApiHandler.badRequest("unknown tournament id") } - fun Tournament<*>.dispatchEvent(event: Event, data: Json? = null) { + fun Tournament<*>.dispatchEvent(event: Event, request: HttpServletRequest, data: Json? = null) { Event.dispatch(event, Json.Object("tournament" to id, "data" to data)) // when storage is not in memory, the tournament has to be persisted if (event != Event.TournamentAdded && event != Event.TournamentDeleted) - Store.replaceTournament(this) + getStore(request).replaceTournament(this) } - } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt index 3c105ee..3a11875 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt @@ -58,7 +58,7 @@ object PairingHandler: PairgothApiHandler { } val games = tournament.pair(round, pairables) val ret = games.map { it.toJson() }.toJsonArray() - tournament.dispatchEvent(GamesAdded, Json.Object("round" to round, "games" to ret)) + tournament.dispatchEvent(GamesAdded, request, Json.Object("round" to round, "games" to ret)) return ret } @@ -98,7 +98,7 @@ object PairingHandler: PairgothApiHandler { if (payload.containsKey("t")) { game.table = payload.getString("t")?.toIntOrNull() ?: badRequest("invalid table number") } - tournament.dispatchEvent(GameUpdated, Json.Object("round" to round, "game" to game.toJson())) + tournament.dispatchEvent(GameUpdated, request, Json.Object("round" to round, "game" to game.toJson())) if (game.table != previousTable) { val sortedPairables = tournament.getSortedPairables(round) val sortedMap = sortedPairables.associateBy { @@ -113,7 +113,10 @@ object PairingHandler: PairgothApiHandler { val games = tournament.games(round).values.sortedBy { if (it.table == 0) Int.MAX_VALUE else it.table } - tournament.dispatchEvent(TablesRenumbered, Json.Object("round" to round, "games" to games.map { it.toJson() }.toCollection(Json.MutableArray()))) + tournament.dispatchEvent( + TablesRenumbered, request, + Json.Object("round" to round, "games" to games.map { it.toJson() }.toCollection(Json.MutableArray())) + ) } } return Json.Object("success" to true) @@ -132,7 +135,10 @@ object PairingHandler: PairgothApiHandler { val games = tournament.games(round).values.sortedBy { if (it.table == 0) Int.MAX_VALUE else it.table } - tournament.dispatchEvent(TablesRenumbered, Json.Object("round" to round, "games" to games.map { it.toJson() }.toCollection(Json.MutableArray()))) + tournament.dispatchEvent( + TablesRenumbered, request, + Json.Object("round" to round, "games" to games.map { it.toJson() }.toCollection(Json.MutableArray())) + ) } return Json.Object("success" to true) } @@ -161,7 +167,7 @@ object PairingHandler: PairgothApiHandler { } } } - tournament.dispatchEvent(GamesDeleted, Json.Object("round" to round, "games" to payload)) + tournament.dispatchEvent(GamesDeleted, request, Json.Object("round" to round, "games" to payload)) return Json.Object("success" to true) } } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt index 32b2c5c..99d99fd 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt @@ -24,7 +24,7 @@ object PlayerHandler: PairgothApiHandler { val payload = getObjectPayload(request) val player = Player.fromJson(payload) tournament.players[player.id] = player - tournament.dispatchEvent(PlayerAdded, player.toJson()) + tournament.dispatchEvent(PlayerAdded, request, player.toJson()) return Json.Object("success" to true, "id" to player.id) } @@ -46,7 +46,7 @@ object PlayerHandler: PairgothApiHandler { } } tournament.players[id] = updated - tournament.dispatchEvent(PlayerUpdated, player.toJson()) + tournament.dispatchEvent(PlayerUpdated, request, player.toJson()) return Json.Object("success" to true) } @@ -59,7 +59,7 @@ object PlayerHandler: PairgothApiHandler { badRequest("player is playing") } tournament.players.remove(id) ?: badRequest("invalid player id") - tournament.dispatchEvent(PlayerDeleted, Json.Object("id" to id)) + tournament.dispatchEvent(PlayerDeleted, request, Json.Object("id" to id)) return Json.Object("success" to true) } } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt index ded6361..35f1a5e 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt @@ -24,7 +24,7 @@ object ResultsHandler: PairgothApiHandler { val payload = getObjectPayload(request) val game = tournament.games(round)[payload.getInt("id")] ?: badRequest("invalid game id") game.result = Game.Result.fromSymbol(payload.getChar("result") ?: badRequest("missing result")) - tournament.dispatchEvent(Event.ResultUpdated, Json.Object("round" to round, "data" to game)) + tournament.dispatchEvent(Event.ResultUpdated, request, Json.Object("round" to round, "data" to game)) return Json.Object("success" to true) } } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TeamHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TeamHandler.kt index 0f6ce87..c89fd26 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TeamHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TeamHandler.kt @@ -25,7 +25,7 @@ object TeamHandler: PairgothApiHandler { val payload = getObjectPayload(request) val team = tournament.teamFromJson(payload) tournament.teams[team.id] = team - tournament.dispatchEvent(TeamAdded, team.toJson()) + tournament.dispatchEvent(TeamAdded, request, team.toJson()) return Json.Object("success" to true, "id" to team.id) } @@ -37,7 +37,7 @@ object TeamHandler: PairgothApiHandler { val payload = getObjectPayload(request) val updated = tournament.teamFromJson(payload, team) tournament.teams[updated.id] = updated - tournament.dispatchEvent(TeamUpdated, team.toJson()) + tournament.dispatchEvent(TeamUpdated, request, team.toJson()) return Json.Object("success" to true) } @@ -46,7 +46,7 @@ object TeamHandler: PairgothApiHandler { if (tournament !is TeamTournament) badRequest("tournament is not a team tournament") val id = getSubSelector(request)?.toIntOrNull() ?: badRequest("missing or invalid team selector") tournament.teams.remove(id) ?: badRequest("invalid team id") - tournament.dispatchEvent(TeamDeleted, Json.Object("id" to id)) + tournament.dispatchEvent(TeamDeleted, request, Json.Object("id" to id)) return Json.Object("success" to true) } } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt index d9abce3..7ae8537 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt @@ -9,9 +9,9 @@ import org.jeudego.pairgoth.model.TeamTournament 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.server.ApiServlet import org.jeudego.pairgoth.server.Event.* +import org.jeudego.pairgoth.store.getStore import org.w3c.dom.Element import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse @@ -21,12 +21,12 @@ object TournamentHandler: PairgothApiHandler { override fun get(request: HttpServletRequest, response: HttpServletResponse): Json? { val accept = request.getHeader("Accept") return when (val id = getSelector(request)?.toIntOrNull()) { - null -> Store.getTournaments().toJsonObject() + null -> getStore(request).getTournaments().toJsonObject() else -> when { - ApiServlet.isJson(accept) -> Store.getTournament(id)?.toJson() ?: badRequest("no tournament with id #${id}") + ApiServlet.isJson(accept) -> getStore(request).getTournament(id)?.toJson() ?: badRequest("no tournament with id #${id}") ApiServlet.isXml(accept) -> { - val export = Store.getTournament(id)?.let { OpenGotha.export(it) } ?: badRequest("no tournament with id #${id}") + val export = getStore(request).getTournament(id)?.let { OpenGotha.export(it) } ?: badRequest("no tournament with id #${id}") response.contentType = "application/xml; charset=UTF-8" response.writer.write(export) null // return null to indicate that we handled the response ourself @@ -42,8 +42,8 @@ object TournamentHandler: PairgothApiHandler { is Element -> OpenGotha.import(payload) else -> badRequest("missing or invalid payload") } - Store.addTournament(tournament) - tournament.dispatchEvent(TournamentAdded, tournament.toJson()) + getStore(request).addTournament(tournament) + tournament.dispatchEvent(TournamentAdded, request, tournament.toJson()) return Json.Object("success" to true, "id" to tournament.id) } @@ -63,14 +63,14 @@ object TournamentHandler: PairgothApiHandler { clear() putAll(tournament.games(round)) } - updated.dispatchEvent(TournamentUpdated, updated.toJson()) + updated.dispatchEvent(TournamentUpdated, request, updated.toJson()) return Json.Object("success" to true) } override fun delete(request: HttpServletRequest, response: HttpServletResponse): Json { val tournament = getTournament(request) - Store.deleteTournament(tournament) - tournament.dispatchEvent(TournamentDeleted, Json.Object("id" to tournament.id)) + getStore(request).deleteTournament(tournament) + tournament.dispatchEvent(TournamentDeleted, request, Json.Object("id" to tournament.id)) return Json.Object("success" to true) } } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/ext/OpenGotha.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/ext/OpenGotha.kt index 7227d14..e382cb1 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/ext/OpenGotha.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/ext/OpenGotha.kt @@ -7,6 +7,9 @@ import org.jeudego.pairgoth.model.* import org.jeudego.pairgoth.opengotha.TournamentType import org.jeudego.pairgoth.opengotha.ObjectFactory import org.jeudego.pairgoth.store.Store +import org.jeudego.pairgoth.store.nextGameId +import org.jeudego.pairgoth.store.nextPlayerId +import org.jeudego.pairgoth.store.nextTournamentId import org.w3c.dom.Element import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -118,7 +121,7 @@ object OpenGotha { ) val tournament = StandardTournament( - id = Store.nextTournamentId, + id = nextTournamentId, type = Tournament.Type.INDIVIDUAL, // CB for now, TODO name = genParams.name, shortName = genParams.shortName, @@ -153,7 +156,7 @@ object OpenGotha { // import players ogTournament.players.player.map { player -> Player( - id = Store.nextPlayerId, + id = nextPlayerId, name = player.name, firstname = player.firstName, rating = player.rating, @@ -174,7 +177,7 @@ object OpenGotha { }.entries.sortedBy { it.key }.map { it.value.map { game -> Game( - id = Store.nextGameId, + id = nextGameId, table = game.tableNumber, black = canonicMap[game.blackPlayer] ?: throw Error("player not found: ${game.blackPlayer}"), white = canonicMap[game.whitePlayer] ?: throw Error("player not found: ${game.whitePlayer}"), diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairable.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairable.kt index 85c9442..3c8994d 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairable.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Pairable.kt @@ -3,6 +3,7 @@ package org.jeudego.pairgoth.model import com.republicate.kson.Json import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest import org.jeudego.pairgoth.store.Store +import org.jeudego.pairgoth.store.nextPlayerId import java.util.* // Pairable @@ -101,7 +102,7 @@ class Player( } fun Player.Companion.fromJson(json: Json.Object, default: Player? = null) = Player( - id = json.getInt("id") ?: default?.id ?: Store.nextPlayerId, + id = json.getInt("id") ?: default?.id ?: nextPlayerId, name = json.getString("name") ?: default?.name ?: badRequest("missing name"), firstname = json.getString("firstname") ?: default?.firstname ?: badRequest("missing firstname"), rating = json.getInt("rating") ?: default?.rating ?: badRequest("missing rating"), diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt index 15a73e3..d5ae7a6 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt @@ -9,6 +9,8 @@ import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest import org.jeudego.pairgoth.pairing.solver.MacMahonSolver import org.jeudego.pairgoth.pairing.solver.SwissSolver import org.jeudego.pairgoth.store.Store +import org.jeudego.pairgoth.store.nextPlayerId +import org.jeudego.pairgoth.store.nextTournamentId import kotlin.math.max import java.util.* import kotlin.math.roundToInt @@ -178,7 +180,7 @@ class TeamTournament( } fun teamFromJson(json: Json.Object, default: TeamTournament.Team? = null) = Team( - id = json.getInt("id") ?: default?.id ?: Store.nextPlayerId, + id = json.getInt("id") ?: default?.id ?: nextPlayerId, name = json.getString("name") ?: default?.name ?: badRequest("missing name"), final = json.getBoolean("final") ?: default?.final ?: badRequest("missing final") ).apply { @@ -199,7 +201,7 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n // No clean way to avoid this redundancy val tournament = if (type.playersNumber == 1) StandardTournament( - id = json.getInt("id") ?: default?.id ?: Store.nextTournamentId, + id = json.getInt("id") ?: default?.id ?: nextTournamentId, type = type, name = json.getString("name") ?: default?.name ?: badRequest("missing name"), shortName = json.getString("shortName") ?: default?.shortName ?: badRequest("missing shortName"), @@ -217,7 +219,7 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n ) else TeamTournament( - id = json.getInt("id") ?: default?.id ?: Store.nextTournamentId, + id = json.getInt("id") ?: default?.id ?: nextTournamentId, type = type, name = json.getString("name") ?: default?.name ?: badRequest("missing name"), shortName = json.getString("shortName") ?: default?.shortName ?: badRequest("missing shortName"), diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/solver/BaseSolver.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/solver/BaseSolver.kt index 0823694..ae6a4f7 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/solver/BaseSolver.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/pairing/solver/BaseSolver.kt @@ -6,6 +6,7 @@ import org.jeudego.pairgoth.pairing.BasePairingHelper import org.jeudego.pairgoth.pairing.detRandom import org.jeudego.pairgoth.pairing.nonDetRandom import org.jeudego.pairgoth.store.Store +import org.jeudego.pairgoth.store.nextGameId import org.jgrapht.alg.matching.blossom.v5.KolmogorovWeightedPerfectMatching import org.jgrapht.alg.matching.blossom.v5.ObjectiveSense import org.jgrapht.graph.DefaultWeightedEdge @@ -118,7 +119,7 @@ sealed class BaseSolver( var result = sorted.flatMap { games(white = it[0], black = it[1]) } // add game for ByePlayer - if (chosenByePlayer != ByePlayer) result += Game(id = Store.nextGameId, table = 0, white = ByePlayer.id, black = chosenByePlayer.id, result = Game.Result.fromSymbol('b')) + if (chosenByePlayer != ByePlayer) result += Game(id = nextGameId, table = 0, white = ByePlayer.id, black = chosenByePlayer.id, result = Game.Result.fromSymbol('b')) val DEBUG_EXPORT_WEIGHT = false if (DEBUG_EXPORT_WEIGHT) { @@ -554,6 +555,6 @@ sealed class BaseSolver( // CB TODO team of individuals pairing val table = if (black.id == 0 || white.id == 0) 0 else usedTables.nextClearBit(1) usedTables.set(table) - return listOf(Game(id = Store.nextGameId, table = table, black = black.id, white = white.id, handicap = hd(white = white, black = black), drawnUpDown = dudd(black, white))) + return listOf(Game(id = nextGameId, table = table, black = black.id, white = white.id, handicap = hd(white = white, black = black), drawnUpDown = dudd(black, white))) } } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/FileStore.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/FileStore.kt index 07c91e8..c6dc53e 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/FileStore.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/FileStore.kt @@ -10,23 +10,40 @@ import org.jeudego.pairgoth.model.fromJson import org.jeudego.pairgoth.model.getID import org.jeudego.pairgoth.model.toFullJson import org.jeudego.pairgoth.model.toID +import org.jeudego.pairgoth.server.WebappManager import java.lang.Integer.max import java.nio.file.Path +import java.nio.file.PathMatcher import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* +import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.readText import kotlin.io.path.useDirectoryEntries +import kotlin.io.path.walk private const val LEFT_PAD = 6 // left padding of IDs with '0' in filename private fun Tournament<*>.filename() = "${id.toString().padStart(LEFT_PAD, '0')}-${shortName}.tour" -class FileStore(pathStr: String): IStore { +class FileStore(pathStr: String): Store { companion object { private val filenameRegex = Regex("^(\\d+)-(.*)\\.tour$") private val displayFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") private val timestampFormat: DateFormat = SimpleDateFormat("yyyyMMddHHmmss") private val timestamp: String get() = timestampFormat.format(Date()) + + @OptIn(ExperimentalPathApi::class) + private fun getMaxID(): ID { + val rootPath = Path.of(WebappManager.properties.getProperty("store.file.path") ?: ".") + val globMatcher = PathMatcher { path -> path.fileName.toString().endsWith(".tour") } + return rootPath.walk().filter { path -> globMatcher.matches(path) }.mapNotNull { path -> + val match = filenameRegex.matchEntire(path.fileName.toString()) + match?.let { it.groupValues[1].toID() } + }.maxOrNull() ?: 0 + } + init { + _nextTournamentId.set(getMaxID()) + } } private val path = Path.of(pathStr).also { @@ -34,13 +51,10 @@ class FileStore(pathStr: String): IStore { if (!file.mkdirs() && !file.isDirectory) throw Error("Property pairgoth.store.file.path must be a directory") } - init { - _nextTournamentId.set(getTournaments().keys.maxOrNull() ?: 0.toID()) - } - private fun lastModified(path: Path) = displayFormat.format(Date(path.toFile().lastModified())) + override fun getTournaments(): Map> { return path.useDirectoryEntries("*.tour") { entries -> entries.mapNotNull { entry -> diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/IStore.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/IStore.kt deleted file mode 100644 index 4a2b8ad..0000000 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/IStore.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.jeudego.pairgoth.store - -import org.jeudego.pairgoth.model.ID -import org.jeudego.pairgoth.model.Tournament -import java.util.concurrent.atomic.AtomicInteger - -internal val _nextTournamentId = AtomicInteger() -internal val _nextPlayerId = AtomicInteger() -internal val _nextGameId = AtomicInteger() - -interface IStore { - - val nextTournamentId get() = _nextTournamentId.incrementAndGet() - val nextPlayerId get() = _nextPlayerId.incrementAndGet() - val nextGameId get() = _nextGameId.incrementAndGet() - - fun getTournaments(): Map> - fun addTournament(tournament: Tournament<*>) - fun getTournament(id: ID): Tournament<*>? - fun replaceTournament(tournament: Tournament<*>) - fun deleteTournament(tournament: Tournament<*>) -} diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/MemoryStore.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/MemoryStore.kt index c9a016c..4ffb968 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/MemoryStore.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/MemoryStore.kt @@ -3,7 +3,7 @@ package org.jeudego.pairgoth.store import org.jeudego.pairgoth.model.ID import org.jeudego.pairgoth.model.Tournament -class MemoryStore: IStore { +class MemoryStore: Store { private val tournaments = mutableMapOf>() override fun getTournaments(): Map> = tournaments.mapValues { diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/Store.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/Store.kt index 34f8fc5..18bf547 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/Store.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/store/Store.kt @@ -1,16 +1,51 @@ package org.jeudego.pairgoth.store +import com.republicate.kson.Json +import org.jeudego.pairgoth.model.ID +import org.jeudego.pairgoth.model.Tournament +import org.jeudego.pairgoth.server.ApiServlet.Companion.USER_KEY import org.jeudego.pairgoth.server.WebappManager +import java.nio.file.Path +import java.util.concurrent.atomic.AtomicInteger +import javax.servlet.http.HttpServletRequest -private fun createStoreImplementation(): IStore { - return when (val storeProperty = WebappManager.properties.getProperty("store") ?: "memory") { - "memory" -> MemoryStore() - "file" -> { - val filePath = WebappManager.properties.getProperty("store.file.path") ?: "." - FileStore(filePath) - } - else -> throw Error("unknown store: $storeProperty") - } +internal val _nextTournamentId = AtomicInteger() +internal val _nextPlayerId = AtomicInteger() +internal val _nextGameId = AtomicInteger() + +val nextTournamentId get() = _nextTournamentId.incrementAndGet() +val nextPlayerId get() = _nextPlayerId.incrementAndGet() +val nextGameId get() = _nextGameId.incrementAndGet() + +interface Store { + fun getTournaments(): Map> + fun addTournament(tournament: Tournament<*>) + fun getTournament(id: ID): Tournament<*>? + fun replaceTournament(tournament: Tournament<*>) + fun deleteTournament(tournament: Tournament<*>) } -object Store: IStore by createStoreImplementation() +fun getStore(request: HttpServletRequest): Store { + val storeType = WebappManager.getMandatoryProperty("store") + return when (val auth = WebappManager.getMandatoryProperty("auth")) { + "none", "sesame" -> + when (storeType) { + "memory" -> MemoryStore() + "file" -> { + val filePath = WebappManager.properties.getProperty("store.file.path") ?: "." + FileStore(filePath) + } + else -> throw Error("invalid store type: $storeType") + } + "oauth" -> { + if (storeType == "memory") throw Error("invalid store type for oauth: $storeType") + var rootPath = WebappManager.properties.getProperty("store.file.path") ?: "." + (request.getAttribute(USER_KEY) as Json.Object?)?.getString("email")?.also { email -> + rootPath = "$rootPath/$email" + Path.of(rootPath).toFile().mkdirs() + } + FileStore(rootPath) + } + else -> throw Error("invalid auth: $auth") + } +}