diff --git a/.drone.yml b/.drone.yml
index d86e2e8..4b40ba2 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -52,7 +52,7 @@ steps:
IMAGENAME: postgres:latest
CONTAINERNAME: postgresapi
COMMAND: create
- OVERWRITE: false
+ OVERWRITE: true
PRIVATE: false
ADMINS: lucasevard,emrekartal,arthurvalin,lucasdelanier
CODEFIRST_CLIENTDRONE_ENV_POSTGRES_ROOT_PASSWORD:
diff --git a/.gitignore b/.gitignore
index b3f8ec6..d141f8b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,4 +35,6 @@ out/
### VS Code ###
.vscode/
-!**/src/target/**
\ No newline at end of file
+!**/src/target/**
+**/src/target/**
+/src/target
\ No newline at end of file
diff --git a/Sources/pom.xml b/Sources/pom.xml
index c569b14..af54282 100644
--- a/Sources/pom.xml
+++ b/Sources/pom.xml
@@ -47,6 +47,11 @@
ktorm-core
3.2.0
+
+ org.ktorm
+ ktorm-support-postgresql
+ 3.2.0
+
ch.qos.logback
logback-classic
diff --git a/Sources/src/main/kotlin/allin/Application.kt b/Sources/src/main/kotlin/allin/Application.kt
index 0de0d66..46ca90c 100644
--- a/Sources/src/main/kotlin/allin/Application.kt
+++ b/Sources/src/main/kotlin/allin/Application.kt
@@ -1,8 +1,11 @@
package allin
-import allin.entities.*
+import allin.data.AllInDataSource
+import allin.data.mock.MockDataSource
+import allin.data.postgres.PostgresDataSource
import allin.routing.*
-import allin.utils.*
+import allin.utils.TokenManager
+import allin.utils.kronJob
import com.typesafe.config.ConfigFactory
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
@@ -12,14 +15,23 @@ import io.ktor.server.config.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
-import org.ktorm.database.Database
+import java.time.ZonedDateTime
+import kotlin.time.ExperimentalTime
+import kotlin.time.minutes
-val db_database=System.getenv().get("POSTGRES_DB")
-val db_user=System.getenv().get("POSTGRES_USER")
-val db_password=System.getenv().get("POSTGRES_PASSWORD")
-val db_host=System.getenv().get("POSTGRES_HOST")
+@ExperimentalTime
+val BET_VERIFY_DELAY = 1.minutes
+
+val data_source = System.getenv()["DATA_SOURCE"]
+
+private val allInDataSource: AllInDataSource = when (data_source) {
+ "mock" -> MockDataSource()
+ "postgres" -> PostgresDataSource()
+ else -> MockDataSource()
+}
+val Application.dataSource: AllInDataSource
+ get() = allInDataSource
-val database = Database.connect("jdbc:postgresql://$db_host/$db_database", user = db_user, password = db_password)
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
@@ -27,6 +39,7 @@ fun main() {
}.start(wait = true)
}
+@OptIn(ExperimentalTime::class)
private fun Application.extracted() {
val config = HoconApplicationConfig(ConfigFactory.load())
val tokenManager = TokenManager.getInstance(config)
@@ -41,16 +54,15 @@ private fun Application.extracted() {
}
}
}
- install(ContentNegotiation) {
- json()
- }
+ install(ContentNegotiation) { json() }
+
BasicRouting()
UserRouter()
BetRouter()
ParticipationRouter()
BetDetailRouter()
- UsersEntity.createUserTable()
- BetsEntity.createBetsTable()
- ResponsesEntity.createResponseTable()
- ParticipationsEntity.createParticipationTable()
-}
+
+ kronJob(BET_VERIFY_DELAY) {
+ dataSource.betDataSource.updateBetStatuses(ZonedDateTime.now())
+ }
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/data/AllInDataSource.kt b/Sources/src/main/kotlin/allin/data/AllInDataSource.kt
new file mode 100644
index 0000000..f2c1406
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/AllInDataSource.kt
@@ -0,0 +1,12 @@
+package allin.data
+
+abstract class AllInDataSource {
+
+ abstract val userDataSource: UserDataSource
+
+
+ abstract val betDataSource: BetDataSource
+
+
+ abstract val participationDataSource: ParticipationDataSource
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/data/BetDataSource.kt b/Sources/src/main/kotlin/allin/data/BetDataSource.kt
new file mode 100644
index 0000000..cb543e0
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/BetDataSource.kt
@@ -0,0 +1,15 @@
+package allin.data
+
+import allin.model.Bet
+import allin.model.UpdatedBetData
+import java.time.ZonedDateTime
+
+interface BetDataSource {
+ fun getAllBets(): List
+ fun getBetById(id: String): Bet?
+ fun getBetsNotFinished(): List
+ fun addBet(bet: Bet)
+ fun removeBet(id: String): Boolean
+ fun updateBet(data: UpdatedBetData): Boolean
+ fun updateBetStatuses(date: ZonedDateTime)
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/data/ParticipationDataSource.kt b/Sources/src/main/kotlin/allin/data/ParticipationDataSource.kt
new file mode 100644
index 0000000..d0dfb09
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/ParticipationDataSource.kt
@@ -0,0 +1,18 @@
+package allin.data
+
+import allin.model.Participation
+
+interface ParticipationDataSource {
+
+
+ fun addParticipation(participation: Participation)
+
+
+ fun getParticipationFromBetId(betid: String): List
+
+
+ fun getParticipationFromUserId(username: String, betid: String): List
+
+
+ fun deleteParticipation(id: String): Boolean
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/data/UserDataSource.kt b/Sources/src/main/kotlin/allin/data/UserDataSource.kt
new file mode 100644
index 0000000..98c5eec
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/UserDataSource.kt
@@ -0,0 +1,21 @@
+package allin.data
+
+import allin.dto.UserDTO
+import allin.model.User
+
+interface UserDataSource {
+
+ fun getUserByUsername(username: String): Pair
+
+
+ fun addUser(user: User)
+
+
+ fun deleteUser(username: String): Boolean
+
+
+ fun modifyUserCoins(username: String, amount: Int)
+
+
+ fun userExists(username: String, email: String): Boolean
+}
\ 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
new file mode 100644
index 0000000..a2d04a0
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/mock/MockBetDataSource.kt
@@ -0,0 +1,44 @@
+package allin.data.mock
+
+import allin.data.BetDataSource
+import allin.model.Bet
+import allin.model.BetStatus
+import allin.model.UpdatedBetData
+import java.time.ZonedDateTime
+
+class MockBetDataSource : BetDataSource {
+ override fun getAllBets(): List = bets
+ override fun getBetById(id: String): Bet? =
+ bets.find { it.id == id }
+
+ override fun removeBet(id: String): Boolean =
+ bets.removeIf { it.id == id }
+
+ override fun updateBet(data: UpdatedBetData): Boolean {
+ return bets.find { it.id == data.id }?.let {
+ it.isPrivate = data.isPrivate
+ } != null
+ }
+
+ override fun getBetsNotFinished(): List =
+ bets.filter { it.endBet >= ZonedDateTime.now() }
+
+ override fun addBet(bet: Bet) {
+ bets += bet
+ }
+
+ override fun updateBetStatuses(date: ZonedDateTime) {
+ bets.forEachIndexed { idx, bet ->
+ if (date >= bet.endRegistration) {
+ if (date >= bet.endBet) {
+ bets[idx] = bet.copy(status = BetStatus.WAITING)
+ } else {
+ bets[idx] = bet.copy(status = BetStatus.CLOSING)
+ }
+ }
+ }
+ }
+
+ private val bets by lazy { mutableListOf() }
+
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/data/mock/MockDataSource.kt b/Sources/src/main/kotlin/allin/data/mock/MockDataSource.kt
new file mode 100644
index 0000000..1cd0b9d
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/mock/MockDataSource.kt
@@ -0,0 +1,12 @@
+package allin.data.mock
+
+import allin.data.AllInDataSource
+import allin.data.BetDataSource
+import allin.data.ParticipationDataSource
+import allin.data.UserDataSource
+
+class MockDataSource : AllInDataSource() {
+ override val userDataSource: UserDataSource = MockUserDataSource()
+ override val betDataSource: BetDataSource = MockBetDataSource()
+ override val participationDataSource: ParticipationDataSource = MockParticipationDataSource()
+}
diff --git a/Sources/src/main/kotlin/allin/data/mock/MockParticipationDataSource.kt b/Sources/src/main/kotlin/allin/data/mock/MockParticipationDataSource.kt
new file mode 100644
index 0000000..c9f22af
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/mock/MockParticipationDataSource.kt
@@ -0,0 +1,22 @@
+package allin.data.mock
+
+import allin.data.ParticipationDataSource
+import allin.model.Participation
+
+class MockParticipationDataSource : ParticipationDataSource {
+ override fun addParticipation(participation: Participation) {
+ participations += participations
+ }
+
+ override fun getParticipationFromBetId(betid: String): List =
+ participations.filter { it.betId == betid }
+
+ override fun getParticipationFromUserId(username: String, betid: String): List =
+ participations.filter { it.betId == betid && it.username == username }
+
+ override fun deleteParticipation(id: String): Boolean =
+ participations.removeIf { it.id == id }
+
+ private val participations by lazy { mutableListOf() }
+
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/data/mock/MockUserDataSource.kt b/Sources/src/main/kotlin/allin/data/mock/MockUserDataSource.kt
new file mode 100644
index 0000000..9a92856
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/mock/MockUserDataSource.kt
@@ -0,0 +1,41 @@
+package allin.data.mock
+
+import allin.data.UserDataSource
+import allin.dto.UserDTO
+import allin.model.User
+
+class MockUserDataSource : UserDataSource {
+ override fun getUserByUsername(username: String): Pair =
+ users.find { it.username == username }?.let {
+ Pair(
+ UserDTO(
+ id = it.id,
+ username = it.username,
+ email = it.email,
+ nbCoins = it.nbCoins,
+ token = it.token
+ ),
+ it.password
+ )
+ } ?: Pair(null, null)
+
+ override fun addUser(user: User) {
+ users += user
+ }
+
+ override fun deleteUser(username: String): Boolean =
+ users.removeIf { it.username == username }
+
+ override fun modifyUserCoins(username: String, amount: Int) {
+ users.find { it.username == username }?.let {
+ it.nbCoins += amount
+ }
+ }
+
+ override fun userExists(username: String, email: String): Boolean =
+ users.any { it.username == username && it.email == email }
+
+ private val users by lazy {
+ mutableListOf()
+ }
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt
new file mode 100644
index 0000000..4c50b09
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresBetDataSource.kt
@@ -0,0 +1,115 @@
+package allin.data.postgres
+
+import allin.data.BetDataSource
+import allin.entities.BetsEntity
+import allin.entities.NO_VALUE
+import allin.entities.ResponsesEntity
+import allin.entities.ResponsesEntity.response
+import allin.entities.YES_VALUE
+import allin.model.Bet
+import allin.model.BetStatus
+import allin.model.BetType
+import allin.model.UpdatedBetData
+import org.ktorm.database.Database
+import org.ktorm.dsl.*
+import java.time.ZoneId
+import java.time.ZonedDateTime
+import java.util.*
+
+class PostgresBetDataSource(private val database: Database) : BetDataSource {
+
+ private fun QueryRowSet.toBet() =
+ Bet(
+ id = this[BetsEntity.id].toString(),
+ theme = this[BetsEntity.theme].toString(),
+ sentenceBet = this[BetsEntity.sentenceBet].toString(),
+ endRegistration = this[BetsEntity.endRegistration]!!.atZone(ZoneId.of("Europe/Paris")),
+ endBet = this[BetsEntity.endBet]!!.atZone(ZoneId.of("Europe/Paris")),
+ isPrivate = this[BetsEntity.isPrivate] ?: false,
+ status = this[BetsEntity.status] ?: BetStatus.IN_PROGRESS,
+ type = this[BetsEntity.type] ?: BetType.CUSTOM,
+ createdBy = this[BetsEntity.createdBy].toString(),
+ response = let {
+ val idBet = this[BetsEntity.id].toString()
+ val type = this[BetsEntity.type] ?: BetType.CUSTOM
+ if (type == BetType.CUSTOM) {
+ database.from(ResponsesEntity)
+ .select(response)
+ .where { ResponsesEntity.id eq UUID.fromString(idBet) }
+ .map { it[response].toString() }
+ } else {
+ listOf(YES_VALUE, NO_VALUE)
+ }
+ }
+ )
+
+ private fun Query.mapToBet() = this.map { it.toBet() }
+
+ override fun getAllBets(): List =
+ database.from(BetsEntity).select().mapToBet()
+
+ override fun getBetById(id: String): Bet? =
+ database.from(BetsEntity).select().where {
+ BetsEntity.id eq UUID.fromString(id)
+ }.mapToBet().firstOrNull()
+
+ override fun getBetsNotFinished(): List {
+ val currentTime = ZonedDateTime.now(ZoneId.of("Europe/Paris"))
+ return database.from(BetsEntity)
+ .select()
+ .where { BetsEntity.endBet greaterEq currentTime.toInstant() }
+ .mapToBet()
+ }
+
+ override fun addBet(bet: Bet) {
+ database.insert(BetsEntity) {
+ set(it.id, UUID.fromString(bet.id))
+ set(it.endBet, bet.endBet.toInstant())
+ set(it.endRegistration, bet.endRegistration.toInstant())
+ set(it.sentenceBet, bet.sentenceBet)
+ set(it.theme, bet.theme)
+ set(it.isPrivate, bet.isPrivate)
+ set(it.createdBy, bet.createdBy)
+ set(it.status, bet.status)
+ set(it.type, bet.type)
+ }
+
+ if (bet.type == BetType.CUSTOM) {
+ bet.response.forEach { selected ->
+ database.insert(ResponsesEntity) {
+ set(it.id, UUID.fromString(bet.id))
+ set(it.response, selected)
+ }
+ }
+ }
+ }
+
+ override fun removeBet(id: String): Boolean {
+ return database.delete(BetsEntity) { it.id eq UUID.fromString(id) } > 0
+ }
+
+ override fun updateBet(data: UpdatedBetData): Boolean {
+ return database.update(BetsEntity) {
+ set(BetsEntity.isPrivate, data.isPrivate)
+ where { BetsEntity.id eq UUID.fromString(data.id) }
+ } > 0
+ }
+
+ override fun updateBetStatuses(date: ZonedDateTime) {
+ database.update(BetsEntity) {
+ set(BetsEntity.status, BetStatus.WAITING)
+ where {
+ (date.toInstant() greaterEq BetsEntity.endRegistration) and
+ (date.toInstant() less BetsEntity.endBet)
+ }
+ }
+
+ database.update(BetsEntity) {
+ set(BetsEntity.status, BetStatus.CLOSING)
+ where {
+ (date.toInstant() greaterEq BetsEntity.endRegistration) and
+ (date.toInstant() greaterEq BetsEntity.endBet)
+ }
+ }
+ }
+}
\ 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
new file mode 100644
index 0000000..fdc5e73
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresDataSource.kt
@@ -0,0 +1,82 @@
+package allin.data.postgres
+
+import allin.data.AllInDataSource
+import allin.data.BetDataSource
+import allin.data.ParticipationDataSource
+import allin.data.UserDataSource
+import allin.utils.Execute
+import org.ktorm.database.Database
+
+class PostgresDataSource : AllInDataSource() {
+
+ private val database: Database
+
+ init {
+ val dbDatabase = System.getenv()["POSTGRES_DB"]
+ val dbUser = System.getenv()["POSTGRES_USER"]
+ val dbPassword = System.getenv()["POSTGRES_PASSWORD"]
+ val dbHost = System.getenv()["POSTGRES_HOST"]
+
+ database = Database.connect(
+ url = "jdbc:postgresql://$dbHost/$dbDatabase",
+ user = dbUser,
+ password = dbPassword
+ )
+
+ database.Execute(
+ """
+ CREATE TABLE IF not exists utilisateur (
+ id uuid PRIMARY KEY,
+ username VARCHAR(255),
+ password VARCHAR(255),
+ coins double precision,
+ email VARCHAR(255)
+ )""".trimIndent()
+ )
+
+ database.Execute(
+ """
+ CREATE TYPE betstatus AS ENUM
+ ('InProgress', 'Waiting', 'Closing', 'Finished', 'Cancelled');
+
+ CREATE TYPE bettype AS ENUM
+ ('Match', 'Binary', 'Custom');
+
+ CREATE TABLE IF not exists bet (
+ id uuid PRIMARY KEY,
+ theme VARCHAR(255),
+ endregistration timestamp,
+ endbet timestamp,
+ sentencebet varchar(500),
+ isprivate boolean,
+ createdby varchar(250),
+ status varchar(20),
+ type varchar(20)
+ )""".trimIndent()
+ )
+
+ database.Execute(
+ """
+ CREATE TABLE IF NOT EXISTS participation (
+ id uuid PRIMARY KEY,
+ bet uuid,
+ username varchar(250),
+ answer varchar(250),
+ stake int
+ )""".trimIndent()
+ )
+
+ database.Execute(
+ """
+ CREATE TABLE IF NOT EXISTS response (
+ id UUID,
+ response VARCHAR(250),
+ CONSTRAINT pk_response_id PRIMARY KEY (id,response)
+ )""".trimIndent()
+ )
+ }
+
+ override val userDataSource: UserDataSource = PostgresUserDataSource(database)
+ override val betDataSource: BetDataSource = PostgresBetDataSource(database)
+ override val participationDataSource: ParticipationDataSource = PostgresParticipationDataSource(database)
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresParticipationDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresParticipationDataSource.kt
new file mode 100644
index 0000000..ad50c6e
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresParticipationDataSource.kt
@@ -0,0 +1,57 @@
+package allin.data.postgres
+
+import allin.data.ParticipationDataSource
+import allin.entities.ParticipationsEntity
+import allin.model.Participation
+import org.ktorm.database.Database
+import org.ktorm.dsl.*
+import java.util.*
+
+class PostgresParticipationDataSource(private val database: Database) : ParticipationDataSource {
+
+ private fun QueryRowSet.toParticipation() =
+ Participation(
+ id = this[ParticipationsEntity.id].toString(),
+ betId = this[ParticipationsEntity.betId].toString(),
+ username = this[ParticipationsEntity.username].toString(),
+ answer = this[ParticipationsEntity.answer].toString(),
+ stake = this[ParticipationsEntity.stake] ?: 0,
+ )
+
+ private fun Query.mapToParticipation() = this.map { it.toParticipation() }
+
+ override fun addParticipation(participation: Participation) {
+ database.insert(ParticipationsEntity) {
+ set(it.id, UUID.fromString(participation.id))
+ set(it.betId, UUID.fromString(participation.betId))
+ set(it.username, participation.username)
+ set(it.answer, participation.answer)
+ set(it.stake, participation.stake)
+ }
+ }
+
+ override fun getParticipationFromBetId(betid: String): List {
+ return database.from(ParticipationsEntity)
+ .select()
+ .where { ParticipationsEntity.betId eq UUID.fromString(betid) }
+ .mapToParticipation()
+ }
+
+ override fun getParticipationFromUserId(username: String, betid: String): List {
+ return database.from(ParticipationsEntity)
+ .select()
+ .where { (ParticipationsEntity.betId eq UUID.fromString(betid)) and (ParticipationsEntity.username eq username) }
+ .mapToParticipation()
+ }
+
+ fun getParticipationEntity(): List {
+ return database.from(ParticipationsEntity).select().mapToParticipation()
+ }
+
+ override fun deleteParticipation(id: String): Boolean {
+ return database.delete(ParticipationsEntity) {
+ it.id eq UUID.fromString(id)
+ } > 0
+ }
+
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresUserDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresUserDataSource.kt
new file mode 100644
index 0000000..65124cf
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresUserDataSource.kt
@@ -0,0 +1,59 @@
+package allin.data.postgres
+
+import allin.data.UserDataSource
+import allin.dto.UserDTO
+import allin.entities.UsersEntity
+import allin.model.User
+import org.ktorm.database.Database
+import org.ktorm.dsl.*
+import java.util.*
+
+class PostgresUserDataSource(private val database: Database) : UserDataSource {
+ override fun getUserByUsername(username: String): Pair =
+ database.from(UsersEntity)
+ .select()
+ .where { UsersEntity.username eq username }
+ .map { row ->
+ Pair(
+ UserDTO(
+ row[UsersEntity.id].toString(),
+ row[UsersEntity.username].toString(),
+ row[UsersEntity.email].toString(),
+ row[UsersEntity.nbCoins] ?: 0,
+ null
+ ),
+ row[UsersEntity.password].toString()
+ )
+ }
+ .firstOrNull() ?: Pair(null, null)
+
+ override fun addUser(user: User) {
+ database.insert(UsersEntity) {
+ set(it.id, UUID.fromString(user.id))
+ set(it.nbCoins, user.nbCoins)
+ set(it.username, user.username)
+ set(it.password, user.password)
+ set(it.email, user.email)
+ }
+ }
+
+ override fun deleteUser(username: String): Boolean {
+ val deletedCount = database.delete(UsersEntity) {
+ it.username eq username
+ }
+ return deletedCount > 0
+ }
+
+ override fun userExists(username: String, email: String): Boolean {
+ return database.from(UsersEntity).select(UsersEntity.username, UsersEntity.email).where {
+ (UsersEntity.username eq username) and (UsersEntity.email eq email)
+ }.totalRecords > 0
+ }
+
+ override fun modifyUserCoins(username: String, amount: Int) {
+ database.update(UsersEntity) {
+ set(UsersEntity.nbCoins, UsersEntity.nbCoins - amount)
+ where { UsersEntity.username eq username }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/entities/BetEntity.kt b/Sources/src/main/kotlin/allin/entities/BetEntity.kt
index 264276d..0830ab8 100644
--- a/Sources/src/main/kotlin/allin/entities/BetEntity.kt
+++ b/Sources/src/main/kotlin/allin/entities/BetEntity.kt
@@ -1,15 +1,11 @@
package allin.entities
-import allin.database
-import allin.entities.ResponsesEntity.getResponse
-import allin.model.Bet
-import allin.utils.Execute
-import org.ktorm.dsl.*
+import allin.model.BetStatus
+import allin.model.BetType
import org.ktorm.entity.Entity
import org.ktorm.schema.*
-import java.time.ZoneId
+import org.ktorm.support.postgresql.pgEnum
import java.time.ZonedDateTime
-import java.util.UUID.fromString
interface BetEntity : Entity {
@@ -18,6 +14,8 @@ interface BetEntity : Entity {
val endRegistration: ZonedDateTime
val endBet: ZonedDateTime
val isPrivate: Boolean
+ val status: BetStatus
+ val type: BetType
val createdBy: String
}
@@ -28,57 +26,7 @@ object BetsEntity : Table("bet") {
val endRegistration = timestamp("endregistration")
val endBet = timestamp("endbet")
val isPrivate = boolean("isprivate")
+ val status = pgEnum("status").bindTo { it.status }
+ val type = pgEnum("type").bindTo { it.type }
val createdBy = varchar("createdby")
-
- fun getBets(): MutableList {
- return database.from(BetsEntity).select().map {
- row -> Bet(
- row[id].toString(),
- row[theme].toString(),
- row[sentenceBet].toString(),
- row[endRegistration]!!.atZone(ZoneId.of("Europe/Paris")),
- row[endBet]!!.atZone(ZoneId.of("Europe/Paris")),
- row[isPrivate]?: false,
- getResponse(fromString(row[id].toString())),
- row[createdBy].toString()
- )
- }.toMutableList()
- }
-
- fun getBetsNotFinished(): MutableList {
- val currentTime = ZonedDateTime.now(ZoneId.of("Europe/Paris"))
- return database.from(BetsEntity)
- .select()
- .where { endBet greaterEq currentTime.toInstant() }
- .map { row ->
- Bet(
- row[id].toString(),
- row[theme].toString(),
- row[sentenceBet].toString(),
- row[endRegistration]!!.atZone(ZoneId.of("Europe/Paris")),
- row[endBet]!!.atZone(ZoneId.of("Europe/Paris")),
- row[isPrivate] ?: false,
- getResponse(fromString(row[id].toString())),
- row[createdBy].toString()
- )
- }.toMutableList()
- }
-
- fun createBetsTable(){
- val request="CREATE TABLE IF not exists bet ( id uuid PRIMARY KEY, theme VARCHAR(255), endregistration timestamp,endbet timestamp,sentencebet varchar(500),isprivate boolean, createdby varchar(250))"
- database.Execute(request)
- }
-
- fun addBetEntity(bet : Bet) {
- database.insert(BetsEntity) {
- set(it.id, fromString(bet.id))
- set(it.endBet,bet.endBet.toInstant())
- set(it.endRegistration,bet.endRegistration.toInstant())
- set(it.sentenceBet,bet.sentenceBet)
- set(it.theme, bet.theme)
- set(it.isPrivate, bet.isPrivate)
- set(it.createdBy, bet.createdBy)
- }
- ResponsesEntity.addResponse(bet.response,fromString(bet.id))
- }
}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/entities/ParticipationEntity.kt b/Sources/src/main/kotlin/allin/entities/ParticipationEntity.kt
index 5684f8f..231d620 100644
--- a/Sources/src/main/kotlin/allin/entities/ParticipationEntity.kt
+++ b/Sources/src/main/kotlin/allin/entities/ParticipationEntity.kt
@@ -1,12 +1,10 @@
package allin.entities
-import allin.database
-import allin.model.Participation
-import allin.utils.Execute
-import org.ktorm.dsl.*
import org.ktorm.entity.Entity
-import org.ktorm.schema.*
-import java.util.*
+import org.ktorm.schema.Table
+import org.ktorm.schema.int
+import org.ktorm.schema.uuid
+import org.ktorm.schema.varchar
interface ParticipationEntity : Entity {
val id: String
@@ -23,68 +21,4 @@ object ParticipationsEntity : Table("participation") {
val username = varchar("username")
val answer = varchar("answer")
val stake = int("stake")
-
- fun createParticipationTable(){
- val request="CREATE TABLE IF NOT EXISTS participation (id uuid PRIMARY KEY,bet uuid,username varchar(250),answer varchar(250),stake int);"
- database.Execute(request)
- }
-
- fun addParticipationEntity(participation : Participation){
- database.insert(ParticipationsEntity){
- set(it.id, UUID.fromString(participation.id))
- set(it.betId,UUID.fromString(participation.betId))
- set(it.username,participation.username)
- set(it.answer,participation.answer)
- set(it.stake,participation.stake)
- }
- }
-
- fun getParticipationEntityFromBetId(betid: String): MutableList {
- return database.from(ParticipationsEntity)
- .select()
- .where { betId eq UUID.fromString(betid) }
- .map { row ->
- Participation(
- row[id].toString(),
- row[betId].toString(),
- row[username].toString(),
- row[answer].toString(),
- row[stake] ?: 0,
- )
- }.toMutableList()
- }
-
- fun getParticipationEntityFromUserId(user: String, betid: String): MutableList {
- return database.from(ParticipationsEntity)
- .select()
- .where { (betId eq UUID.fromString(betid)) and (username eq user) }
- .map { row ->
- Participation(
- row[id].toString(),
- row[betId].toString(),
- row[username].toString(),
- row[answer].toString(),
- row[stake] ?: 0,
- )
- }.toMutableList()
- }
-
- fun getParticipationEntity(): MutableList {
- return database.from(ParticipationsEntity).select().map {
- row -> Participation(
- row[id].toString(),
- row[betId].toString(),
- row[username].toString(),
- row[answer].toString(),
- row[stake]?:0,
- )
- }.toMutableList()
- }
-
- fun deleteParticipation(participation: Participation): Boolean {
- val deletedCount = database.delete(ParticipationsEntity) {
- it.id eq UUID.fromString(participation.id)
- }
- return deletedCount > 0
- }
}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/entities/ResponseEntity.kt b/Sources/src/main/kotlin/allin/entities/ResponseEntity.kt
index c029a5e..5da1b16 100644
--- a/Sources/src/main/kotlin/allin/entities/ResponseEntity.kt
+++ b/Sources/src/main/kotlin/allin/entities/ResponseEntity.kt
@@ -1,8 +1,5 @@
package allin.entities
-import allin.database
-import allin.utils.Execute
-import org.ktorm.dsl.*
import org.ktorm.entity.Entity
import org.ktorm.schema.Table
import org.ktorm.schema.uuid
@@ -10,6 +7,9 @@ import org.ktorm.schema.varchar
import java.util.*
+const val YES_VALUE = "Yes"
+const val NO_VALUE = "No"
+
interface ResponseEntity : Entity {
val betId: UUID
val response: String
@@ -18,24 +18,4 @@ interface ResponseEntity : Entity {
object ResponsesEntity : Table("response") {
val id = uuid("id").primaryKey()
val response = varchar("response").primaryKey()
- fun createResponseTable(){
- val request="CREATE TABLE IF NOT EXISTS response (id UUID,response VARCHAR(250),CONSTRAINT pk_response_id PRIMARY KEY (id,response));"
- database.Execute(request)
- }
-
- fun getResponse(idBet: UUID): MutableList {
- return database.from(ResponsesEntity)
- .select(response)
- .where { id eq idBet }
- .map { it[response].toString() }.toMutableList()
- }
-
- fun addResponse(responses : MutableList, idBet : UUID ) {
- responses.forEach {selected ->
- database.insert(ResponsesEntity) {
- set(it.id, idBet)
- set(it.response,selected)
- }
- }
- }
}
diff --git a/Sources/src/main/kotlin/allin/entities/UserEntity.kt b/Sources/src/main/kotlin/allin/entities/UserEntity.kt
index 1bb82ea..907b361 100644
--- a/Sources/src/main/kotlin/allin/entities/UserEntity.kt
+++ b/Sources/src/main/kotlin/allin/entities/UserEntity.kt
@@ -1,16 +1,13 @@
package allin.entities
import allin.database
-import allin.dto.UserDTO
-import allin.model.User
-import allin.utils.Execute
import allin.utils.ExecuteWithResult
import org.ktorm.database.use
-import org.ktorm.dsl.*
-import org.ktorm.entity.*
+import org.ktorm.dsl.eq
+import org.ktorm.dsl.update
+import org.ktorm.entity.Entity
import org.ktorm.schema.*
-import java.time.Instant.now
-import java.util.UUID.fromString
+import java.time.Instant
interface UserEntity : Entity {
val username: String
@@ -18,6 +15,7 @@ interface UserEntity : Entity {
var password: String
var nbCoins: Int
}
+
object UsersEntity : Table("utilisateur") {
val id = uuid("id").primaryKey()
val username = varchar("username")
@@ -25,93 +23,28 @@ object UsersEntity : Table("utilisateur") {
val nbCoins = int("coins")
val email = varchar("email")
val lastGift = timestamp("lastgift")
+}
- fun getUserToUserDTO(): MutableList {
- return database.from(UsersEntity).select().map {
- row -> UserDTO(
- row[id].toString(),
- row[username].toString(),
- row[email].toString(),
- row[nbCoins]?:0,
- null
- )
- }.toMutableList()
- }
-
- fun createUserTable(){
- val request="CREATE TABLE IF not exists utilisateur ( id uuid PRIMARY KEY, username VARCHAR(255), password VARCHAR(255),coins numeric,email VARCHAR(255), lastgift timestamp)"
- database.Execute(request)
- }
-
- fun modifyCoins(user: String, cost : Int){
- val request = "UPDATE utilisateur SET coins = coins - $cost WHERE username = '$user';"
- database.Execute(request)
- }
-
- fun addCoinByUsername(username: String, coins : Int){
- database.update(UsersEntity){
- set(nbCoins,coins+ nbCoins)
- where {it.username eq username}
- }
- }
-
- fun getUserByUsernameAndPassword(login: String): Pair {
- return database.from(UsersEntity)
- .select()
- .where { (username eq login) /*and (password eq passwordParam)*/ }
- .map { row ->
- Pair(
- UserDTO(
- row[id].toString(),
- row[username].toString(),
- row[email].toString(),
- row[nbCoins] ?: 0,
- null
- ),
- row[password].toString()
- )
- }
- .firstOrNull() ?: Pair(null, null)
- }
-
- fun addUserEntity(user : User){
- database.insert(UsersEntity){
- set(it.id,fromString(user.id))
- set(it.nbCoins,user.nbCoins)
- set(it.username,user.username)
- set(it.password,user.password)
- set(it.email,user.email)
- set(it.lastGift,now())
- }
- }
- fun deleteUserByUsername(username: String): Boolean {
- val deletedCount = database.delete(UsersEntity) {
- it.username eq username
- }
- return deletedCount > 0
- }
-
- fun canHaveDailyGift(username: String): Boolean {
- val request = "SELECT CASE WHEN NOW() - lastgift > INTERVAL '1 day' THEN true ELSE false END AS is_lastgift_greater_than_1_day FROM utilisateur WHERE username = '$username';"
- val resultSet = database.ExecuteWithResult(request)
+/*CREATE TABLE IF not exists utilisateur ( id uuid PRIMARY KEY, username VARCHAR(255), password VARCHAR(255),coins numeric,email VARCHAR(255), lastgift timestamp)*/
+/*
+fun canHaveDailyGift(username: String): Boolean {
+ val request = "SELECT CASE WHEN NOW() - lastgift > INTERVAL '1 day' THEN true ELSE false END AS is_lastgift_greater_than_1_day FROM utilisateur WHERE username = '$username';"
+ val resultSet = database.ExecuteWithResult(request)
- resultSet?.use {
- if (resultSet.next()) {
- val isDailyGift = resultSet.getBoolean("is_lastgift_greater_than_1_day")
- if (isDailyGift) {
- database.update(UsersEntity) {
- set(lastGift, now())
- where { it.username eq username }
- }
+ resultSet?.use {
+ if (resultSet.next()) {
+ val isDailyGift = resultSet.getBoolean("is_lastgift_greater_than_1_day")
+ if (isDailyGift) {
+ database.update(UsersEntity) {
+ set(UsersEntity.lastGift, Instant.now())
+ where { it.username eq username }
}
- return isDailyGift
}
+ return isDailyGift
}
- return false
}
-
+ return false
}
-
-
+*/
diff --git a/Sources/src/main/kotlin/allin/ext/PipelineContextExt.kt b/Sources/src/main/kotlin/allin/ext/PipelineContextExt.kt
index e111f9b..0d5db8c 100644
--- a/Sources/src/main/kotlin/allin/ext/PipelineContextExt.kt
+++ b/Sources/src/main/kotlin/allin/ext/PipelineContextExt.kt
@@ -1,7 +1,7 @@
package allin.ext
+import allin.data.UserDataSource
import allin.dto.UserDTO
-import allin.entities.UsersEntity
import allin.model.ApiMessage
import io.ktor.http.*
import io.ktor.server.application.*
@@ -10,15 +10,18 @@ import io.ktor.server.auth.jwt.*
import io.ktor.server.response.*
import io.ktor.util.pipeline.*
+
suspend fun PipelineContext<*, ApplicationCall>.hasToken(content: suspend (principal: JWTPrincipal) -> Unit) =
call.principal()?.let { content(it) } ?: call.respond(HttpStatusCode.Unauthorized)
+
suspend fun PipelineContext<*, ApplicationCall>.verifyUserFromToken(
+ userDataSource: UserDataSource,
principal: JWTPrincipal,
content: suspend (user: UserDTO, password: String) -> Unit
) {
val username = principal.payload.getClaim("username").asString()
- val userPassword = UsersEntity.getUserByUsernameAndPassword(username)
+ val userPassword = userDataSource.getUserByUsername(username)
userPassword.first?.let { content(it, userPassword.second ?: "") }
?: call.respond(HttpStatusCode.NotFound, ApiMessage.TokenUserNotFound)
}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/model/Bet.kt b/Sources/src/main/kotlin/allin/model/Bet.kt
index e088e2b..c27a2f5 100644
--- a/Sources/src/main/kotlin/allin/model/Bet.kt
+++ b/Sources/src/main/kotlin/allin/model/Bet.kt
@@ -1,20 +1,21 @@
package allin.model
-import allin.serializer.UUIDSerializer
+import allin.model.BetStatus.IN_PROGRESS
import allin.serializer.ZonedDateTimeSerializer
import kotlinx.serialization.Serializable
import java.time.ZonedDateTime
-import java.util.*
@Serializable
data class Bet(
val id: String = "",
val theme: String,
val sentenceBet: String,
+ val status: BetStatus = IN_PROGRESS,
+ val type: BetType,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean,
- var response: MutableList,
+ var response: List,
val createdBy: String = ""
)
@@ -23,5 +24,5 @@ data class UpdatedBetData(
val id: String,
@Serializable(ZonedDateTimeSerializer::class) val endBet: ZonedDateTime,
val isPrivate: Boolean,
- val response: MutableList
+ val response: List
)
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/model/BetStatus.kt b/Sources/src/main/kotlin/allin/model/BetStatus.kt
new file mode 100644
index 0000000..ce29e1b
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/model/BetStatus.kt
@@ -0,0 +1,9 @@
+package allin.model
+
+enum class BetStatus {
+ IN_PROGRESS,
+ WAITING,
+ CLOSING,
+ FINISHED,
+ CANCELLED
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/model/BetType.kt b/Sources/src/main/kotlin/allin/model/BetType.kt
new file mode 100644
index 0000000..a6a39c3
--- /dev/null
+++ b/Sources/src/main/kotlin/allin/model/BetType.kt
@@ -0,0 +1,7 @@
+package allin.model
+
+enum class BetType {
+ MATCH,
+ BINARY,
+ CUSTOM
+}
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/routing/BasicRouting.kt b/Sources/src/main/kotlin/allin/routing/BasicRouting.kt
index 0d78278..7bebf88 100644
--- a/Sources/src/main/kotlin/allin/routing/BasicRouting.kt
+++ b/Sources/src/main/kotlin/allin/routing/BasicRouting.kt
@@ -4,7 +4,8 @@ import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
-fun Application.BasicRouting(){
+
+fun Application.BasicRouting() {
routing {
get("/") {
call.respond("Bienvenue sur l'API de AlLin!")
diff --git a/Sources/src/main/kotlin/allin/routing/BetDetailRouter.kt b/Sources/src/main/kotlin/allin/routing/BetDetailRouter.kt
index 8596f34..a8d9243 100644
--- a/Sources/src/main/kotlin/allin/routing/BetDetailRouter.kt
+++ b/Sources/src/main/kotlin/allin/routing/BetDetailRouter.kt
@@ -1,8 +1,6 @@
package allin.routing
-import allin.entities.BetsEntity.getBets
-import allin.entities.ParticipationsEntity.getParticipationEntityFromBetId
-import allin.entities.ParticipationsEntity.getParticipationEntityFromUserId
+import allin.dataSource
import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.BetDetail
@@ -13,15 +11,20 @@ import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
+
fun Application.BetDetailRouter() {
+ val userDataSource = this.dataSource.userDataSource
+ val betDataSource = this.dataSource.betDataSource
+ val participationDataSource = this.dataSource.participationDataSource
+
routing {
authenticate {
get("/betdetail/get/{id}") {
hasToken { principal ->
- verifyUserFromToken(principal) { user, _ ->
+ verifyUserFromToken(userDataSource, principal) { user, _ ->
val id = call.parameters["id"].toString()
- val participations = getParticipationEntityFromBetId(id)
- val selectedBet = getBets().find { it.id == id }
+ val participations = participationDataSource.getParticipationFromBetId(id)
+ val selectedBet = betDataSource.getBetById(id)
if (selectedBet != null) {
call.respond(
HttpStatusCode.Accepted,
@@ -29,7 +32,7 @@ fun Application.BetDetailRouter() {
selectedBet,
getBetAnswerDetail(selectedBet, participations),
participations.toList(),
- getParticipationEntityFromUserId(user.username, id).lastOrNull()
+ participationDataSource.getParticipationFromUserId(user.username, id).lastOrNull()
)
)
} else {
diff --git a/Sources/src/main/kotlin/allin/routing/BetRouter.kt b/Sources/src/main/kotlin/allin/routing/BetRouter.kt
index 6d3e3fc..26d2206 100644
--- a/Sources/src/main/kotlin/allin/routing/BetRouter.kt
+++ b/Sources/src/main/kotlin/allin/routing/BetRouter.kt
@@ -1,10 +1,6 @@
package allin.routing
-import allin.entities.BetsEntity.addBetEntity
-import allin.entities.BetsEntity.getBets
-import allin.entities.BetsEntity.getBetsNotFinished
-import allin.entities.ParticipationsEntity.getParticipationEntity
-import allin.entities.ParticipationsEntity.getParticipationEntityFromUserId
+import allin.dataSource
import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.ApiMessage
@@ -21,35 +17,30 @@ import java.util.*
val tokenManagerBet = AppConfig.tokenManager
+
fun Application.BetRouter() {
+ val userDataSource = this.dataSource.userDataSource
+ val betDataSource = this.dataSource.betDataSource
+ val participationDataSource = this.dataSource.participationDataSource
+
routing {
route("/bets/add") {
authenticate {
- post {
- hasToken { principal ->
- val bet = call.receive()
- val id = UUID.randomUUID().toString()
- val username = tokenManagerBet.getUsernameFromToken(principal)
- val bets = getBets()
- bets.find { it.id == id }?.let {
- call.respond(HttpStatusCode.Conflict, ApiMessage.BetAlreadyExist)
- } ?: run {
- val betWithId = Bet(
- id,
- bet.theme,
- bet.sentenceBet,
- bet.endRegistration,
- bet.endBet,
- bet.isPrivate,
- bet.response,
- username
- )
- addBetEntity(betWithId)
- call.respond(HttpStatusCode.Created, betWithId)
- }
- }
+ post {
+ hasToken { principal ->
+ val bet = call.receive()
+ val id = UUID.randomUUID().toString()
+ val username = tokenManagerBet.getUsernameFromToken(principal)
+ betDataSource.getBetById(id)?.let {
+ call.respond(HttpStatusCode.Conflict, ApiMessage.BetAlreadyExist)
+ } ?: run {
+ val betWithId = bet.copy(id = id, createdBy = username)
+ betDataSource.addBet(betWithId)
+ call.respond(HttpStatusCode.Created, betWithId)
+ }
+ }
- }
+ }
}
}
@@ -57,17 +48,15 @@ fun Application.BetRouter() {
route("/bets/gets") {
get {
// if(bets.size>0)
- val bets= getBets()
- call.respond(HttpStatusCode.Accepted, bets.toList())
+ call.respond(HttpStatusCode.Accepted, betDataSource.getAllBets())
// else call.respond(HttpStatusCode.NoContent)
}
}
route("/bets/get/{id}") {
get {
- val bets= getBets()
val id = call.parameters["id"] ?: ""
- bets.find { it.id == id }?.let { bet ->
+ betDataSource.getBetById(id)?.let { bet ->
call.respond(HttpStatusCode.Accepted, bet)
} ?: call.respond(HttpStatusCode.NotFound, ApiMessage.BetNotFound)
}
@@ -75,35 +64,35 @@ fun Application.BetRouter() {
route("/bets/delete") {
post {
- val idbet = call.receive