Skip to content
Snippets Groups Projects
Commit 952c7dcc authored by Clément CORNU's avatar Clément CORNU
Browse files

feat: new player endpoints (put + get) + update structure and readme

parent 7c435698
No related branches found
No related tags found
No related merge requests found
Showing
with 91 additions and 41 deletions
......@@ -24,6 +24,10 @@ docker compose up -d
## Available endpoints
## Technical choices
- I chose ... because ...
## I want to ensure tests are running
## Left to do
......@@ -32,11 +36,16 @@ docker compose up -d
- [x] faire la connexion avec Dynamo
- 1er endpoint
- [x] créer l'entité joueur
- [ ] créer le repo et le connecter à la BD
- [ ] tester unitairement le service
- [x] créer le repo et le connecter à la BD
- [x] tester unitairement le service
- [ ] tester l'intégration complète
- [ ] endpoint fonctionnel pour l'ajout d'un avec test
- [ ] tests d'inté
- [ ] tests d'intégration
- [ ] tests unitaires
- [ ] tous les endpoints autres
- [ ] gestion de la sécurité
\ No newline at end of file
- gestion de la sécurité
## Going further
- Database migration tool
- ...
\ No newline at end of file
......@@ -12,7 +12,7 @@ plugins {
}
group = "betclic.test"
version = "0.0.1"
version = "0.0.1-SNAPSHOT"
application {
mainClass.set("io.ktor.server.netty.EngineMain")
......@@ -26,19 +26,28 @@ repositories {
}
dependencies {
// Ktor
implementation("io.ktor:ktor-server-core")
implementation("io.ktor:ktor-server-auth")
implementation("io.ktor:ktor-server-openapi")
implementation("io.ktor:ktor-server-config-yaml")
implementation("io.ktor:ktor-server-netty")
// Dependency injection
implementation("io.insert-koin:koin-ktor:$koin_version")
implementation("io.insert-koin:koin-logger-slf4j:$koin_version")
// Content negociation
implementation("io.ktor:ktor-server-content-negotiation")
implementation("io.ktor:ktor-serialization-jackson")
implementation("io.ktor:ktor-server-netty")
implementation("ch.qos.logback:logback-classic:$logback_version")
implementation("io.ktor:ktor-server-config-yaml")
// Database
implementation("software.amazon.awssdk:dynamodb-enhanced:$dynamo_version")
implementation("software.amazon.awssdk:dynamodb:$dynamo_version")
implementation("dev.andrewohara:dynamokt:$dynamo_kt_version")
// Tests
testImplementation("io.ktor:ktor-server-test-host")
testImplementation("io.mockk:mockk:$mockk_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
......
package betclic.test
import betclic.test.player.configureRouting
import betclic.test.configuration.configureHTTP
import betclic.test.configuration.configureKoin
import betclic.test.configuration.configureRouting
import betclic.test.configuration.configureSerialization
import io.ktor.server.application.*
import kotlinx.coroutines.runBlocking
......
package betclic.test
package betclic.test.configuration
import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.serialization.jackson.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.configureSerialization() {
install(ContentNegotiation) {
......@@ -13,9 +11,4 @@ fun Application.configureSerialization() {
enable(SerializationFeature.INDENT_OUTPUT)
}
}
routing {
get("/json/jackson") {
call.respond(mapOf("hello" to "world"))
}
}
}
package betclic.test
package betclic.test.configuration
import io.ktor.server.application.*
import io.ktor.server.plugins.openapi.*
......
package betclic.test
package betclic.test.configuration
import betclic.test.player.PlayerEntity
import dev.andrewohara.dynamokt.DataClassTableSchema
......@@ -24,12 +24,6 @@ private fun createDynamoDbClient(): DynamoDbAsyncClient {
return DynamoDbAsyncClient.builder().endpointOverride(URI("http://localhost:8000")).build()
}
fun Application.createEnhancedDynamoDbClient(dynamoDbClient: DynamoDbAsyncClient): DynamoDbEnhancedAsyncClient {
return DynamoDbEnhancedAsyncClient.builder()
.dynamoDbClient(dynamoDbClient)
.build()
}
suspend fun createNecessaryTables(
dynamoDbClient: DynamoDbAsyncClient,
dynamoDbEnhancedClient: DynamoDbEnhancedAsyncClient
......
package betclic.test
package betclic.test.configuration
import betclic.test.player.PlayerRepository
import betclic.test.player.PlayerService
......
package betclic.test.configuration
import betclic.test.player.playerRoutes
import io.ktor.server.application.*
import io.ktor.server.routing.*
fun Application.configureRouting() {
routing {
playerRoutes()
}
}
\ No newline at end of file
package betclic.test
package betclic.test.configuration
import io.ktor.server.application.*
......
......@@ -6,6 +6,7 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.future.await
import org.slf4j.LoggerFactory
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient
import software.amazon.awssdk.enhanced.dynamodb.Key
class PlayerRepository(dynamoDbEnhancedClient: DynamoDbEnhancedAsyncClient) {
private val tableName = PlayerEntity::class.simpleName
......@@ -13,8 +14,20 @@ class PlayerRepository(dynamoDbEnhancedClient: DynamoDbEnhancedAsyncClient) {
private val table = dynamoDbEnhancedClient.table(tableName, tableSchema)
private val logger = LoggerFactory.getLogger(Application::class.java)
suspend fun createNewPlayer(player: Player): Unit = coroutineScope {
val saved = table.putItem(player.toPlayerEntity()).await()
logger.info("Successfully created new player $saved")
suspend fun createNewPlayer(player: Player) = coroutineScope {
val createdPlayer = table.putItem(player.toPlayerEntity()).await()
logger.info("Successfully created new player $createdPlayer")
}
suspend fun updatePlayer(player: Player): Player {
val updatedPlayer = table.updateItem(player.toPlayerEntity()).await()
return updatedPlayer.toPlayer()
}
suspend fun findPlayerByPseudo(pseudo: String): Player {
val foundPlayer = table.getItem(
Key.builder().partitionValue(pseudo).build()
).await()
return foundPlayer.toPlayer()
}
}
\ No newline at end of file
......@@ -7,14 +7,21 @@ import io.ktor.server.response.*
import io.ktor.server.routing.*
import org.koin.ktor.ext.inject
fun Application.configureRouting() {
routing {
fun Routing.playerRoutes() {
val playerService by inject<PlayerService>()
post("/player") {
post("/players") {
val request = call.receive<String>()
playerService.createNewPlayer(request)
call.respond(HttpStatusCode.Created)
}
put("/players") {
val request = call.receive<Player>()
call.respond(playerService.updatePlayer(request))
}
get("/players/{pseudo}") {
val pseudo = call.parameters["pseudo"] ?: return@get call.respond(HttpStatusCode.BadRequest)
call.respond<Player>(playerService.getPlayerByPseudo(pseudo))
}
}
\ No newline at end of file
......@@ -2,4 +2,8 @@ package betclic.test.player
interface PlayerService {
suspend fun createNewPlayer(pseudo: String)
suspend fun updatePlayer(player: Player): Player
suspend fun getPlayerByPseudo(pseudo: String): Player
}
\ No newline at end of file
......@@ -5,4 +5,12 @@ class PlayerServiceImpl(private val playerRepository: PlayerRepository) : Player
override suspend fun createNewPlayer(pseudo: String) {
playerRepository.createNewPlayer(Player(pseudo = pseudo))
}
override suspend fun updatePlayer(player: Player): Player {
return playerRepository.updatePlayer(player)
}
override suspend fun getPlayerByPseudo(pseudo: String): Player {
return playerRepository.findPlayerByPseudo(pseudo)
}
}
\ No newline at end of file
package betclic.test.player
package player
import io.ktor.client.request.*
import io.ktor.http.*
......@@ -9,7 +9,6 @@ import kotlin.test.assertEquals
class PlayerIntegrationTest {
@Test
fun `When calling post player, should return created`() = testApplication {
assertEquals(HttpStatusCode.Created, client.post("/player") {
header(HttpHeaders.ContentType, ContentType.Text.Plain)
setBody("Test d'intégration")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment