From d4da0fd3f671dc955bfacf78f854e242b48f88ec Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Wed, 15 May 2024 12:42:38 +0200 Subject: [PATCH] :sparkles: [no_ci] Add friend routes --- Sources/src/main/kotlin/allin/Application.kt | 3 +- .../kotlin/allin/data/FriendDataSource.kt | 9 +- .../allin/data/mock/MockFriendDataSource.kt | 10 +- .../data/postgres/PostgresFriendDataSource.kt | 47 +++++--- .../data/postgres/entities/FriendEntity.kt | 24 +++- .../src/main/kotlin/allin/model/ApiMessage.kt | 3 + Sources/src/main/kotlin/allin/model/Friend.kt | 7 ++ .../main/kotlin/allin/routing/friendRouter.kt | 114 ++++++++++++++++++ 8 files changed, 179 insertions(+), 38 deletions(-) create mode 100644 Sources/src/main/kotlin/allin/model/Friend.kt create mode 100644 Sources/src/main/kotlin/allin/routing/friendRouter.kt diff --git a/Sources/src/main/kotlin/allin/Application.kt b/Sources/src/main/kotlin/allin/Application.kt index 209ec90..0bc84fa 100644 --- a/Sources/src/main/kotlin/allin/Application.kt +++ b/Sources/src/main/kotlin/allin/Application.kt @@ -38,7 +38,7 @@ val Application.dataSource: AllInDataSource get() = allInDataSource fun main() { - embeddedServer(Netty, port = 8080, host = "0.0.0.0") { + embeddedServer(Netty, port = 10001, host = "0.0.0.0") { extracted() }.start(wait = true) } @@ -85,6 +85,7 @@ private fun Application.extracted() { betRouter() participationRouter() betDetailRouter() + friendRouter() kronJob(BET_VERIFY_DELAY) { dataSource.betDataSource.updateBetStatuses(ZonedDateTime.now()) diff --git a/Sources/src/main/kotlin/allin/data/FriendDataSource.kt b/Sources/src/main/kotlin/allin/data/FriendDataSource.kt index 05b0658..bb72df0 100644 --- a/Sources/src/main/kotlin/allin/data/FriendDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/FriendDataSource.kt @@ -1,11 +1,8 @@ package allin.data -import allin.model.User - interface FriendDataSource { fun addFriend(sender: String, receiver: String) - fun getFriendFromUserId(id: String): List - fun getFriendFromUsername(username: String) - fun deleteFriend(senderId: String, receiverId: String) - fun isFriend(firstUser: String, secondUser: String) + fun getFriendFromUserId(id: String): List + fun deleteFriend(senderId: String, receiverId: String): Boolean + fun isFriend(firstUser: String, secondUser: String): Boolean } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/data/mock/MockFriendDataSource.kt b/Sources/src/main/kotlin/allin/data/mock/MockFriendDataSource.kt index ce77707..585d394 100644 --- a/Sources/src/main/kotlin/allin/data/mock/MockFriendDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/mock/MockFriendDataSource.kt @@ -1,26 +1,22 @@ package allin.data.mock import allin.data.FriendDataSource -import allin.model.User class MockFriendDataSource(mockData: MockDataSource.MockData) : FriendDataSource { override fun addFriend(sender: String, receiver: String) { TODO("Not yet implemented") } - override fun getFriendFromUserId(id: String): List { + override fun getFriendFromUserId(id: String): List { TODO("Not yet implemented") } - override fun getFriendFromUsername(username: String) { + override fun deleteFriend(senderId: String, receiverId: String): Boolean { TODO("Not yet implemented") } - override fun deleteFriend(senderId: String, receiverId: String) { + override fun isFriend(firstUser: String, secondUser: String): Boolean { TODO("Not yet implemented") } - override fun isFriend(firstUser: String, secondUser: String) { - TODO("Not yet implemented") - } } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/data/postgres/PostgresFriendDataSource.kt b/Sources/src/main/kotlin/allin/data/postgres/PostgresFriendDataSource.kt index 9354419..cbfd179 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresFriendDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresFriendDataSource.kt @@ -1,33 +1,42 @@ package allin.data.postgres import allin.data.FriendDataSource -import allin.data.postgres.entities.FriendsEntity -import allin.model.User +import allin.data.postgres.entities.FriendEntity +import allin.data.postgres.entities.friends import org.ktorm.database.Database -import org.ktorm.dsl.insert +import org.ktorm.dsl.and +import org.ktorm.dsl.eq +import org.ktorm.entity.add +import org.ktorm.entity.filter +import org.ktorm.entity.map +import org.ktorm.entity.removeIf class PostgresFriendDataSource(private val database: Database) : FriendDataSource { override fun addFriend(sender: String, receiver: String) { - database.insert(FriendsEntity) { - set(it.sender, sender) - set(it.receiver, receiver) - } + database.friends.add( + FriendEntity { + this.sender = sender + this.receiver = receiver + } + ) } - override fun getFriendFromUserId(id: String): List { - TODO() + override fun getFriendFromUserId(id: String): List { + val friendList = database.friends.map { it.toFriend() } + val friendPairs = friendList.map { it.sender to it.receiver }.toSet() + return friendList + .filter { it.sender == id } + .map { it.receiver } } - override fun getFriendFromUsername(username: String) { - TODO("Not yet implemented") - } - - override fun deleteFriend(senderId: String, receiverId: String) { - TODO("Not yet implemented") - } - - override fun isFriend(firstUser: String, secondUser: String) { - TODO("Not yet implemented") + override fun deleteFriend(senderId: String, receiverId: String): Boolean { + database.friends.removeIf { it.sender eq senderId } + return database.friends.removeIf { it.sender eq senderId } > 0 } + override fun isFriend(firstUser: String, secondUser: String) = + database.friends + .filter { (it.sender eq firstUser) and (it.receiver eq secondUser) } + .map { it.toFriend() } + .isNotEmpty() } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/data/postgres/entities/FriendEntity.kt b/Sources/src/main/kotlin/allin/data/postgres/entities/FriendEntity.kt index a436c71..5ef4920 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/entities/FriendEntity.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/entities/FriendEntity.kt @@ -1,15 +1,29 @@ package allin.data.postgres.entities +import allin.model.Friend +import org.ktorm.database.Database import org.ktorm.entity.Entity +import org.ktorm.entity.sequenceOf import org.ktorm.schema.Table import org.ktorm.schema.varchar interface FriendEntity : Entity { - val sender: String - val receiver: String + companion object : Entity.Factory() + + var sender: String + var receiver: String + + fun toFriend() = + Friend( + sender = sender, + receiver = receiver, + ) + } object FriendsEntity : Table("friend") { - val sender = varchar("id").primaryKey().bindTo { it.sender } - val receiver = varchar("bet").primaryKey().bindTo { it.receiver } -} \ No newline at end of file + val sender = varchar("sender").primaryKey().bindTo { it.sender } + val receiver = varchar("receiver").primaryKey().bindTo { it.receiver } +} + +val Database.friends get() = this.sequenceOf(FriendsEntity) diff --git a/Sources/src/main/kotlin/allin/model/ApiMessage.kt b/Sources/src/main/kotlin/allin/model/ApiMessage.kt index a09c1e1..2d1a045 100644 --- a/Sources/src/main/kotlin/allin/model/ApiMessage.kt +++ b/Sources/src/main/kotlin/allin/model/ApiMessage.kt @@ -13,4 +13,7 @@ object ApiMessage { const val NOT_ENOUGH_COINS = "Not enough coins." const val NO_GIFT = "Can't get daily gift." const val USER_CANT_BE_DELETE = "This user can't be delete now !" + const val FRIENDS_ALREADY_EXISTS = "User already exists in your Friends List." + const val FRIENDS_DOESNT_EXISTS = "User already exists in your Friends List." + } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/model/Friend.kt b/Sources/src/main/kotlin/allin/model/Friend.kt new file mode 100644 index 0000000..a9efe26 --- /dev/null +++ b/Sources/src/main/kotlin/allin/model/Friend.kt @@ -0,0 +1,7 @@ +package allin.model + + +data class Friend( + val sender: String, + val receiver: String +) \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/routing/friendRouter.kt b/Sources/src/main/kotlin/allin/routing/friendRouter.kt new file mode 100644 index 0000000..c1ad79b --- /dev/null +++ b/Sources/src/main/kotlin/allin/routing/friendRouter.kt @@ -0,0 +1,114 @@ +package allin.routing + +import allin.dataSource +import allin.ext.hasToken +import allin.model.ApiMessage +import io.github.smiley4.ktorswaggerui.dsl.post +import io.ktor.http.* + +import io.ktor.server.application.* +import io.ktor.server.auth.* +import io.ktor.server.auth.jwt.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* + +fun Application.friendRouter() { + + val userDataSource = this.dataSource.userDataSource + val friendDataSource = this.dataSource.friendDataSource + + + routing { + authenticate { + + post("/friend/add", { + description = "Allows a user to add a friend" + request { + headerParameter("JWT token of the logged user") + body { + description = "User to add in the friends list" + } + } + response { + HttpStatusCode.Created to { + description = "the friend has been added" + body() { + description = "Friend with assigned id" + } + } + HttpStatusCode.Conflict to { + description = "Friend already exist in the friends list" + body(ApiMessage.FRIENDS_ALREADY_EXISTS) + } + } + }) { + hasToken { principal -> + val requestMap = call.receive>() + val usernameFriend = requestMap["username"] ?: return@hasToken call.respond(HttpStatusCode.BadRequest, "Username is missing") + val username = tokenManagerBet.getUsernameFromToken(principal) + + val user = userDataSource.getUserByUsername(username).first + val userFriend = userDataSource.getUserByUsername(usernameFriend).first + + if (user == null || userFriend == null) { + call.respond(HttpStatusCode.Conflict, ApiMessage.USER_NOT_FOUND) + } else { + val friendlist = friendDataSource.getFriendFromUserId(user.id) + if (friendlist.contains(userFriend.id)) { + call.respond(HttpStatusCode.Conflict,ApiMessage.FRIENDS_ALREADY_EXISTS) + } else { + friendDataSource.addFriend(user.id, userFriend.id) + call.respond(HttpStatusCode.Created, usernameFriend) + } + } + } + + } + post("/friend/delete", { + description = "Allows a user to delete a friend" + request { + headerParameter("JWT token of the logged user") + body { + description = "User to delete in the friends list" + } + } + response { + HttpStatusCode.Created to { + description = "the friend has been delete" + body() { + description = "Friend with assigned id" + } + } + HttpStatusCode.Conflict to { + description = "Friend doesn't exist in the friends list" + body(ApiMessage.FRIENDS_DOESNT_EXISTS) + } + } + }) { + hasToken { principal -> + val requestMap = call.receive>() + val usernameFriend = requestMap["username"] ?: return@hasToken call.respond(HttpStatusCode.BadRequest, "Username is missing") + val username = tokenManagerBet.getUsernameFromToken(principal) + + val user = userDataSource.getUserByUsername(username).first + val userFriend = userDataSource.getUserByUsername(usernameFriend).first + + if (user == null || userFriend == null) { + call.respond(HttpStatusCode.Conflict, ApiMessage.USER_NOT_FOUND) + } else { + val friendlist = friendDataSource.getFriendFromUserId(user.id) + if (!friendlist.contains(userFriend.id)) { + call.respond(HttpStatusCode.Conflict,ApiMessage.FRIENDS_DOESNT_EXISTS) + } else { + friendDataSource.deleteFriend(user.id, userFriend.id) + call.respond(HttpStatusCode.Created, usernameFriend) + } + } + } + + } + + } + } +} \ No newline at end of file