feat: NCS-53 changed IO to MicroService for easier scaling
This commit is contained in:
Generated
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
<option name="deprecationWarnings" value="true" />
|
<option name="deprecationWarnings" value="true" />
|
||||||
<option name="uncheckedWarnings" value="true" />
|
<option name="uncheckedWarnings" value="true" />
|
||||||
</profile>
|
</profile>
|
||||||
<profile name="Gradle 2" modules="NowChessSystems.modules.bot.main,NowChessSystems.modules.bot.scoverage,NowChessSystems.modules.bot.test,NowChessSystems.modules.core.integrationTest,NowChessSystems.modules.core.main,NowChessSystems.modules.core.native-test,NowChessSystems.modules.core.quarkus-generated-sources,NowChessSystems.modules.core.quarkus-test-generated-sources,NowChessSystems.modules.core.scoverage,NowChessSystems.modules.core.test,NowChessSystems.modules.io.main,NowChessSystems.modules.io.scoverage,NowChessSystems.modules.io.test,NowChessSystems.modules.rule.main,NowChessSystems.modules.rule.scoverage,NowChessSystems.modules.rule.test,NowChessSystems.modules.ui.main,NowChessSystems.modules.ui.scoverage,NowChessSystems.modules.ui.test">
|
<profile name="Gradle 2" modules="NowChessSystems.modules.bot.main,NowChessSystems.modules.bot.scoverage,NowChessSystems.modules.bot.test,NowChessSystems.modules.core.integrationTest,NowChessSystems.modules.core.main,NowChessSystems.modules.core.native-test,NowChessSystems.modules.core.quarkus-generated-sources,NowChessSystems.modules.core.quarkus-test-generated-sources,NowChessSystems.modules.core.scoverage,NowChessSystems.modules.core.test,NowChessSystems.modules.io.integrationTest,NowChessSystems.modules.io.main,NowChessSystems.modules.io.native-test,NowChessSystems.modules.io.quarkus-generated-sources,NowChessSystems.modules.io.quarkus-test-generated-sources,NowChessSystems.modules.io.scoverage,NowChessSystems.modules.io.test,NowChessSystems.modules.rule.main,NowChessSystems.modules.rule.scoverage,NowChessSystems.modules.rule.test,NowChessSystems.modules.ui.main,NowChessSystems.modules.ui.scoverage,NowChessSystems.modules.ui.test">
|
||||||
<option name="deprecationWarnings" value="true" />
|
<option name="deprecationWarnings" value="true" />
|
||||||
<option name="uncheckedWarnings" value="true" />
|
<option name="uncheckedWarnings" value="true" />
|
||||||
<parameters>
|
<parameters>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
vars {
|
vars {
|
||||||
baseUrl: http://localhost:8080
|
baseUrl: http://localhost:8080
|
||||||
|
ioBaseUrl: http://localhost:8081
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
meta {
|
||||||
|
name: Export FEN
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{ioBaseUrl}}/io/export/fen
|
||||||
|
body: json
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Content-Type: application/json
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"a1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"b1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"d1": {"color": "White", "pieceType": "Queen"},
|
||||||
|
"e1": {"color": "White", "pieceType": "King"},
|
||||||
|
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"g1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"h1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||||
|
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||||
|
"e8": {"color": "Black", "pieceType": "King"},
|
||||||
|
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||||
|
},
|
||||||
|
"turn": "White",
|
||||||
|
"castlingRights": {
|
||||||
|
"whiteKingSide": true,
|
||||||
|
"whiteQueenSide": true,
|
||||||
|
"blackKingSide": true,
|
||||||
|
"blackQueenSide": true
|
||||||
|
},
|
||||||
|
"enPassantSquare": null,
|
||||||
|
"halfMoveClock": 0,
|
||||||
|
"moves": [],
|
||||||
|
"result": null,
|
||||||
|
"initialBoard": {
|
||||||
|
"a1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"b1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"d1": {"color": "White", "pieceType": "Queen"},
|
||||||
|
"e1": {"color": "White", "pieceType": "King"},
|
||||||
|
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"g1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"h1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||||
|
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||||
|
"e8": {"color": "Black", "pieceType": "King"},
|
||||||
|
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
meta {
|
||||||
|
name: Export PGN
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{ioBaseUrl}}/io/export/pgn
|
||||||
|
body: json
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Content-Type: application/json
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"a1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"b1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"d1": {"color": "White", "pieceType": "Queen"},
|
||||||
|
"e1": {"color": "White", "pieceType": "King"},
|
||||||
|
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"g1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"h1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||||
|
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||||
|
"e8": {"color": "Black", "pieceType": "King"},
|
||||||
|
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||||
|
},
|
||||||
|
"turn": "White",
|
||||||
|
"castlingRights": {
|
||||||
|
"whiteKingSide": true,
|
||||||
|
"whiteQueenSide": true,
|
||||||
|
"blackKingSide": true,
|
||||||
|
"blackQueenSide": true
|
||||||
|
},
|
||||||
|
"enPassantSquare": null,
|
||||||
|
"halfMoveClock": 0,
|
||||||
|
"moves": [],
|
||||||
|
"result": null,
|
||||||
|
"initialBoard": {
|
||||||
|
"a1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"b1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"d1": {"color": "White", "pieceType": "Queen"},
|
||||||
|
"e1": {"color": "White", "pieceType": "King"},
|
||||||
|
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"g1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"h1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||||
|
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||||
|
"e8": {"color": "Black", "pieceType": "King"},
|
||||||
|
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
meta {
|
||||||
|
name: export
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
meta {
|
||||||
|
name: Import FEN
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{ioBaseUrl}}/io/import/fen
|
||||||
|
body: json
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Content-Type: application/json
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
meta {
|
||||||
|
name: Import PGN
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{ioBaseUrl}}/io/import/pgn
|
||||||
|
body: json
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Content-Type: application/json
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"pgn": "1. e4 e5 2. Nf3 Nc6 *"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
meta {
|
||||||
|
name: import
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
@@ -49,10 +49,10 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
implementation(project(":modules:api"))
|
implementation(project(":modules:api"))
|
||||||
implementation(project(":modules:io"))
|
|
||||||
implementation(project(":modules:rule"))
|
implementation(project(":modules:rule"))
|
||||||
implementation("com.microsoft.onnxruntime:onnxruntime:${versions["ONNXRUNTIME"]!!}")
|
implementation("com.microsoft.onnxruntime:onnxruntime:${versions["ONNXRUNTIME"]!!}")
|
||||||
|
|
||||||
|
testImplementation(project(":modules:io"))
|
||||||
testImplementation(platform("org.junit:junit-bom:${versions["JUNIT_BOM"]!!}"))
|
testImplementation(platform("org.junit:junit-bom:${versions["JUNIT_BOM"]!!}"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}")
|
testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}")
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
greeting:
|
quarkus:
|
||||||
message: "hello"
|
http:
|
||||||
|
port: 8080
|
||||||
|
application:
|
||||||
|
name: nowchess-core
|
||||||
|
rest-client:
|
||||||
|
io-service:
|
||||||
|
url: http://localhost:8081
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package de.nowchess.chess.client
|
||||||
|
|
||||||
|
import de.nowchess.api.game.GameContext
|
||||||
|
import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest}
|
||||||
|
import jakarta.ws.rs.*
|
||||||
|
import jakarta.ws.rs.core.MediaType
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient
|
||||||
|
|
||||||
|
@Path("/io")
|
||||||
|
@RegisterRestClient(configKey = "io-service")
|
||||||
|
trait IoServiceClient:
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/import/fen")
|
||||||
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def importFen(body: ImportFenRequest): GameContext
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/import/pgn")
|
||||||
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def importPgn(body: ImportPgnRequest): GameContext
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/export/fen")
|
||||||
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Produces(Array(MediaType.TEXT_PLAIN))
|
||||||
|
def exportFen(ctx: GameContext): String
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/export/pgn")
|
||||||
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Produces(Array("application/x-chess-pgn"))
|
||||||
|
def exportPgn(ctx: GameContext): String
|
||||||
@@ -2,7 +2,10 @@ package de.nowchess.chess.config
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.Version
|
import com.fasterxml.jackson.core.Version
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||||
|
import de.nowchess.api.board.Square
|
||||||
|
import de.nowchess.io.json.{SquareKeyDeserializer, SquareKeySerializer}
|
||||||
import io.quarkus.jackson.ObjectMapperCustomizer
|
import io.quarkus.jackson.ObjectMapperCustomizer
|
||||||
import jakarta.inject.Singleton
|
import jakarta.inject.Singleton
|
||||||
|
|
||||||
@@ -15,3 +18,7 @@ class JacksonConfig extends ObjectMapperCustomizer:
|
|||||||
new Version(2, 21, 1, null, "com.fasterxml.jackson.module", "jackson-module-scala")
|
new Version(2, 21, 1, null, "com.fasterxml.jackson.module", "jackson-module-scala")
|
||||||
// scalafix:on DisableSyntax.null
|
// scalafix:on DisableSyntax.null
|
||||||
})
|
})
|
||||||
|
val squareModule = new SimpleModule()
|
||||||
|
squareModule.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
|
||||||
|
squareModule.addKeySerializer(classOf[Square], new SquareKeySerializer())
|
||||||
|
mapper.registerModule(squareModule)
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package de.nowchess.chess.config
|
package de.nowchess.chess.config
|
||||||
|
|
||||||
|
import de.nowchess.api.board.{CastlingRights, Color, File, Piece, PieceType, Rank, Square}
|
||||||
import de.nowchess.api.dto.*
|
import de.nowchess.api.dto.*
|
||||||
|
import de.nowchess.api.game.{DrawReason, GameContext, GameResult}
|
||||||
|
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection
|
import io.quarkus.runtime.annotations.RegisterForReflection
|
||||||
|
|
||||||
@RegisterForReflection(
|
@RegisterForReflection(
|
||||||
@@ -18,6 +21,21 @@ import io.quarkus.runtime.annotations.RegisterForReflection
|
|||||||
classOf[LegalMovesResponseDto],
|
classOf[LegalMovesResponseDto],
|
||||||
classOf[OkResponseDto],
|
classOf[OkResponseDto],
|
||||||
classOf[PlayerInfoDto],
|
classOf[PlayerInfoDto],
|
||||||
|
|
||||||
|
classOf[GameContext],
|
||||||
|
classOf[Color],
|
||||||
|
classOf[Piece],
|
||||||
|
classOf[PieceType],
|
||||||
|
classOf[CastlingRights],
|
||||||
|
classOf[Square],
|
||||||
|
classOf[File],
|
||||||
|
classOf[Rank],
|
||||||
|
classOf[Move],
|
||||||
|
classOf[MoveType],
|
||||||
|
classOf[PromotionPiece],
|
||||||
|
classOf[GameResult],
|
||||||
|
classOf[DrawReason],
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
class NativeReflectionConfig
|
class NativeReflectionConfig
|
||||||
|
|||||||
@@ -6,18 +6,21 @@ import de.nowchess.api.dto.*
|
|||||||
import de.nowchess.api.game.{DrawReason, GameContext, GameResult}
|
import de.nowchess.api.game.{DrawReason, GameContext, GameResult}
|
||||||
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
||||||
import de.nowchess.api.player.{PlayerId, PlayerInfo}
|
import de.nowchess.api.player.{PlayerId, PlayerInfo}
|
||||||
|
import de.nowchess.chess.client.IoServiceClient
|
||||||
import de.nowchess.chess.controller.Parser
|
import de.nowchess.chess.controller.Parser
|
||||||
import de.nowchess.chess.engine.GameEngine
|
import de.nowchess.chess.engine.GameEngine
|
||||||
import de.nowchess.chess.exception.{BadRequestException, GameNotFoundException}
|
import de.nowchess.chess.exception.{BadRequestException, GameNotFoundException}
|
||||||
import de.nowchess.chess.observer.*
|
import de.nowchess.chess.observer.*
|
||||||
import de.nowchess.chess.registry.{GameEntry, GameRegistry}
|
import de.nowchess.chess.registry.{GameEntry, GameRegistry}
|
||||||
import de.nowchess.io.fen.{FenExporter, FenParser}
|
import de.nowchess.io.fen.FenExporter
|
||||||
import de.nowchess.io.pgn.{PgnExporter, PgnParser}
|
import de.nowchess.io.pgn.PgnExporter
|
||||||
|
import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest}
|
||||||
import io.smallrye.mutiny.Multi
|
import io.smallrye.mutiny.Multi
|
||||||
import jakarta.enterprise.context.ApplicationScoped
|
import jakarta.enterprise.context.ApplicationScoped
|
||||||
import jakarta.inject.Inject
|
import jakarta.inject.Inject
|
||||||
import jakarta.ws.rs.*
|
import jakarta.ws.rs.*
|
||||||
import jakarta.ws.rs.core.{MediaType, Response}
|
import jakarta.ws.rs.core.{MediaType, Response}
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RestClient
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import scala.compiletime.uninitialized
|
import scala.compiletime.uninitialized
|
||||||
@@ -32,6 +35,10 @@ class GameResource:
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
var objectMapper: ObjectMapper = uninitialized
|
var objectMapper: ObjectMapper = uninitialized
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@RestClient
|
||||||
|
var ioClient: IoServiceClient = uninitialized
|
||||||
// scalafix:on DisableSyntax.var
|
// scalafix:on DisableSyntax.var
|
||||||
|
|
||||||
private val DefaultWhite = PlayerInfo(PlayerId("p1"), "Player 1")
|
private val DefaultWhite = PlayerInfo(PlayerId("p1"), "Player 1")
|
||||||
@@ -142,7 +149,6 @@ class GameResource:
|
|||||||
val black = playerInfoFrom(req.black, DefaultBlack)
|
val black = playerInfoFrom(req.black, DefaultBlack)
|
||||||
val entry = newEntry(GameContext.initial, white, black)
|
val entry = newEntry(GameContext.initial, white, black)
|
||||||
registry.store(entry)
|
registry.store(entry)
|
||||||
println(s"Created game ${entry.gameId}")
|
|
||||||
created(toGameFullDto(entry))
|
created(toGameFullDto(entry))
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@@ -264,9 +270,7 @@ class GameResource:
|
|||||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
def importFen(body: ImportFenRequestDto): Response =
|
def importFen(body: ImportFenRequestDto): Response =
|
||||||
val ctx = FenParser.parseFen(body.fen) match
|
val ctx = ioClient.importFen(ImportFenRequest(body.fen))
|
||||||
case Left(err) => throw BadRequestException("INVALID_FEN", err, Some("fen"))
|
|
||||||
case Right(ctx) => ctx
|
|
||||||
val white = playerInfoFrom(body.white, DefaultWhite)
|
val white = playerInfoFrom(body.white, DefaultWhite)
|
||||||
val black = playerInfoFrom(body.black, DefaultBlack)
|
val black = playerInfoFrom(body.black, DefaultBlack)
|
||||||
val entry = newEntry(ctx, white, black)
|
val entry = newEntry(ctx, white, black)
|
||||||
@@ -278,11 +282,8 @@ class GameResource:
|
|||||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
def importPgn(body: ImportPgnRequestDto): Response =
|
def importPgn(body: ImportPgnRequestDto): Response =
|
||||||
val engine = GameEngine()
|
val ctx = ioClient.importPgn(ImportPgnRequest(body.pgn))
|
||||||
engine.loadGame(PgnParser, body.pgn) match
|
val entry = newEntry(ctx, DefaultWhite, DefaultBlack)
|
||||||
case Left(err) => throw BadRequestException("INVALID_PGN", err, Some("pgn"))
|
|
||||||
case Right(_) => ()
|
|
||||||
val entry = GameEntry(registry.generateId(), engine, DefaultWhite, DefaultBlack)
|
|
||||||
registry.store(entry)
|
registry.store(entry)
|
||||||
created(toGameFullDto(entry))
|
created(toGameFullDto(entry))
|
||||||
|
|
||||||
@@ -291,21 +292,12 @@ class GameResource:
|
|||||||
@Produces(Array(MediaType.TEXT_PLAIN))
|
@Produces(Array(MediaType.TEXT_PLAIN))
|
||||||
def exportFen(@PathParam("gameId") gameId: String): Response =
|
def exportFen(@PathParam("gameId") gameId: String): Response =
|
||||||
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
ok(FenExporter.exportGameContext(entry.engine.context))
|
ok(ioClient.exportFen(entry.engine.context))
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/{gameId}/export/pgn")
|
@Path("/{gameId}/export/pgn")
|
||||||
@Produces(Array("application/x-chess-pgn"))
|
@Produces(Array("application/x-chess-pgn"))
|
||||||
def exportPgn(@PathParam("gameId") gameId: String): Response =
|
def exportPgn(@PathParam("gameId") gameId: String): Response =
|
||||||
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
val pgn = PgnExporter.exportGame(
|
ok(ioClient.exportPgn(entry.engine.context))
|
||||||
Map(
|
|
||||||
"Event" -> "NowChess game",
|
|
||||||
"White" -> entry.white.displayName,
|
|
||||||
"Black" -> entry.black.displayName,
|
|
||||||
"Result" -> "*",
|
|
||||||
),
|
|
||||||
entry.engine.context.moves,
|
|
||||||
)
|
|
||||||
ok(pgn)
|
|
||||||
// scalafix:on DisableSyntax.throw
|
// scalafix:on DisableSyntax.throw
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("scala")
|
id("scala")
|
||||||
id("org.scoverage") version "8.1"
|
id("org.scoverage") version "8.1"
|
||||||
|
id("io.quarkus")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "de.nowchess"
|
group = "de.nowchess"
|
||||||
@@ -28,6 +29,10 @@ tasks.withType<ScalaCompile> {
|
|||||||
scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8")
|
scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val quarkusPlatformGroupId: String by project
|
||||||
|
val quarkusPlatformArtifactId: String by project
|
||||||
|
val quarkusPlatformVersion: String by project
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
compileOnly("org.scala-lang:scala3-compiler_3") {
|
compileOnly("org.scala-lang:scala3-compiler_3") {
|
||||||
@@ -53,19 +58,50 @@ dependencies {
|
|||||||
implementation("com.fasterxml.jackson.module:jackson-module-scala_3:${versions["JACKSON_SCALA"]!!}")
|
implementation("com.fasterxml.jackson.module:jackson-module-scala_3:${versions["JACKSON_SCALA"]!!}")
|
||||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${versions["JACKSON"]!!}")
|
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${versions["JACKSON"]!!}")
|
||||||
|
|
||||||
|
implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
|
||||||
|
implementation("io.quarkus:quarkus-rest")
|
||||||
|
implementation("io.quarkus:quarkus-rest-jackson")
|
||||||
|
implementation("io.quarkus:quarkus-arc")
|
||||||
|
implementation("io.quarkus:quarkus-config-yaml")
|
||||||
|
implementation("io.quarkus:quarkus-smallrye-health")
|
||||||
|
implementation("io.quarkus:quarkus-smallrye-openapi")
|
||||||
|
|
||||||
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}")
|
testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}")
|
||||||
testImplementation("co.helmethair:scalatest-junit-runner:${versions["SCALATEST_JUNIT"]!!}")
|
testImplementation("co.helmethair:scalatest-junit-runner:${versions["SCALATEST_JUNIT"]!!}")
|
||||||
|
testImplementation("io.quarkus:quarkus-junit5")
|
||||||
|
testImplementation("io.rest-assured:rest-assured")
|
||||||
|
|
||||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations.matching { !it.name.startsWith("scoverage") }.configureEach {
|
||||||
|
resolutionStrategy.force("org.scala-lang:scala-library:${versions["SCALA_LIBRARY"]!!}")
|
||||||
|
}
|
||||||
|
configurations.scoverage {
|
||||||
|
resolutionStrategy.eachDependency {
|
||||||
|
if (requested.group == "org.scoverage" && requested.name.startsWith("scalac-scoverage-plugin_")) {
|
||||||
|
useTarget("${requested.group}:scalac-scoverage-plugin_2.13.16:2.3.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<JavaCompile> {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
options.compilerArgs.add("-parameters")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<Jar>().configureEach {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
useJUnitPlatform {
|
useJUnitPlatform {
|
||||||
includeEngines("scalatest")
|
includeEngines("scalatest", "junit-jupiter")
|
||||||
testLogging {
|
testLogging {
|
||||||
events("skipped", "failed")
|
events("passed", "skipped", "failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finalizedBy(tasks.reportScoverage)
|
finalizedBy(tasks.reportScoverage)
|
||||||
@@ -73,3 +109,6 @@ tasks.test {
|
|||||||
tasks.reportScoverage {
|
tasks.reportScoverage {
|
||||||
dependsOn(tasks.test)
|
dependsOn(tasks.test)
|
||||||
}
|
}
|
||||||
|
tasks.jar {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
quarkus:
|
||||||
|
http:
|
||||||
|
port: 8081
|
||||||
|
application:
|
||||||
|
name: nowchess-io
|
||||||
|
smallrye-openapi:
|
||||||
|
info-title: NowChess IO Service
|
||||||
|
info-version: 1.0.0
|
||||||
|
info-description: Chess notation import and export — FEN and PGN
|
||||||
|
path: /openapi
|
||||||
|
swagger-ui:
|
||||||
|
always-include: true
|
||||||
|
path: /swagger-ui
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.nowchess.io.json
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.{DeserializationContext, KeyDeserializer}
|
||||||
|
import de.nowchess.api.board.Square
|
||||||
|
|
||||||
|
class SquareKeyDeserializer extends KeyDeserializer:
|
||||||
|
override def deserializeKey(key: String, ctx: DeserializationContext): AnyRef =
|
||||||
|
Square.fromAlgebraic(key).orNull
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package de.nowchess.io.json
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.databind.{JsonSerializer, SerializerProvider}
|
||||||
|
import de.nowchess.api.board.Square
|
||||||
|
|
||||||
|
class SquareKeySerializer extends JsonSerializer[Square]:
|
||||||
|
override def serialize(value: Square, gen: JsonGenerator, provider: SerializerProvider): Unit =
|
||||||
|
gen.writeFieldName(value.toString)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package de.nowchess.io.service.config
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.Version
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
|
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||||
|
import de.nowchess.api.board.Square
|
||||||
|
import de.nowchess.io.json.{SquareKeyDeserializer, SquareKeySerializer}
|
||||||
|
import io.quarkus.jackson.ObjectMapperCustomizer
|
||||||
|
import jakarta.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class JacksonConfig extends ObjectMapperCustomizer:
|
||||||
|
def customize(mapper: ObjectMapper): Unit =
|
||||||
|
mapper.registerModule(new DefaultScalaModule() {
|
||||||
|
override def version(): Version =
|
||||||
|
// scalafix:off DisableSyntax.null
|
||||||
|
new Version(2, 21, 1, null, "com.fasterxml.jackson.module", "jackson-module-scala")
|
||||||
|
// scalafix:on DisableSyntax.null
|
||||||
|
})
|
||||||
|
val squareModule = new SimpleModule()
|
||||||
|
squareModule.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
|
||||||
|
squareModule.addKeySerializer(classOf[Square], new SquareKeySerializer())
|
||||||
|
mapper.registerModule(squareModule)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package de.nowchess.io.service.config
|
||||||
|
|
||||||
|
import de.nowchess.api.board.{CastlingRights, Color, File, Piece, PieceType, Rank, Square}
|
||||||
|
import de.nowchess.api.game.{DrawReason, GameContext, GameResult}
|
||||||
|
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
||||||
|
import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest, IoErrorDto}
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection
|
||||||
|
|
||||||
|
@RegisterForReflection(
|
||||||
|
targets = Array(
|
||||||
|
classOf[ImportFenRequest],
|
||||||
|
classOf[ImportPgnRequest],
|
||||||
|
classOf[IoErrorDto],
|
||||||
|
classOf[GameContext],
|
||||||
|
classOf[GameResult],
|
||||||
|
classOf[DrawReason],
|
||||||
|
classOf[Color],
|
||||||
|
classOf[Piece],
|
||||||
|
classOf[PieceType],
|
||||||
|
classOf[CastlingRights],
|
||||||
|
classOf[Square],
|
||||||
|
classOf[File],
|
||||||
|
classOf[Rank],
|
||||||
|
classOf[Move],
|
||||||
|
classOf[MoveType],
|
||||||
|
classOf[PromotionPiece],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
class NativeReflectionConfig
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package de.nowchess.io.service.dto
|
||||||
|
|
||||||
|
case class ImportFenRequest(fen: String)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package de.nowchess.io.service.dto
|
||||||
|
|
||||||
|
case class ImportPgnRequest(pgn: String)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package de.nowchess.io.service.dto
|
||||||
|
|
||||||
|
case class IoErrorDto(code: String, message: String)
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package de.nowchess.io.service.resource
|
||||||
|
|
||||||
|
import de.nowchess.api.game.GameContext
|
||||||
|
import de.nowchess.io.fen.{FenExporter, FenParser}
|
||||||
|
import de.nowchess.io.pgn.{PgnExporter, PgnParser}
|
||||||
|
import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest, IoErrorDto}
|
||||||
|
import io.smallrye.mutiny.Uni
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped
|
||||||
|
import jakarta.ws.rs.*
|
||||||
|
import jakarta.ws.rs.core.{MediaType, Response}
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.Operation
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.media.{Content, Schema}
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.responses.{APIResponse, APIResponses}
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag
|
||||||
|
|
||||||
|
@Path("/io")
|
||||||
|
@ApplicationScoped
|
||||||
|
@Tag(name = "IO", description = "Chess notation import and export")
|
||||||
|
class IoResource:
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/import/fen")
|
||||||
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Operation(summary = "Import FEN", description = "Parse a FEN string into a GameContext")
|
||||||
|
@APIResponses(
|
||||||
|
Array(
|
||||||
|
new APIResponse(responseCode = "200", description = "Parsed GameContext"),
|
||||||
|
new APIResponse(responseCode = "400", description = "Invalid FEN"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def importFen(body: ImportFenRequest): Uni[Response] =
|
||||||
|
Uni.createFrom().item {
|
||||||
|
FenParser.parseFen(body.fen) match
|
||||||
|
case Left(err) =>
|
||||||
|
Response.status(400).entity(IoErrorDto("INVALID_FEN", err)).build()
|
||||||
|
case Right(ctx) =>
|
||||||
|
Response.ok(ctx).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/import/pgn")
|
||||||
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Operation(summary = "Import PGN", description = "Parse a PGN string into a GameContext")
|
||||||
|
@APIResponses(
|
||||||
|
Array(
|
||||||
|
new APIResponse(responseCode = "200", description = "Parsed GameContext"),
|
||||||
|
new APIResponse(responseCode = "400", description = "Invalid PGN"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def importPgn(body: ImportPgnRequest): Uni[Response] =
|
||||||
|
Uni.createFrom().item {
|
||||||
|
PgnParser.importGameContext(body.pgn) match
|
||||||
|
case Left(err) =>
|
||||||
|
Response.status(400).entity(IoErrorDto("INVALID_PGN", err)).build()
|
||||||
|
case Right(ctx) =>
|
||||||
|
Response.ok(ctx).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/export/fen")
|
||||||
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Produces(Array(MediaType.TEXT_PLAIN))
|
||||||
|
@Operation(summary = "Export FEN", description = "Serialize a GameContext to FEN notation")
|
||||||
|
@APIResponse(responseCode = "200", description = "FEN string")
|
||||||
|
def exportFen(ctx: GameContext): Uni[Response] =
|
||||||
|
Uni.createFrom().item(Response.ok(FenExporter.exportGameContext(ctx)).build())
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/export/pgn")
|
||||||
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Produces(Array("application/x-chess-pgn"))
|
||||||
|
@Operation(summary = "Export PGN", description = "Serialize a GameContext to PGN notation")
|
||||||
|
@APIResponse(responseCode = "200", description = "PGN text")
|
||||||
|
def exportPgn(ctx: GameContext): Uni[Response] =
|
||||||
|
Uni.createFrom().item(Response.ok(PgnExporter.exportGameContext(ctx)).build())
|
||||||
Reference in New Issue
Block a user