diff --git a/Sources/src/main/kotlin/allin/data/FriendDataSource.kt b/Sources/src/main/kotlin/allin/data/FriendDataSource.kt index cc2e98c..7b97940 100644 --- a/Sources/src/main/kotlin/allin/data/FriendDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/FriendDataSource.kt @@ -7,4 +7,5 @@ interface FriendDataSource { fun getFriendFromUserId(id: String): List fun deleteFriend(senderId: String, receiverId: String): Boolean fun isFriend(firstUser: String, secondUser: String): Boolean + fun filterUsersByUsername(fromUserId: String, search: String): List } \ 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 2ab4979..48126cc 100644 --- a/Sources/src/main/kotlin/allin/data/mock/MockFriendDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/mock/MockFriendDataSource.kt @@ -1,7 +1,9 @@ package allin.data.mock import allin.data.FriendDataSource +import allin.dto.UserDTO import allin.model.Friend +import allin.model.FriendStatus class MockFriendDataSource(private val mockData: MockDataSource.MockData) : FriendDataSource { @@ -15,7 +17,11 @@ class MockFriendDataSource(private val mockData: MockDataSource.MockData) : Frie override fun getFriendFromUserId(id: String) = friends.map { Friend(sender = it.sender, receiver = it.receiver) } .filter { it.sender == id } - .mapNotNull { users.find { usr -> it.receiver == usr.id }?.toDto() } + .mapNotNull { + users + .find { usr -> it.receiver == usr.id } + ?.toDto(friendStatus = FriendStatus.FRIEND) + } override fun deleteFriend(senderId: String, receiverId: String) = friends.removeIf { (it.sender == senderId) && (it.receiver == receiverId) } @@ -26,4 +32,23 @@ class MockFriendDataSource(private val mockData: MockDataSource.MockData) : Frie .filter { (it.sender == firstUser) and (it.receiver == secondUser) } .map { Friend(sender = it.sender, receiver = it.receiver) } .isNotEmpty() + + + override fun filterUsersByUsername(fromUserId: String, search: String): List = + users.filter { (it.username.contains(search, ignoreCase = true)) } + .map { user -> + user.toDto( + friendStatus = friends.filter { friend -> + friend.sender == fromUserId && friend.receiver == user.id + }.let { + if (it.isEmpty()) FriendStatus.NOT_FRIEND + else friends.filter { friend -> + friend.sender == user.id && friend.receiver == fromUserId + }.let { + if (it.isEmpty()) FriendStatus.REQUESTED + else FriendStatus.FRIEND + } + } + ) + } } \ 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 1be8ba3..0fc0053 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/PostgresFriendDataSource.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/PostgresFriendDataSource.kt @@ -3,10 +3,14 @@ package allin.data.postgres import allin.data.FriendDataSource import allin.data.postgres.entities.FriendEntity import allin.data.postgres.entities.friends +import allin.data.postgres.entities.getFriendStatus import allin.data.postgres.entities.users +import allin.dto.UserDTO +import allin.model.FriendStatus import org.ktorm.database.Database import org.ktorm.dsl.and import org.ktorm.dsl.eq +import org.ktorm.dsl.like import org.ktorm.entity.* class PostgresFriendDataSource(private val database: Database) : FriendDataSource { @@ -25,7 +29,7 @@ class PostgresFriendDataSource(private val database: Database) : FriendDataSourc .mapNotNull { database.users.find { usr -> usr.id eq it.receiver - }?.toUserDTO() + }?.toUserDTO(friendStatus = FriendStatus.FRIEND) } @@ -39,4 +43,14 @@ class PostgresFriendDataSource(private val database: Database) : FriendDataSourc .filter { (it.sender eq firstUser) and (it.receiver eq secondUser) } .map { it.toFriend() } .isNotEmpty() + + override fun filterUsersByUsername(fromUserId: String, search: String): List = + database.users.filter { + it.username like "%$search%" + } + .map { user -> + user.toUserDTO( + friendStatus = database.getFriendStatus(fromUserId, user.id) + ) + } } \ 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 5ef4920..dad4492 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/entities/FriendEntity.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/entities/FriendEntity.kt @@ -1,8 +1,13 @@ package allin.data.postgres.entities import allin.model.Friend +import allin.model.FriendStatus import org.ktorm.database.Database +import org.ktorm.dsl.and +import org.ktorm.dsl.eq import org.ktorm.entity.Entity +import org.ktorm.entity.filter +import org.ktorm.entity.isEmpty import org.ktorm.entity.sequenceOf import org.ktorm.schema.Table import org.ktorm.schema.varchar @@ -18,7 +23,6 @@ interface FriendEntity : Entity { sender = sender, receiver = receiver, ) - } object FriendsEntity : Table("friend") { @@ -27,3 +31,18 @@ object FriendsEntity : Table("friend") { } val Database.friends get() = this.sequenceOf(FriendsEntity) +fun Database.getFriendStatus(ofUserId: String, withUserId: String) = + this.friends + .filter { (it.receiver eq withUserId) and (it.sender eq ofUserId) } + .let { + if (it.isEmpty()) { + FriendStatus.NOT_FRIEND + } else { + this.friends + .filter { (it.receiver eq ofUserId) and (it.sender eq withUserId) } + .let { + if (it.isEmpty()) FriendStatus.REQUESTED + else FriendStatus.FRIEND + } + } + } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/data/postgres/entities/UserEntity.kt b/Sources/src/main/kotlin/allin/data/postgres/entities/UserEntity.kt index 463b91e..bf1defb 100644 --- a/Sources/src/main/kotlin/allin/data/postgres/entities/UserEntity.kt +++ b/Sources/src/main/kotlin/allin/data/postgres/entities/UserEntity.kt @@ -1,6 +1,7 @@ package allin.data.postgres.entities import allin.dto.UserDTO +import allin.model.FriendStatus import org.ktorm.database.Database import org.ktorm.entity.Entity import org.ktorm.entity.sequenceOf @@ -20,13 +21,14 @@ interface UserEntity : Entity { var nbCoins: Int var lastGift: Instant - fun toUserDTO() = + fun toUserDTO(friendStatus: FriendStatus? = null) = UserDTO( id = id, username = username, email = email, nbCoins = nbCoins, - token = null + token = null, + friendStatus = friendStatus ) } diff --git a/Sources/src/main/kotlin/allin/dto/UserDTO.kt b/Sources/src/main/kotlin/allin/dto/UserDTO.kt index c34686f..b22ddca 100644 --- a/Sources/src/main/kotlin/allin/dto/UserDTO.kt +++ b/Sources/src/main/kotlin/allin/dto/UserDTO.kt @@ -1,5 +1,6 @@ package allin.dto +import allin.model.FriendStatus import kotlinx.serialization.Serializable @Serializable @@ -8,5 +9,6 @@ data class UserDTO( val username: String, val email: String, val nbCoins: Int, - var token: String? + var token: String?, + val friendStatus: FriendStatus? ) diff --git a/Sources/src/main/kotlin/allin/model/FriendStatus.kt b/Sources/src/main/kotlin/allin/model/FriendStatus.kt new file mode 100644 index 0000000..a5d8763 --- /dev/null +++ b/Sources/src/main/kotlin/allin/model/FriendStatus.kt @@ -0,0 +1,7 @@ +package allin.model + +enum class FriendStatus { + FRIEND, + REQUESTED, + NOT_FRIEND +} \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/model/User.kt b/Sources/src/main/kotlin/allin/model/User.kt index 87ac89d..89058d3 100644 --- a/Sources/src/main/kotlin/allin/model/User.kt +++ b/Sources/src/main/kotlin/allin/model/User.kt @@ -16,13 +16,14 @@ data class User( var nbCoins: Int = DEFAULT_COIN_AMOUNT, var token: String? = null ) { - fun toDto() = + fun toDto(friendStatus: FriendStatus? = null) = UserDTO( id = id, username = username, email = email, nbCoins = nbCoins, - token = token + token = token, + friendStatus = friendStatus ) } diff --git a/Sources/src/main/kotlin/allin/routing/friendRouter.kt b/Sources/src/main/kotlin/allin/routing/friendRouter.kt index 24f1880..6b747a9 100644 --- a/Sources/src/main/kotlin/allin/routing/friendRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/friendRouter.kt @@ -92,7 +92,6 @@ fun Application.friendRouter() { } } } - } post("/friends/delete", { description = "Allows a user to delete a friend" @@ -141,6 +140,33 @@ fun Application.friendRouter() { } + get("/friends/search/{search}", { + description = "Search for users based on username" + request { + headerParameter("JWT token of the logged user") + pathParameter("Search string") + } + response { + HttpStatusCode.OK to { + body> { + description = "Filtered users." + } + } + } + + }) { + hasToken { principal -> + verifyUserFromToken(userDataSource, principal) { userDto, _ -> + val users = friendDataSource.filterUsersByUsername( + fromUserId = userDto.id, + search = call.parameters["search"] ?: "" + ) + + call.respond(HttpStatusCode.OK, users) + } + } + } + } } } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/routing/userRouter.kt b/Sources/src/main/kotlin/allin/routing/userRouter.kt index ace7d8b..2b93aef 100644 --- a/Sources/src/main/kotlin/allin/routing/userRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/userRouter.kt @@ -55,11 +55,9 @@ fun Application.userRouter() { val tempUser = call.receive() if (RegexCheckerUser.isEmailInvalid(tempUser.email)) { call.respond(HttpStatusCode.Forbidden, ApiMessage.INVALID_MAIL) - } - else if (userDataSource.userExists(tempUser.username)) { + } else if (userDataSource.userExists(tempUser.username)) { call.respond(HttpStatusCode.Conflict, ApiMessage.USER_ALREADY_EXISTS) - } - else if (userDataSource.emailExists(tempUser.email)) { + } else if (userDataSource.emailExists(tempUser.email)) { call.respond(HttpStatusCode.Conflict, ApiMessage.MAIL_ALREADY_EXISTS) } else { val user = User(