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 getFriendFromUserId(id: String): List<UserDTO>
fun deleteFriend(senderId: String, receiverId: String): Boolean fun deleteFriend(senderId: String, receiverId: String): Boolean
fun isFriend(firstUser: String, secondUser: 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 package allin.data.mock
import allin.data.FriendDataSource import allin.data.FriendDataSource
import allin.dto.UserDTO
import allin.model.Friend import allin.model.Friend
import allin.model.FriendStatus
class MockFriendDataSource(private val mockData: MockDataSource.MockData) : FriendDataSource { 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) = override fun getFriendFromUserId(id: String) =
friends.map { Friend(sender = it.sender, receiver = it.receiver) } friends.map { Friend(sender = it.sender, receiver = it.receiver) }
.filter { it.sender == id } .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) = override fun deleteFriend(senderId: String, receiverId: String) =
friends.removeIf { (it.sender == senderId) && (it.receiver == receiverId) } 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) } .filter { (it.sender == firstUser) and (it.receiver == secondUser) }
.map { Friend(sender = it.sender, receiver = it.receiver) } .map { Friend(sender = it.sender, receiver = it.receiver) }
.isNotEmpty() .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.FriendDataSource
import allin.data.postgres.entities.FriendEntity import allin.data.postgres.entities.FriendEntity
import allin.data.postgres.entities.friends import allin.data.postgres.entities.friends
import allin.data.postgres.entities.getFriendStatus
import allin.data.postgres.entities.users import allin.data.postgres.entities.users
import allin.dto.UserDTO
import allin.model.FriendStatus
import org.ktorm.database.Database import org.ktorm.database.Database
import org.ktorm.dsl.and import org.ktorm.dsl.and
import org.ktorm.dsl.eq import org.ktorm.dsl.eq
import org.ktorm.dsl.like
import org.ktorm.entity.* import org.ktorm.entity.*
class PostgresFriendDataSource(private val database: Database) : FriendDataSource { class PostgresFriendDataSource(private val database: Database) : FriendDataSource {
@ -25,7 +29,7 @@ class PostgresFriendDataSource(private val database: Database) : FriendDataSourc
.mapNotNull { .mapNotNull {
database.users.find { usr -> database.users.find { usr ->
usr.id eq it.receiver 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) } .filter { (it.sender eq firstUser) and (it.receiver eq secondUser) }
.map { it.toFriend() } .map { it.toFriend() }
.isNotEmpty() .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 package allin.data.postgres.entities
import allin.model.Friend import allin.model.Friend
import allin.model.FriendStatus
import org.ktorm.database.Database import org.ktorm.database.Database
import org.ktorm.dsl.and
import org.ktorm.dsl.eq
import org.ktorm.entity.Entity import org.ktorm.entity.Entity
import org.ktorm.entity.filter
import org.ktorm.entity.isEmpty
import org.ktorm.entity.sequenceOf import org.ktorm.entity.sequenceOf
import org.ktorm.schema.Table import org.ktorm.schema.Table
import org.ktorm.schema.varchar import org.ktorm.schema.varchar
@ -18,7 +23,6 @@ interface FriendEntity : Entity<FriendEntity> {
sender = sender, sender = sender,
receiver = receiver, receiver = receiver,
) )
} }
object FriendsEntity : Table<FriendEntity>("friend") { object FriendsEntity : Table<FriendEntity>("friend") {
@ -27,3 +31,18 @@ object FriendsEntity : Table<FriendEntity>("friend") {
} }
val Database.friends get() = this.sequenceOf(FriendsEntity) 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 package allin.data.postgres.entities
import allin.dto.UserDTO import allin.dto.UserDTO
import allin.model.FriendStatus
import org.ktorm.database.Database import org.ktorm.database.Database
import org.ktorm.entity.Entity import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf import org.ktorm.entity.sequenceOf
@ -20,13 +21,14 @@ interface UserEntity : Entity<UserEntity> {
var nbCoins: Int var nbCoins: Int
var lastGift: Instant var lastGift: Instant
fun toUserDTO() = fun toUserDTO(friendStatus: FriendStatus? = null) =
UserDTO( UserDTO(
id = id, id = id,
username = username, username = username,
email = email, email = email,
nbCoins = nbCoins, nbCoins = nbCoins,
token = null token = null,
friendStatus = friendStatus
) )
} }

@ -1,5 +1,6 @@
package allin.dto package allin.dto
import allin.model.FriendStatus
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
@ -8,5 +9,6 @@ data class UserDTO(
val username: String, val username: String,
val email: String, val email: String,
val nbCoins: Int, 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 nbCoins: Int = DEFAULT_COIN_AMOUNT,
var token: String? = null var token: String? = null
) { ) {
fun toDto() = fun toDto(friendStatus: FriendStatus? = null) =
UserDTO( UserDTO(
id = id, id = id,
username = username, username = username,
email = email, email = email,
nbCoins = nbCoins, nbCoins = nbCoins,
token = token token = token,
friendStatus = friendStatus
) )
} }

@ -92,7 +92,6 @@ fun Application.friendRouter() {
} }
} }
} }
} }
post("/friends/delete", { post("/friends/delete", {
description = "Allows a user to delete a friend" 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>() val tempUser = call.receive<UserRequest>()
if (RegexCheckerUser.isEmailInvalid(tempUser.email)) { if (RegexCheckerUser.isEmailInvalid(tempUser.email)) {
call.respond(HttpStatusCode.Forbidden, ApiMessage.INVALID_MAIL) 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) 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) call.respond(HttpStatusCode.Conflict, ApiMessage.MAIL_ALREADY_EXISTS)
} else { } else {
val user = User( val user = User(

Loading…
Cancel
Save