Add friend status and user search
continuous-integration/drone/push Build is passing Details

pull/14/head
avalin 11 months ago
parent 04860d14e5
commit c30826ecfe

@ -7,4 +7,5 @@ interface FriendDataSource {
fun getFriendFromUserId(id: String): List<UserDTO>
fun deleteFriend(senderId: String, receiverId: String): Boolean
fun isFriend(firstUser: String, secondUser: String): Boolean
fun filterUsersByUsername(fromUserId: String, search: String): List<UserDTO>
}

@ -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<UserDTO> =
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
}
}
)
}
}

@ -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<UserDTO> =
database.users.filter {
it.username like "%$search%"
}
.map { user ->
user.toUserDTO(
friendStatus = database.getFriendStatus(fromUserId, user.id)
)
}
}

@ -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<FriendEntity> {
sender = sender,
receiver = receiver,
)
}
object FriendsEntity : Table<FriendEntity>("friend") {
@ -27,3 +31,18 @@ object FriendsEntity : Table<FriendEntity>("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
}
}
}

@ -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<UserEntity> {
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
)
}

@ -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?
)

@ -0,0 +1,7 @@
package allin.model
enum class FriendStatus {
FRIEND,
REQUESTED,
NOT_FRIEND
}

@ -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
)
}

@ -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<JWTPrincipal>("JWT token of the logged user")
pathParameter<String>("Search string")
}
response {
HttpStatusCode.OK to {
body<List<UserDTO>> {
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)
}
}
}
}
}
}

@ -55,11 +55,9 @@ fun Application.userRouter() {
val tempUser = call.receive<UserRequest>()
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(

Loading…
Cancel
Save