From 9ff2e8d8962917869b64f926a1b0662fe00582b5 Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Tue, 21 May 2024 14:25:55 +0200 Subject: [PATCH 01/15] :sparkles: Add route and column for get the most popular bet --- .../main/kotlin/allin/data/BetDataSource.kt | 1 + .../allin/data/mock/MockBetDataSource.kt | 4 +++ .../data/postgres/PostgresBetDataSource.kt | 9 ++++++ .../allin/data/postgres/PostgresDataSource.kt | 32 ++++++++++++++++++- .../allin/data/postgres/entities/BetEntity.kt | 4 +++ .../main/kotlin/allin/routing/betRouter.kt | 29 ++++++++++++++++- 6 files changed, 77 insertions(+), 2 deletions(-) diff --git a/Sources/src/main/kotlin/allin/data/BetDataSource.kt b/Sources/src/main/kotlin/allin/data/BetDataSource.kt index 7aa2762..ad94ffb 100644 --- a/Sources/src/main/kotlin/allin/data/BetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/BetDataSource.kt @@ -20,4 +20,5 @@ interface BetDataSource { fun getWonNotifications(username: String): List fun getHistory(username: String): List fun getCurrent(username: String): List + fun getMostPopularBet(): Bet? } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt b/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt index 20a20ec..53278d5 100644 --- a/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt @@ -164,4 +164,8 @@ class MockBetDataSource(private val mockData: MockDataSource.MockData) : BetData userParticipation = participation ) } + + override fun getMostPopularBet(): Bet? { + TODO("Not yet implemented") + } } diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt index f650a69..75a5f43 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt @@ -110,6 +110,14 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { } } + override fun getMostPopularBet(): Bet? { + val max=database.bets.filter { (it.isPrivate eq false) and (it.status eq BetStatus.WAITING) }.maxBy { it.popularityscore } + if(max!=null){ + return database.bets.filter { (it.popularityscore eq max) and (it.isPrivate eq false) and (it.status eq BetStatus.WAITING) }.map { it.toBet(database) }.first() + } + return null + } + override fun addBet(bet: Bet) { database.bets.add( BetEntity { @@ -172,4 +180,5 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { } } } + } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresDataSource.kt index 3e08eff..921d658 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresDataSource.kt @@ -48,7 +48,8 @@ class PostgresDataSource : AllInDataSource() { isprivate boolean, createdby varchar(250), status varchar(20), - type varchar(20) + type varchar(20), + popularityscore numeric ) """.trimIndent() ) @@ -124,6 +125,35 @@ class PostgresDataSource : AllInDataSource() { ) """.trimIndent() ) + + database.execute(""" + CREATE OR REPLACE FUNCTION update_popularity_score() + RETURNS TRIGGER AS ${'$'}${'$'} + DECLARE + participant_count INT; + total_stakes INT; + BEGIN + -- Calculate participant count and total stakes for the bet + SELECT COUNT(*), COALESCE(SUM(stake), 0) INTO participant_count, total_stakes + FROM participation + WHERE bet = NEW.bet; + + -- Update the popularityscore in the bet table + UPDATE bet + SET popularityscore = (participant_count * participant_count + total_stakes) + WHERE id = NEW.bet; + + RETURN NEW; + END; + ${'$'}${'$'} LANGUAGE plpgsql; + + DROP TRIGGER IF EXISTS update_popularity_score ON participation; + + CREATE TRIGGER update_popularity_score + AFTER INSERT OR UPDATE ON participation + FOR EACH ROW + EXECUTE FUNCTION update_popularity_score(); + """.trimIndent()) } override val userDataSource: UserDataSource by lazy { PostgresUserDataSource(database) } diff --git a/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt b/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt index ad68238..8c0e964 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt @@ -5,6 +5,7 @@ import org.ktorm.database.Database import org.ktorm.dsl.eq import org.ktorm.entity.* import org.ktorm.schema.* +import org.postgresql.util.ByteConverter.numeric import java.time.Instant import java.time.ZoneId import java.time.ZonedDateTime @@ -23,6 +24,7 @@ interface BetEntity : Entity { var status: BetStatus var type: BetType var createdBy: String + var popularityscore: Int fun toBet(database: Database) = Bet( @@ -60,6 +62,7 @@ interface BetEntity : Entity { ) } + } object BetsEntity : Table("bet") { @@ -73,6 +76,7 @@ object BetsEntity : Table("bet") { val status = enum("status").bindTo { it.status } val type = enum("type").bindTo { it.type } val createdBy = varchar("createdby").bindTo { it.createdBy } + val popularityscore = int("popularityscore").bindTo { it.popularityscore } } val Database.bets get() = this.sequenceOf(BetsEntity) \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/routing/betRouter.kt b/Sources/src/main/kotlin/allin/routing/betRouter.kt index 2cc737f..f97727e 100644 --- a/Sources/src/main/kotlin/allin/routing/betRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/betRouter.kt @@ -53,7 +53,7 @@ fun Application.betRouter() { betDataSource.getBetById(id)?.let { call.respond(HttpStatusCode.Conflict, ApiMessage.BET_ALREADY_EXIST) } ?: run { - val betWithId = bet.copy(id = id, createdBy = user.first?.username.toString()) + val betWithId = bet.copy(id = id, createdBy = user.first?.id.toString()) betDataSource.addBet(betWithId) call.respond(HttpStatusCode.Created, betWithId) } @@ -83,6 +83,33 @@ fun Application.betRouter() { } } + authenticate { + get("/bets/popular", { + description = "Allows you to recover the most popular public bets" + request { + headerParameter("JWT token of the logged user") + } + response { + HttpStatusCode.Accepted to { + description = "The most popular public bet is available" + body { + description = "The most popular public bet" + } + } + } + }) { + hasToken { principal -> + verifyUserFromToken(userDataSource, principal) { _, _ -> + val bet = betDataSource.getMostPopularBet() + if (bet != null) { + call.respond(HttpStatusCode.Accepted, bet) + } + call.respond(HttpStatusCode.NotFound,"Aucun bet n'a pu être récupérer") + } + } + } + } + get("/bets/get/{id}", { description = "Retrieves a specific bet" request { From f2a84adfcce74c97de344de1b61eff83f79ab0b5 Mon Sep 17 00:00:00 2001 From: Lucas EVARD Date: Tue, 21 May 2024 14:30:40 +0200 Subject: [PATCH 02/15] [db] Raise database --- .drone.star | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.star b/.drone.star index 32cb14a..5910419 100644 --- a/.drone.star +++ b/.drone.star @@ -2,7 +2,7 @@ def main(ctx): commit_message = ctx.build.message.lower() if "readme.md" in commit_message or "[no_ci]" in commit_message: return nullPipeline() - + if "[db]" in commit_message: return [ ci(ctx), From 1c3fb9a7f30709a9cc738445a4bb4747aed26544 Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Tue, 21 May 2024 15:16:13 +0200 Subject: [PATCH 03/15] :sparkles: [db] Patch cache problem --- .../data/postgres/PostgresUserDataSource.kt | 14 +++++---- .../main/kotlin/allin/routing/userRouter.kt | 29 ++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresUserDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresUserDataSource.kt index 932cda8..31a51f8 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresUserDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresUserDataSource.kt @@ -40,15 +40,17 @@ class PostgresUserDataSource(private val database: Database) : UserDataSource { database.users.removeIf { (it.username eq username) or (it.email eq username) } > 0 override fun addCoins(username: String, amount: Int) { - database.users - .find { it.username eq username } - ?.set(UsersEntity.nbCoins.name, UsersEntity.nbCoins + amount) + database.update(UsersEntity) { + set(it.nbCoins, it.nbCoins + amount) + where { it.username eq username } + } } override fun removeCoins(username: String, amount: Int) { - database.users - .find { it.username eq username } - ?.set(UsersEntity.nbCoins.name, UsersEntity.nbCoins - amount) + database.update(UsersEntity) { + set(it.nbCoins, it.nbCoins - amount) + where { it.username eq username } + } } override fun userExists(username: String) = diff --git a/Sources/src/main/kotlin/allin/routing/userRouter.kt b/Sources/src/main/kotlin/allin/routing/userRouter.kt index d37b4e6..ace7d8b 100644 --- a/Sources/src/main/kotlin/allin/routing/userRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/userRouter.kt @@ -56,24 +56,25 @@ fun Application.userRouter() { if (RegexCheckerUser.isEmailInvalid(tempUser.email)) { call.respond(HttpStatusCode.Forbidden, ApiMessage.INVALID_MAIL) } - if (userDataSource.userExists(tempUser.username)) { + else if (userDataSource.userExists(tempUser.username)) { call.respond(HttpStatusCode.Conflict, ApiMessage.USER_ALREADY_EXISTS) } - if(userDataSource.emailExists(tempUser.email)){ + else if (userDataSource.emailExists(tempUser.email)) { call.respond(HttpStatusCode.Conflict, ApiMessage.MAIL_ALREADY_EXISTS) + } else { + val user = User( + id = UUID.randomUUID().toString(), + username = tempUser.username, + email = tempUser.email, + password = tempUser.password, + nbCoins = DEFAULT_COINS, + token = null + ) + CryptManagerUser.passwordCrypt(user) + user.token = tokenManagerUser.generateOrReplaceJWTToken(user) + userDataSource.addUser(user) + call.respond(HttpStatusCode.Created, user) } - val user = User( - id = UUID.randomUUID().toString(), - username = tempUser.username, - email = tempUser.email, - password = tempUser.password, - nbCoins = DEFAULT_COINS, - token = null - ) - CryptManagerUser.passwordCrypt(user) - user.token = tokenManagerUser.generateOrReplaceJWTToken(user) - userDataSource.addUser(user) - call.respond(HttpStatusCode.Created, user) } post("/users/login", { From b03bf7e121db7bd97b6651fc97e6a3dd07c29691 Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Wed, 22 May 2024 10:11:07 +0200 Subject: [PATCH 04/15] :sparkles: debug Popular bet --- .../src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt index 75a5f43..3751052 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt @@ -112,6 +112,7 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { override fun getMostPopularBet(): Bet? { val max=database.bets.filter { (it.isPrivate eq false) and (it.status eq BetStatus.WAITING) }.maxBy { it.popularityscore } + println(max) if(max!=null){ return database.bets.filter { (it.popularityscore eq max) and (it.isPrivate eq false) and (it.status eq BetStatus.WAITING) }.map { it.toBet(database) }.first() } From ddacad6885e6a00f6a2f67cde097d5ed882404b9 Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Wed, 22 May 2024 10:13:33 +0200 Subject: [PATCH 05/15] :sparkles: debug Popular bet --- .../main/kotlin/allin/data/postgres/PostgresBetDataSource.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt index 3751052..f554b03 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt @@ -111,6 +111,11 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { } override fun getMostPopularBet(): Bet? { + + database.bets.forEach { + println(it) + } + val max=database.bets.filter { (it.isPrivate eq false) and (it.status eq BetStatus.WAITING) }.maxBy { it.popularityscore } println(max) if(max!=null){ From 74ddd915f9332f9b687e881b088ca84bf8622be7 Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Wed, 22 May 2024 10:17:38 +0200 Subject: [PATCH 06/15] :sparkles: [db] debug Popular bet --- Sources/src/main/kotlin/allin/Application.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/src/main/kotlin/allin/Application.kt b/Sources/src/main/kotlin/allin/Application.kt index 374fb93..dc29c5d 100644 --- a/Sources/src/main/kotlin/allin/Application.kt +++ b/Sources/src/main/kotlin/allin/Application.kt @@ -58,6 +58,7 @@ private fun Application.extracted() { } } } + install(ContentNegotiation) { json() } install(SwaggerUI) { swagger { From a3aa69b07dfbe9f4f808a412a629b1bbcb68a129 Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Wed, 22 May 2024 10:54:12 +0200 Subject: [PATCH 07/15] :sparkles: debug Popular bet --- .../main/kotlin/allin/data/BetDataSource.kt | 1 + .../allin/data/mock/MockBetDataSource.kt | 4 +++ .../data/postgres/PostgresBetDataSource.kt | 15 ++++++++++ .../allin/data/postgres/PostgresDataSource.kt | 29 ------------------- .../allin/routing/participationRouter.kt | 2 ++ 5 files changed, 22 insertions(+), 29 deletions(-) diff --git a/Sources/src/main/kotlin/allin/data/BetDataSource.kt b/Sources/src/main/kotlin/allin/data/BetDataSource.kt index ad94ffb..2e9ee1d 100644 --- a/Sources/src/main/kotlin/allin/data/BetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/BetDataSource.kt @@ -21,4 +21,5 @@ interface BetDataSource { fun getHistory(username: String): List fun getCurrent(username: String): List fun getMostPopularBet(): Bet? + fun updatePopularityScore(betId: String) } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt b/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt index 53278d5..d29b790 100644 --- a/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt @@ -168,4 +168,8 @@ class MockBetDataSource(private val mockData: MockDataSource.MockData) : BetData override fun getMostPopularBet(): Bet? { TODO("Not yet implemented") } + + override fun updatePopularityScore(betId: String) { + TODO("Not yet implemented") + } } diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt index f554b03..76ea6a1 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt @@ -124,6 +124,21 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { return null } + override fun updatePopularityScore(betId: String) { + val bet = database.bets.filter { it.id eq betId }.firstOrNull() + if (bet == null) { + return + } + val participations = database.participations.filter { it.betId eq betId } + val stakes = participations.map { it.stake } + val score = (participations.count() * participations.count()) + stakes.sum() + database.update(BetsEntity) { + set(it.popularityscore, score) + where { it.id eq betId } + } + } + + override fun addBet(bet: Bet) { database.bets.add( BetEntity { diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresDataSource.kt index 921d658..41b075b 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresDataSource.kt @@ -125,35 +125,6 @@ class PostgresDataSource : AllInDataSource() { ) """.trimIndent() ) - - database.execute(""" - CREATE OR REPLACE FUNCTION update_popularity_score() - RETURNS TRIGGER AS ${'$'}${'$'} - DECLARE - participant_count INT; - total_stakes INT; - BEGIN - -- Calculate participant count and total stakes for the bet - SELECT COUNT(*), COALESCE(SUM(stake), 0) INTO participant_count, total_stakes - FROM participation - WHERE bet = NEW.bet; - - -- Update the popularityscore in the bet table - UPDATE bet - SET popularityscore = (participant_count * participant_count + total_stakes) - WHERE id = NEW.bet; - - RETURN NEW; - END; - ${'$'}${'$'} LANGUAGE plpgsql; - - DROP TRIGGER IF EXISTS update_popularity_score ON participation; - - CREATE TRIGGER update_popularity_score - AFTER INSERT OR UPDATE ON participation - FOR EACH ROW - EXECUTE FUNCTION update_popularity_score(); - """.trimIndent()) } override val userDataSource: UserDataSource by lazy { PostgresUserDataSource(database) } diff --git a/Sources/src/main/kotlin/allin/routing/participationRouter.kt b/Sources/src/main/kotlin/allin/routing/participationRouter.kt index 43016b6..a6147f8 100644 --- a/Sources/src/main/kotlin/allin/routing/participationRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/participationRouter.kt @@ -22,6 +22,7 @@ fun Application.participationRouter() { val userDataSource = this.dataSource.userDataSource val participationDataSource = this.dataSource.participationDataSource + val betDataSource = this.dataSource.betDataSource routing { authenticate { @@ -59,6 +60,7 @@ fun Application.participationRouter() { ) userDataSource.removeCoins(username = user.username, amount = participation.stake) + betDataSource.updatePopularityScore(participation.betId) call.respond(HttpStatusCode.Created) } else { call.respond(HttpStatusCode.Forbidden, ApiMessage.NOT_ENOUGH_COINS) From 75666ad2a3f508638e3ec8ab0e8d996ca0daf9b9 Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Wed, 22 May 2024 11:15:02 +0200 Subject: [PATCH 08/15] :sparkles: Refactor and add PopularityScore for Mocking Data Source --- .../allin/data/mock/MockBetDataSource.kt | 18 ++++++++++++++---- .../data/postgres/PostgresBetDataSource.kt | 15 ++------------- Sources/src/main/kotlin/allin/model/Bet.kt | 3 ++- .../allin/routing/participationRouter.kt | 5 +++++ 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt b/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt index d29b790..0efd3e5 100644 --- a/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt @@ -1,8 +1,15 @@ package allin.data.mock import allin.data.BetDataSource +import allin.data.postgres.entities.BetsEntity +import allin.data.postgres.entities.bets +import allin.data.postgres.entities.participations import allin.model.* import allin.model.BetStatus.* +import org.ktorm.dsl.and +import org.ktorm.dsl.eq +import org.ktorm.dsl.update +import org.ktorm.entity.* import java.time.ZonedDateTime import kotlin.math.roundToInt @@ -165,11 +172,14 @@ class MockBetDataSource(private val mockData: MockDataSource.MockData) : BetData ) } - override fun getMostPopularBet(): Bet? { - TODO("Not yet implemented") - } + override fun getMostPopularBet() = + mockData.bets.filter { !it.isPrivate && it.status == WAITING }.maxBy { it.popularityscore } override fun updatePopularityScore(betId: String) { - TODO("Not yet implemented") + val bet = mockData.bets.firstOrNull { it.id == betId } ?: return + val participations = mockData.participations.filter { it.betId == betId } + val score = participations.size * participations.size + participations.sumOf { it.stake } + bet.popularityscore = score } + } diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt index 76ea6a1..08f94fd 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt @@ -111,13 +111,7 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { } override fun getMostPopularBet(): Bet? { - - database.bets.forEach { - println(it) - } - val max=database.bets.filter { (it.isPrivate eq false) and (it.status eq BetStatus.WAITING) }.maxBy { it.popularityscore } - println(max) if(max!=null){ return database.bets.filter { (it.popularityscore eq max) and (it.isPrivate eq false) and (it.status eq BetStatus.WAITING) }.map { it.toBet(database) }.first() } @@ -125,20 +119,15 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { } override fun updatePopularityScore(betId: String) { - val bet = database.bets.filter { it.id eq betId }.firstOrNull() - if (bet == null) { - return - } + database.bets.filter { it.id eq betId }.firstOrNull() ?: return val participations = database.participations.filter { it.betId eq betId } - val stakes = participations.map { it.stake } - val score = (participations.count() * participations.count()) + stakes.sum() + val score = (participations.count() * participations.count()) + participations.map { it.stake }.sum() database.update(BetsEntity) { set(it.popularityscore, score) where { it.id eq betId } } } - override fun addBet(bet: Bet) { database.bets.add( BetEntity { diff --git a/Sources/src/main/kotlin/allin/model/Bet.kt b/Sources/src/main/kotlin/allin/model/Bet.kt index 23eb638..07a8360 100644 --- a/Sources/src/main/kotlin/allin/model/Bet.kt +++ b/Sources/src/main/kotlin/allin/model/Bet.kt @@ -19,7 +19,8 @@ data class Bet( @Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime, var isPrivate: Boolean, var response: List, - val createdBy: String = "" + val createdBy: String = "", + var popularityscore: Int ) @Serializable diff --git a/Sources/src/main/kotlin/allin/routing/participationRouter.kt b/Sources/src/main/kotlin/allin/routing/participationRouter.kt index a6147f8..1e0aa8c 100644 --- a/Sources/src/main/kotlin/allin/routing/participationRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/participationRouter.kt @@ -48,6 +48,11 @@ fun Application.participationRouter() { hasToken { principal -> val participation = call.receive() verifyUserFromToken(userDataSource, principal) { user, _ -> + + if(betDataSource.getBetById(participation.betId)== null){ + call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND) + } + if (user.nbCoins >= participation.stake) { participationDataSource.addParticipation( Participation( From f82aba9a02aedc80d8384da261450d9f21ecfdef Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Wed, 22 May 2024 11:19:42 +0200 Subject: [PATCH 09/15] :sparkles: Refactor and add PopularityScore for Mocking Data Source --- .../main/kotlin/allin/data/postgres/PostgresBetDataSource.kt | 1 + .../src/main/kotlin/allin/data/postgres/entities/BetEntity.kt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt index 08f94fd..d243f00 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt @@ -141,6 +141,7 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { this.createdBy = bet.createdBy this.status = bet.status this.type = bet.type + this.popularityscore = 0 } ) diff --git a/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt b/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt index 8c0e964..31949eb 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt @@ -41,7 +41,8 @@ interface BetEntity : Entity { } else { database.responses.filter { it.betId eq id }.map { it.response } }, - createdBy = createdBy + createdBy = createdBy, + popularityscore = popularityscore, ) fun toBetDetail(database: Database, username: String): BetDetail { From 0b80c4dc90f3ed604d3712e35897346c47386010 Mon Sep 17 00:00:00 2001 From: Arthur VALIN Date: Tue, 21 May 2024 11:20:46 +0200 Subject: [PATCH 10/15] =?UTF-8?q?Mise=20=C3=A0=20jour=20de=20'.drone.star'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .drone.star | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.star b/.drone.star index 5910419..08cdb5b 100644 --- a/.drone.star +++ b/.drone.star @@ -93,7 +93,7 @@ def cd(ctx): "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_PASSWORD": {"from_secret": "db_password"}, "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_HOST": {"from_secret": "db_host"}, "CODEFIRST_CLIENTDRONE_ENV_SALT": {"from_secret": "SALT"}, - "ADMINS": "lucasevard,emrekartal,arthurvalin,lucasdelanier", + "ADMINS": ["lucasevard", "emrekartal", "arthurvalin", "lucasdelanier"], "IMAGENAME": "hub.codefirst.iut.uca.fr/lucas.evard/api:latest", "CONTAINERNAME": "api", "COMMAND": "create", @@ -122,7 +122,7 @@ def db(ctx): "COMMAND": "create", "OVERWRITE": "false", "PRIVATE": "false", - "ADMINS": "lucasevard,emrekartal,arthurvalin,lucasdelanier", + "ADMINS": ["lucasevard", "emrekartal", "arthurvalin", "lucasdelanier"], "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_ROOT_PASSWORD": {"from_secret": "db_root_password"}, "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_DB": {"from_secret": "db_database"}, "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_USER": {"from_secret": "db_user"}, From e5b262af145a1fa5be7305b2885b211231b66d3a Mon Sep 17 00:00:00 2001 From: Arthur VALIN Date: Tue, 21 May 2024 11:26:13 +0200 Subject: [PATCH 11/15] revert 667cdc54726cd0814e1fae29ebd3c780332bbd71 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit revert Mise à jour de '.drone.star' --- .drone.star | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.star b/.drone.star index 08cdb5b..5910419 100644 --- a/.drone.star +++ b/.drone.star @@ -93,7 +93,7 @@ def cd(ctx): "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_PASSWORD": {"from_secret": "db_password"}, "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_HOST": {"from_secret": "db_host"}, "CODEFIRST_CLIENTDRONE_ENV_SALT": {"from_secret": "SALT"}, - "ADMINS": ["lucasevard", "emrekartal", "arthurvalin", "lucasdelanier"], + "ADMINS": "lucasevard,emrekartal,arthurvalin,lucasdelanier", "IMAGENAME": "hub.codefirst.iut.uca.fr/lucas.evard/api:latest", "CONTAINERNAME": "api", "COMMAND": "create", @@ -122,7 +122,7 @@ def db(ctx): "COMMAND": "create", "OVERWRITE": "false", "PRIVATE": "false", - "ADMINS": ["lucasevard", "emrekartal", "arthurvalin", "lucasdelanier"], + "ADMINS": "lucasevard,emrekartal,arthurvalin,lucasdelanier", "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_ROOT_PASSWORD": {"from_secret": "db_root_password"}, "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_DB": {"from_secret": "db_database"}, "CODEFIRST_CLIENTDRONE_ENV_POSTGRES_USER": {"from_secret": "db_user"}, From 17c526b8b30756b174b67a00d484e44f90c6dd98 Mon Sep 17 00:00:00 2001 From: Lucas DELANIER Date: Tue, 21 May 2024 11:36:49 +0200 Subject: [PATCH 12/15] =?UTF-8?q?Mise=20=C3=A0=20jour=20de=20'README.md'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 622fc3c..82c9bf4 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,6 @@ Le controller BET gère toutes les opérations liées aux paris. Il permet de r # Déploiement Le déploiement est réalisé sur Code First via les services **Drone** et **Runner** ! :rocket: - Lien de l'API sur codefirst : [API All In](https://codefirst.iut.uca.fr/containers/AllDev-api) From 9b66d1f01b826848da543c4f03fef46868705c92 Mon Sep 17 00:00:00 2001 From: Lucas DELANIER Date: Tue, 21 May 2024 11:38:21 +0200 Subject: [PATCH 13/15] add filter get bets (#11) Co-authored-by: ludelanier Co-authored-by: avalin Co-authored-by: Lucas EVARD Reviewed-on: https://codefirst.iut.uca.fr/git/AllDev/Api/pulls/11 Co-authored-by: Lucas DELANIER Co-committed-by: Lucas DELANIER --- README.md | 1 - Sources/src/main/kotlin/allin/Application.kt | 1 + .../main/kotlin/allin/data/BetDataSource.kt | 7 +--- .../allin/data/mock/MockBetDataSource.kt | 37 +++++++++++++++++- .../data/postgres/PostgresBetDataSource.kt | 39 +++++++++++++++++-- .../entities/BetResultNotificationEntity.kt | 4 +- .../src/main/kotlin/allin/model/BetFilter.kt | 15 +++++++ .../main/kotlin/allin/routing/betRouter.kt | 11 +++++- 8 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 Sources/src/main/kotlin/allin/model/BetFilter.kt diff --git a/README.md b/README.md index 82c9bf4..0e6f14f 100644 --- a/README.md +++ b/README.md @@ -89,5 +89,4 @@ Lien de l'API sur codefirst : [API All In](https://codefirst.iut.uca.fr/containe
© AllDev - API -
diff --git a/Sources/src/main/kotlin/allin/Application.kt b/Sources/src/main/kotlin/allin/Application.kt index dc29c5d..7b5a97d 100644 --- a/Sources/src/main/kotlin/allin/Application.kt +++ b/Sources/src/main/kotlin/allin/Application.kt @@ -88,6 +88,7 @@ private fun Application.extracted() { betDetailRouter() friendRouter() + kronJob(BET_VERIFY_DELAY) { dataSource.betDataSource.updateBetStatuses(ZonedDateTime.now()) } diff --git a/Sources/src/main/kotlin/allin/data/BetDataSource.kt b/Sources/src/main/kotlin/allin/data/BetDataSource.kt index 2e9ee1d..f0c1e03 100644 --- a/Sources/src/main/kotlin/allin/data/BetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/BetDataSource.kt @@ -1,13 +1,10 @@ package allin.data -import allin.model.Bet -import allin.model.BetDetail -import allin.model.BetResultDetail -import allin.model.UpdatedBetData +import allin.model.* import java.time.ZonedDateTime interface BetDataSource { - fun getAllBets(): List + fun getAllBets(filters: List): List fun getBetById(id: String): Bet? fun getBetDetailById(id: String, username: String): BetDetail? fun getBetsNotFinished(): List diff --git a/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt b/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt index 0efd3e5..7c522ea 100644 --- a/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt @@ -22,7 +22,42 @@ class MockBetDataSource(private val mockData: MockDataSource.MockData) : BetData private val betInfos get() = mockData.betInfos private val answerInfos get() = mockData.answerInfos - override fun getAllBets(): List = bets + override fun getAllBets(filters: List): List { + return when { + filters.isEmpty() -> bets + + filters.size == 1 -> { + val filter = filters[0] + + when (filter) { + BetFilter.PUBLIC -> bets.filter { !it.isPrivate } + BetFilter.INVITATION -> bets.filter { it.isPrivate } + BetFilter.FINISHED -> bets.filter { it.status == FINISHED } + BetFilter.IN_PROGRESS -> bets.filter { + it.status in listOf(IN_PROGRESS, WAITING, CLOSING) + } + }.map { it } + } + + else -> { + bets.filter { bet -> + val public = (BetFilter.PUBLIC in filters) && !bet.isPrivate + val invitation = (BetFilter.INVITATION in filters) && bet.isPrivate + val finished = + (BetFilter.FINISHED in filters) and ((bet.status == FINISHED) or (bet.status == CANCELLED)) + val inProgress = (BetFilter.IN_PROGRESS in filters) and (bet.status in listOf( + IN_PROGRESS, + WAITING, + CLOSING + )) + + (public || invitation) && (finished or inProgress) + }.map { it } + } + } + + } + override fun getBetById(id: String): Bet? = bets.find { it.id == id } diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt index d243f00..72c335d 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt @@ -11,8 +11,41 @@ import java.time.ZonedDateTime class PostgresBetDataSource(private val database: Database) : BetDataSource { - override fun getAllBets(): List = - database.bets.map { it.toBet(database) } + override fun getAllBets(filters: List): List { + return when { + filters.isEmpty() -> database.bets.map { it.toBet(database) } + + filters.size == 1 -> { + val filter = filters.first() + + when (filter) { + BetFilter.PUBLIC -> database.bets.filter { !it.isPrivate } + BetFilter.INVITATION -> database.bets.filter { it.isPrivate } + BetFilter.FINISHED -> database.bets.filter { it.status eq BetStatus.FINISHED } + BetFilter.IN_PROGRESS -> database.bets.filter { + it.status inList listOf(BetStatus.IN_PROGRESS, BetStatus.WAITING, BetStatus.CLOSING) + } + }.map { it.toBet(database) } + } + + else -> { + database.bets.filter { bet -> + val public = (BetFilter.PUBLIC in filters) and !bet.isPrivate + val invitation = (BetFilter.INVITATION in filters) and bet.isPrivate + val finished = + (BetFilter.FINISHED in filters) and ((bet.status eq BetStatus.FINISHED) or (bet.status eq BetStatus.CANCELLED)) + val inProgress = (BetFilter.IN_PROGRESS in filters) and (bet.status inList listOf( + BetStatus.IN_PROGRESS, + BetStatus.WAITING, + BetStatus.CLOSING + )) + + (public or invitation) and (finished or inProgress) + }.map { it.toBet(database) } + } + } + } + override fun getBetById(id: String): Bet? = database.bets.find { it.id eq id }?.toBet(database) @@ -21,7 +54,7 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { database.bets.find { it.id eq id }?.toBetDetail(database, username) override fun getBetsNotFinished(): List { - val currentTime = ZonedDateTime.now(ZoneId.of("Europe/Paris")) + val currentTime = ZonedDateTime.now(ZoneId.of("+02:00")) return database.bets .filter { it.endBet greaterEq currentTime.toInstant() } .map { it.toBet(database) } diff --git a/Sources/src/main/kotlin/allin/data/postgres/entities/BetResultNotificationEntity.kt b/Sources/src/main/kotlin/allin/data/postgres/entities/BetResultNotificationEntity.kt index 8ee4496..8662cca 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/entities/BetResultNotificationEntity.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/entities/BetResultNotificationEntity.kt @@ -14,8 +14,8 @@ interface BetResultNotificationEntity : Entity { } object BetResultNotificationsEntity : Table("betresultnotification") { - val betId = varchar("betid").primaryKey() - val username = varchar("username").primaryKey() + val betId = varchar("betid").primaryKey().bindTo { it.betId } + val username = varchar("username").primaryKey().bindTo { it.username } } val Database.betResultNotifications get() = this.sequenceOf(BetResultNotificationsEntity) \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/model/BetFilter.kt b/Sources/src/main/kotlin/allin/model/BetFilter.kt new file mode 100644 index 0000000..268d1bb --- /dev/null +++ b/Sources/src/main/kotlin/allin/model/BetFilter.kt @@ -0,0 +1,15 @@ +package allin.model + +import kotlinx.serialization.Serializable + +enum class BetFilter { + PUBLIC, + INVITATION, + IN_PROGRESS, + FINISHED +} + +@Serializable +data class BetFiltersRequest( + val filters: List +) \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/routing/betRouter.kt b/Sources/src/main/kotlin/allin/routing/betRouter.kt index f97727e..d235998 100644 --- a/Sources/src/main/kotlin/allin/routing/betRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/betRouter.kt @@ -61,10 +61,13 @@ fun Application.betRouter() { } } authenticate { - get("/bets/gets", { + post("/bets/gets", { description = "Allows you to recover all bets" request { headerParameter("JWT token of the logged user") + body> { + description = "List of filters" + } } response { HttpStatusCode.Accepted to { @@ -77,7 +80,11 @@ fun Application.betRouter() { }) { hasToken { principal -> verifyUserFromToken(userDataSource, principal) { _, _ -> - call.respond(HttpStatusCode.Accepted, betDataSource.getAllBets()) + val filtersRequest = + kotlin.runCatching { call.receiveNullable() }.getOrNull() + val filters = + filtersRequest?.filters ?: emptyList() // Use provided filters or empty list if null + call.respond(HttpStatusCode.Accepted, betDataSource.getAllBets(filters)) } } } From c64db61e257e8223dc0c6fe496af0e4598785832 Mon Sep 17 00:00:00 2001 From: Lucas EVARD Date: Wed, 22 May 2024 11:33:58 +0200 Subject: [PATCH 14/15] revert f82aba9a02aedc80d8384da261450d9f21ecfdef revert :sparkles: Refactor and add PopularityScore for Mocking Data Source --- .../main/kotlin/allin/data/postgres/PostgresBetDataSource.kt | 1 - .../src/main/kotlin/allin/data/postgres/entities/BetEntity.kt | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt index 72c335d..d19155e 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt @@ -174,7 +174,6 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource { this.createdBy = bet.createdBy this.status = bet.status this.type = bet.type - this.popularityscore = 0 } ) diff --git a/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt b/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt index 31949eb..8c0e964 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt @@ -41,8 +41,7 @@ interface BetEntity : Entity { } else { database.responses.filter { it.betId eq id }.map { it.response } }, - createdBy = createdBy, - popularityscore = popularityscore, + createdBy = createdBy ) fun toBetDetail(database: Database, username: String): BetDetail { From 7312bcab7783bd2ae5a8c2395f7267d5cf3fba4e Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Wed, 22 May 2024 11:41:31 +0200 Subject: [PATCH 15/15] :sparkles: Merge --- .../src/main/kotlin/allin/data/postgres/entities/BetEntity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt b/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt index 8c0e964..31949eb 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/entities/BetEntity.kt @@ -41,7 +41,8 @@ interface BetEntity : Entity { } else { database.responses.filter { it.betId eq id }.map { it.response } }, - createdBy = createdBy + createdBy = createdBy, + popularityscore = popularityscore, ) fun toBetDetail(database: Database, username: String): BetDetail {