From fef8bee71c3ae264a244249dd435f0e4d8f1d3bd Mon Sep 17 00:00:00 2001
From: ccornu <ccornu@takima.fr>
Date: Fri, 7 Feb 2025 18:20:45 +0100
Subject: [PATCH] feat: add all required endpoints

---
 README.md                                   |  6 +--
 build.gradle.kts                            |  2 +
 gradle.properties                           |  1 +
 src/main/kotlin/player/PlayerInfoDTO.kt     |  7 ++++
 src/main/kotlin/player/PlayerRepository.kt  | 21 +++++++++--
 src/main/kotlin/player/PlayerRoute.kt       | 41 +++++++++++++++------
 src/main/kotlin/player/PlayerService.kt     |  8 +++-
 src/main/kotlin/player/PlayerServiceImpl.kt | 30 ++++++++++++++-
 8 files changed, 95 insertions(+), 21 deletions(-)
 create mode 100644 src/main/kotlin/player/PlayerInfoDTO.kt

diff --git a/README.md b/README.md
index dcd8dbf..a656b2c 100644
--- a/README.md
+++ b/README.md
@@ -39,11 +39,11 @@ docker compose up -d
     - [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
+    - [x] endpoint fonctionnel pour l'ajout d'un joueur avec test
         - [ ] tests d'intégration
         - [ ] tests unitaires
-- [ ] tous les endpoints autres
-- gestion de la sécurité
+- [x] tous les endpoints demandés
+- [ ] gestion de la sécurité
 
 ## Going further
 
diff --git a/build.gradle.kts b/build.gradle.kts
index ade3b6f..29241d0 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,6 +4,7 @@ val logback_version: String by project
 val dynamo_version: String by project
 val dynamo_kt_version: String by project
 val mockk_version: String by project
+val kotlin_reactive_version: String by project
 
 
 plugins {
@@ -46,6 +47,7 @@ dependencies {
     implementation("software.amazon.awssdk:dynamodb-enhanced:$dynamo_version")
     implementation("software.amazon.awssdk:dynamodb:$dynamo_version")
     implementation("dev.andrewohara:dynamokt:$dynamo_kt_version")
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$kotlin_reactive_version")
 
     // Tests
     testImplementation("io.ktor:ktor-server-test-host")
diff --git a/gradle.properties b/gradle.properties
index 4bf19e4..89aec6c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,3 +6,4 @@ dynamo_kt_version=1.0.0
 ktor_version=2.3.13
 logback_version=1.4.14
 mockk_version=1.10.0
+kotlin_reactive_version=1.9.0
diff --git a/src/main/kotlin/player/PlayerInfoDTO.kt b/src/main/kotlin/player/PlayerInfoDTO.kt
new file mode 100644
index 0000000..c74c977
--- /dev/null
+++ b/src/main/kotlin/player/PlayerInfoDTO.kt
@@ -0,0 +1,7 @@
+package betclic.test.player
+
+class PlayerInfoDTO(
+    val pseudo: String,
+    val pointsNumber: Int,
+    val ranking: Int,
+)
\ No newline at end of file
diff --git a/src/main/kotlin/player/PlayerRepository.kt b/src/main/kotlin/player/PlayerRepository.kt
index c85d2a0..3a15fd9 100644
--- a/src/main/kotlin/player/PlayerRepository.kt
+++ b/src/main/kotlin/player/PlayerRepository.kt
@@ -4,6 +4,7 @@ import dev.andrewohara.dynamokt.DataClassTableSchema
 import io.ktor.server.application.*
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.future.await
+import kotlinx.coroutines.reactive.asFlow
 import org.slf4j.LoggerFactory
 import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient
 import software.amazon.awssdk.enhanced.dynamodb.Key
@@ -24,10 +25,24 @@ class PlayerRepository(dynamoDbEnhancedClient: DynamoDbEnhancedAsyncClient) {
         return updatedPlayer.toPlayer()
     }
 
-    suspend fun findPlayerByPseudo(pseudo: String): Player {
+    suspend fun findPlayerByPseudo(pseudo: String): Player? {
         val foundPlayer = table.getItem(
             Key.builder().partitionValue(pseudo).build()
         ).await()
-        return foundPlayer.toPlayer()
+        return foundPlayer?.toPlayer()
     }
-}
\ No newline at end of file
+
+    suspend fun findAll(): List<Player> {
+        return buildList {
+            table.scan().asFlow().collect { it.items().stream().forEach { item -> add(item.toPlayer()) } }
+        }
+    }
+
+
+    suspend fun deleteAllPlayers() {
+        table.deleteTable()
+        table.createTable().await()
+
+    }
+}
+
diff --git a/src/main/kotlin/player/PlayerRoute.kt b/src/main/kotlin/player/PlayerRoute.kt
index ff94f41..c55907f 100644
--- a/src/main/kotlin/player/PlayerRoute.kt
+++ b/src/main/kotlin/player/PlayerRoute.kt
@@ -9,19 +9,36 @@ import org.koin.ktor.ext.inject
 
 fun Routing.playerRoutes() {
     val playerService by inject<PlayerService>()
-    post("/players") {
-        val request = call.receive<String>()
-        playerService.createNewPlayer(request)
-        call.respond(HttpStatusCode.Created)
-    }
+    route("/players") {
+        post {
+            val request = call.receive<String>()
+            playerService.createNewPlayer(request)
+            call.respond(HttpStatusCode.Created)
+        }
 
-    put("/players") {
-        val request = call.receive<Player>()
-        call.respond(playerService.updatePlayer(request))
-    }
+        put {
+            val request = call.receive<Player>()
+            call.respond(playerService.updatePlayer(request))
+        }
+
+        get("/{pseudo}") {
+            val pseudo = call.parameters["pseudo"] ?: return@get call.respond(HttpStatusCode.BadRequest)
+            call.respond<Player>(playerService.findPlayerByPseudo(pseudo))
+        }
 
-    get("/players/{pseudo}") {
-        val pseudo = call.parameters["pseudo"] ?: return@get call.respond(HttpStatusCode.BadRequest)
-        call.respond<Player>(playerService.getPlayerByPseudo(pseudo))
+        get("/{pseudo}/info") {
+            val pseudo = call.parameters["pseudo"] ?: return@get call.respond(HttpStatusCode.BadRequest)
+            call.respond<PlayerInfoDTO>(playerService.getPlayerInfoByPseudo(pseudo))
+        }
+
+        get("/ranking") {
+            call.respond(playerService.getPlayersRanked())
+        }
+
+        delete {
+            playerService.deleteAllPlayers()
+            call.respond(HttpStatusCode.NoContent)
+        }
     }
+
 }
\ No newline at end of file
diff --git a/src/main/kotlin/player/PlayerService.kt b/src/main/kotlin/player/PlayerService.kt
index 2f06a29..2b12d17 100644
--- a/src/main/kotlin/player/PlayerService.kt
+++ b/src/main/kotlin/player/PlayerService.kt
@@ -5,5 +5,11 @@ interface PlayerService {
 
     suspend fun updatePlayer(player: Player): Player
 
-    suspend fun getPlayerByPseudo(pseudo: String): Player
+    suspend fun findPlayerByPseudo(pseudo: String): Player
+
+    suspend fun getPlayerInfoByPseudo(pseudo: String): PlayerInfoDTO
+
+    suspend fun getPlayersRanked(): List<PlayerInfoDTO>
+
+    suspend fun deleteAllPlayers()
 }
\ No newline at end of file
diff --git a/src/main/kotlin/player/PlayerServiceImpl.kt b/src/main/kotlin/player/PlayerServiceImpl.kt
index 9af6cdc..a9fc7a4 100644
--- a/src/main/kotlin/player/PlayerServiceImpl.kt
+++ b/src/main/kotlin/player/PlayerServiceImpl.kt
@@ -1,5 +1,7 @@
 package betclic.test.player
 
+import io.ktor.server.plugins.*
+
 class PlayerServiceImpl(private val playerRepository: PlayerRepository) : PlayerService {
 
     override suspend fun createNewPlayer(pseudo: String) {
@@ -10,7 +12,31 @@ class PlayerServiceImpl(private val playerRepository: PlayerRepository) : Player
         return playerRepository.updatePlayer(player)
     }
 
-    override suspend fun getPlayerByPseudo(pseudo: String): Player {
-        return playerRepository.findPlayerByPseudo(pseudo)
+    override suspend fun findPlayerByPseudo(pseudo: String): Player {
+        val player = playerRepository.findPlayerByPseudo(pseudo) ?: throw NotFoundException("Player $pseudo not found")
+        return player
+    }
+
+    override suspend fun getPlayerInfoByPseudo(pseudo: String): PlayerInfoDTO {
+        val allPlayersRanked = getPlayersRanked()
+        val player = allPlayersRanked.find { it.pseudo == pseudo }
+            ?: throw NotFoundException("Player $pseudo not found in ranking")
+        return player
+    }
+
+    override suspend fun getPlayersRanked(): List<PlayerInfoDTO> {
+        val allPlayers = playerRepository.findAll()
+        return allPlayers.sortedByDescending { it.pointsNumber }.mapIndexed { index, player ->
+            PlayerInfoDTO(
+                pseudo = player.pseudo,
+                pointsNumber = player.pointsNumber,
+                ranking = index + 1
+            )
+        }
+
+    }
+
+    override suspend fun deleteAllPlayers() {
+        playerRepository.deleteAllPlayers()
     }
 }
\ No newline at end of file
-- 
GitLab