From 13d101b5b77959585c4cce38202c50232da51c47 Mon Sep 17 00:00:00 2001 From: Pedro Felix Date: Mon, 10 Oct 2022 20:29:15 +0100 Subject: [PATCH] gh-2: uses jsonb instead of a string to hold the board information. --- code/tic-tac-tow-service/build.gradle.kts | 5 ++-- .../tic-tac-tow-service/sql/create-schema.sql | 2 +- .../pt/isel/daw/tictactow/domain/Board.kt | 2 +- .../repository/jdbi/JdbiGamesRepository.kt | 28 +++++++++++++++++-- .../daw/tictactow/repository/jdbi/Utils.kt | 2 ++ .../repository/jdbi/mappers/BoardMapper.kt | 5 +++- .../repository/GameRepositoryTests.kt | 14 ++++++++++ 7 files changed, 51 insertions(+), 7 deletions(-) diff --git a/code/tic-tac-tow-service/build.gradle.kts b/code/tic-tac-tow-service/build.gradle.kts index a5fa285..b7b02e9 100644 --- a/code/tic-tac-tow-service/build.gradle.kts +++ b/code/tic-tac-tow-service/build.gradle.kts @@ -28,8 +28,9 @@ dependencies { implementation("org.springframework.security:spring-security-core:5.7.3") // for JDBI - implementation("org.jdbi:jdbi3-core:3.32.0") - implementation("org.jdbi:jdbi3-kotlin:3.32.0") + implementation("org.jdbi:jdbi3-core:3.33.0") + implementation("org.jdbi:jdbi3-kotlin:3.33.0") + implementation("org.jdbi:jdbi3-postgres:3.33.0") implementation("org.postgresql:postgresql:42.5.0") testImplementation("org.springframework.boot:spring-boot-starter-test") diff --git a/code/tic-tac-tow-service/sql/create-schema.sql b/code/tic-tac-tow-service/sql/create-schema.sql index 56f3e03..78d2dbc 100644 --- a/code/tic-tac-tow-service/sql/create-schema.sql +++ b/code/tic-tac-tow-service/sql/create-schema.sql @@ -14,7 +14,7 @@ create table dbo.Tokens( create table dbo.Games( id UUID not null, state VARCHAR(64) not null, - board CHAR(9) not null, + board jsonb not null, created int not null, updated int not null, deadline int, diff --git a/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/domain/Board.kt b/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/domain/Board.kt index fbc34aa..0b54ef4 100644 --- a/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/domain/Board.kt +++ b/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/domain/Board.kt @@ -1,7 +1,7 @@ package pt.isel.daw.tictactow.domain data class Board( - private val cells: Array> + val cells: Array> ) { init { require( diff --git a/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/JdbiGamesRepository.kt b/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/JdbiGamesRepository.kt index f503118..06a2985 100644 --- a/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/JdbiGamesRepository.kt +++ b/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/JdbiGamesRepository.kt @@ -1,8 +1,12 @@ package pt.isel.daw.tictactow.repository.jdbi +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.KotlinModule import org.jdbi.v3.core.Handle import org.jdbi.v3.core.kotlin.mapTo import org.jdbi.v3.core.mapper.Nested +import org.jdbi.v3.core.statement.Update +import org.postgresql.util.PGobject import pt.isel.daw.tictactow.domain.Board import pt.isel.daw.tictactow.domain.Game import pt.isel.daw.tictactow.domain.User @@ -23,7 +27,7 @@ class JdbiGamesRepository( ) .bind("id", game.id) .bind("state", game.state) - .bind("board", game.board.toString()) + .bindBoard("board", game.board) .bind("created", game.created.epochSecond) .bind("updated", game.updated.epochSecond) .bind("deadline", game.deadline?.epochSecond) @@ -61,11 +65,31 @@ class JdbiGamesRepository( ) .bind("id", game.id) .bind("state", game.state) - .bind("board", game.board.toString()) + .bindBoard("board", game.board) .bind("updated", game.updated.epochSecond) .bind("deadline", game.deadline?.epochSecond) .execute() } + + companion object { + private fun Update.bindBoard(name: String, board: Board) = run { + bind( + name, + PGobject().apply { + type = "jsonb" + value = serializeBoardToJson(board) + } + ) + } + + private val objectMapper = ObjectMapper().registerModule(KotlinModule.Builder().build()) + + private fun serializeBoardToJson(board: Board): String = objectMapper.writeValueAsString(board.cells) + + fun deserializeBoardFromJson(json: String) = Board( + objectMapper.readValue(json, Array>::class.java) + ) + } } class GameDbModel( diff --git a/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/Utils.kt b/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/Utils.kt index f1cfc72..7631e63 100644 --- a/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/Utils.kt +++ b/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/Utils.kt @@ -2,6 +2,7 @@ package pt.isel.daw.tictactow.repository.jdbi import org.jdbi.v3.core.Jdbi import org.jdbi.v3.core.kotlin.KotlinPlugin +import org.jdbi.v3.postgres.PostgresPlugin import pt.isel.daw.tictactow.repository.jdbi.mappers.BoardMapper import pt.isel.daw.tictactow.repository.jdbi.mappers.InstantMapper import pt.isel.daw.tictactow.repository.jdbi.mappers.PasswordValidationInfoMapper @@ -9,6 +10,7 @@ import pt.isel.daw.tictactow.repository.jdbi.mappers.TokenValidationInfoMapper fun Jdbi.configure(): Jdbi { installPlugin(KotlinPlugin()) + installPlugin(PostgresPlugin()) registerColumnMapper(PasswordValidationInfoMapper()) registerColumnMapper(TokenValidationInfoMapper()) diff --git a/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/mappers/BoardMapper.kt b/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/mappers/BoardMapper.kt index 640a8fc..fcc3dae 100644 --- a/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/mappers/BoardMapper.kt +++ b/code/tic-tac-tow-service/src/main/kotlin/pt/isel/daw/tictactow/repository/jdbi/mappers/BoardMapper.kt @@ -2,13 +2,16 @@ package pt.isel.daw.tictactow.repository.jdbi.mappers import org.jdbi.v3.core.mapper.ColumnMapper import org.jdbi.v3.core.statement.StatementContext +import org.postgresql.util.PGobject import pt.isel.daw.tictactow.domain.Board +import pt.isel.daw.tictactow.repository.jdbi.JdbiGamesRepository import java.sql.ResultSet import java.sql.SQLException class BoardMapper : ColumnMapper { @Throws(SQLException::class) override fun map(r: ResultSet, columnNumber: Int, ctx: StatementContext?): Board { - return Board.fromString(r.getString(columnNumber)) + val obj = r.getObject(columnNumber, PGobject::class.java) + return JdbiGamesRepository.deserializeBoardFromJson(obj.value ?: throw IllegalArgumentException("TODO")) } } \ No newline at end of file diff --git a/code/tic-tac-tow-service/src/test/kotlin/pt/isel/daw/tictactow/repository/GameRepositoryTests.kt b/code/tic-tac-tow-service/src/test/kotlin/pt/isel/daw/tictactow/repository/GameRepositoryTests.kt index 01eeac4..5ca4dc8 100644 --- a/code/tic-tac-tow-service/src/test/kotlin/pt/isel/daw/tictactow/repository/GameRepositoryTests.kt +++ b/code/tic-tac-tow-service/src/test/kotlin/pt/isel/daw/tictactow/repository/GameRepositoryTests.kt @@ -4,8 +4,10 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.fail import org.junit.jupiter.api.Test import pt.isel.daw.tictactow.RealClock +import pt.isel.daw.tictactow.domain.Board import pt.isel.daw.tictactow.domain.GameLogic import pt.isel.daw.tictactow.domain.PasswordValidationInfo +import pt.isel.daw.tictactow.domain.Position import pt.isel.daw.tictactow.repository.jdbi.JdbiGamesRepository import pt.isel.daw.tictactow.repository.jdbi.JdbiUsersRepository import pt.isel.daw.tictactow.utils.testWithHandleAndRollback @@ -36,5 +38,17 @@ class GameRepositoryTests { // then: the two games are equal assertEquals(game, retrievedGame) + + // when: updating the game + val newGame = game.copy(board = game.board.mutate(Position(1, 1), Board.State.PLAYER_X)) + + // and: storing the game + gameRepo.update(newGame) + + // and: retrieving the game again + val newRetrievedGame = gameRepo.getById(game.id) + + // then: the two games are equal + assertEquals(newGame, newRetrievedGame) } } \ No newline at end of file