Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bace029a8a | |||
| 7664042193 |
@@ -115,3 +115,25 @@
|
|||||||
* **tournament:** use HS256 director token for native tournament-server calls ([b98bdd2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b98bdd2a64eb6c8279bd3cfe15d70628025ef0e5))
|
* **tournament:** use HS256 director token for native tournament-server calls ([b98bdd2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b98bdd2a64eb6c8279bd3cfe15d70628025ef0e5))
|
||||||
* **tournament:** use Optional[String] for selfUrl ConfigProperty to avoid startup failure ([28cbc2e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/28cbc2e18447aa8a04a5868889a49b555075d0c6))
|
* **tournament:** use Optional[String] for selfUrl ConfigProperty to avoid startup failure ([28cbc2e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/28cbc2e18447aa8a04a5868889a49b555075d0c6))
|
||||||
* wrap server list response in ExternalTournamentServerList ([f2d79e4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f2d79e4952aea6bde762c294eb202474b7827054))
|
* wrap server list response in ExternalTournamentServerList ([f2d79e4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f2d79e4952aea6bde762c294eb202474b7827054))
|
||||||
|
## (2026-06-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* NCS-121 pipeline for tournament ([#68](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/68)) ([145f467](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/145f4676483f92bfe6f2d9ca40e2cb4200982e87))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **reflection:** add GameWritebackEventDto to native reflection configuration ([1aee39c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1aee39c1ad286984501ac4b47da2b72d60b58a6f))
|
||||||
|
* **reflection:** add native reflection configuration for tournament classes ([65bc6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/65bc6a759937543df2d29905688bfa9e68d0c9d4))
|
||||||
|
* **tournament:** auto-join external tournaments and publish created ones ([#77](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/77)) ([9978b7e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9978b7ea78eb658a225a461b9cd339386c0c14f3))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** remove dynamic server add/remove endpoints ([6d06edd](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6d06edda69a50de65cd9efa27f26a4cc6b437f9d))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **tournament:** mirror bot join onto native twin ([7664042](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/76640421930c26a9260da002c90e2966b97a57a4))
|
||||||
|
* **tournament:** replace scala.util.Random singleton with UUID for native image ([a50884a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a50884a11b1de500e74c18fd08d2d102d53cc3e9))
|
||||||
|
* **tournament:** sync native-server participants and route start ([#78](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/78)) ([1f4e9c8](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1f4e9c8498f55d95ab48758df60c7618445bf6ca))
|
||||||
|
* **tournament:** use HS256 director token for native tournament-server calls ([b98bdd2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b98bdd2a64eb6c8279bd3cfe15d70628025ef0e5))
|
||||||
|
* **tournament:** use Optional[String] for selfUrl ConfigProperty to avoid startup failure ([28cbc2e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/28cbc2e18447aa8a04a5868889a49b555075d0c6))
|
||||||
|
* wrap server list response in ExternalTournamentServerList ([f2d79e4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f2d79e4952aea6bde762c294eb202474b7827054))
|
||||||
|
|||||||
+18
-1
@@ -119,6 +119,16 @@ class TournamentResource:
|
|||||||
case Some(nativeId) => tournamentService.setNativeTournamentId(t.id, nativeId)
|
case Some(nativeId) => tournamentService.setNativeTournamentId(t.id, nativeId)
|
||||||
case None => log.warnf("Failed to publish tournament %s to native server %s", t.id, nativeServerUrl)
|
case None => log.warnf("Failed to publish tournament %s to native server %s", t.id, nativeServerUrl)
|
||||||
|
|
||||||
|
// Mirror a bot join onto the native twin so it surfaces in the UI, which reads participant and
|
||||||
|
// standings fields from the native server (see nativeOverlay).
|
||||||
|
private def joinNativeTwin(id: String, botName: String): Unit =
|
||||||
|
if nativeServerUrl.nonEmpty then
|
||||||
|
tournamentService.get(id).flatMap(nativeIdFor).foreach { nativeId =>
|
||||||
|
if externalClient.joinNativeAsBot(nativeServerUrl, nativeId, botName) then
|
||||||
|
log.infof("Joined bot %s on native twin %s of tournament %s", botName, nativeId, id)
|
||||||
|
else log.warnf("Failed to join bot %s on native twin %s of tournament %s", botName, nativeId, id)
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve the native-server twin of a local tournament. Backfills the stored id by matching
|
// Resolve the native-server twin of a local tournament. Backfills the stored id by matching
|
||||||
// fullName against the native list for tournaments created before the id was captured.
|
// fullName against the native list for tournaments created before the id was captured.
|
||||||
private def nativeIdFor(t: de.nowchess.tournament.domain.Tournament): Option[String] =
|
private def nativeIdFor(t: de.nowchess.tournament.domain.Tournament): Option[String] =
|
||||||
@@ -250,7 +260,14 @@ class TournamentResource:
|
|||||||
val botId = Option(jwt.getSubject).getOrElse("")
|
val botId = Option(jwt.getSubject).getOrElse("")
|
||||||
val botName = Option(jwt.getClaim[AnyRef]("name")).map(_.toString).getOrElse(botId)
|
val botName = Option(jwt.getClaim[AnyRef]("name")).map(_.toString).getOrElse(botId)
|
||||||
tournamentService.join(id, botId, botName) match
|
tournamentService.join(id, botId, botName) match
|
||||||
case Right(_) => Response.ok(OkDto()).build()
|
case Right(_) =>
|
||||||
|
joinNativeTwin(id, botName)
|
||||||
|
Response.ok(OkDto()).build()
|
||||||
|
// Already in the NowChess participant list but possibly never mirrored onto the native
|
||||||
|
// twin (where the UI reads participants from) — make the native join idempotent.
|
||||||
|
case Left(TournamentError.AlreadyJoined) =>
|
||||||
|
joinNativeTwin(id, botName)
|
||||||
|
Response.ok(OkDto()).build()
|
||||||
case Left(error) =>
|
case Left(error) =>
|
||||||
error match
|
error match
|
||||||
case TournamentError.NotFound(_) =>
|
case TournamentError.NotFound(_) =>
|
||||||
|
|||||||
+22
-1
@@ -60,9 +60,12 @@ class ExternalTournamentClient:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def registerDirector(serverUrl: String, name: String): Option[String] =
|
private def registerDirector(serverUrl: String, name: String): Option[String] =
|
||||||
|
registerAccount(serverUrl, name, isBot = false)
|
||||||
|
|
||||||
|
private def registerAccount(serverUrl: String, name: String, isBot: Boolean): Option[String] =
|
||||||
Try {
|
Try {
|
||||||
val client = buildClient()
|
val client = buildClient()
|
||||||
val body = s"""{"name":"${name.replace("\"", "\\\"")}","isBot":false}"""
|
val body = s"""{"name":"${name.replace("\"", "\\\"")}","isBot":$isBot}"""
|
||||||
val response = client
|
val response = client
|
||||||
.target(s"$serverUrl/api/auth/register")
|
.target(s"$serverUrl/api/auth/register")
|
||||||
.request(MediaType.APPLICATION_JSON)
|
.request(MediaType.APPLICATION_JSON)
|
||||||
@@ -76,6 +79,24 @@ class ExternalTournamentClient:
|
|||||||
client.close()
|
client.close()
|
||||||
}.getOrElse(None)
|
}.getOrElse(None)
|
||||||
|
|
||||||
|
// The tournament server holds only the director token, which cannot join as a bot. Register the
|
||||||
|
// bot on the native server by name to mint a bot token, then join the native twin as that bot.
|
||||||
|
def joinNativeAsBot(serverUrl: String, tournamentId: String, botName: String): Boolean =
|
||||||
|
registerAccount(serverUrl, botName, isBot = true).exists { token =>
|
||||||
|
Try {
|
||||||
|
val client = buildClient()
|
||||||
|
val response = client
|
||||||
|
.target(s"$serverUrl/api/tournament/$tournamentId/join")
|
||||||
|
.request(MediaType.APPLICATION_JSON)
|
||||||
|
.header("Authorization", s"Bearer $token")
|
||||||
|
.post(Entity.json(""))
|
||||||
|
try response.getStatus / 100 == 2 || response.getStatus == 409
|
||||||
|
finally
|
||||||
|
response.close()
|
||||||
|
client.close()
|
||||||
|
}.getOrElse(false)
|
||||||
|
}
|
||||||
|
|
||||||
private def createNative(serverUrl: String, token: String, form: String): Option[String] =
|
private def createNative(serverUrl: String, token: String, form: String): Option[String] =
|
||||||
Try {
|
Try {
|
||||||
val client = buildClient()
|
val client = buildClient()
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
MAJOR=0
|
MAJOR=0
|
||||||
MINOR=8
|
MINOR=9
|
||||||
PATCH=0
|
PATCH=0
|
||||||
|
|||||||
Reference in New Issue
Block a user