CreateTravel creates matrix room and invites driver and travelers

This commit is contained in:
2021-03-26 19:01:42 +01:00
parent 65d27b0461
commit ade520749a
18 changed files with 130 additions and 39 deletions

View File

@@ -5,12 +5,14 @@ import eu.fosil.okupamicoche.entities.User
class CreateUserDto(
private val id: String,
private val username: String,
private val matrixId: String,
private val name: String,
private val email: String?
) {
constructor(user: User) : this(
user.id,
user.username,
user.matrixId,
user.name,
user.email
)
@@ -19,6 +21,7 @@ class CreateUserDto(
return User(
id = id,
username = username,
matrixId = matrixId,
name = name,
email = email
)

View File

@@ -8,7 +8,7 @@ class UserDto(
// Los campos deben ser públicos para que aparezcan en el JSON
val id: UserId,
val username: String,
val matrixId: String?,
val matrixId: String,
val name: String,
val email: String?,
val travelsAsDriver: List<TravelDto> = emptyList(),

View File

@@ -7,7 +7,7 @@ class UserInfoDto(
// Los campos deben ser públicos para que aparezcan en el JSON
val id: UserId,
val username: String,
val matrixId: String?,
val matrixId: String,
val name: String
) {
constructor(user: User) : this(

View File

@@ -17,4 +17,10 @@ class Travel(
var places: Int,
var description: String? = null,
var matrixRoomId: String
)
) {
fun users(): Set<User> {
val allUsers = mutableSetOf(driver)
allUsers.addAll(travelers)
return allUsers
}
}

View File

@@ -8,7 +8,7 @@ typealias UserId = String
class User(
@Id var id: UserId,
var username: String,
var matrixId: String? = null,
var matrixId: String,
var name: String,
var email: String? = null,
@OneToMany(mappedBy = "driver")

View File

@@ -23,7 +23,7 @@ class UserKeycloak(
return User(
id,
username,
"@$username:fosil.eu",
"@$username:synapse",
name,
email
)

View File

@@ -4,7 +4,7 @@ import eu.fosil.okupamicoche.entities.ApiErrorResponse
import eu.fosil.okupamicoche.entities.ApiResponse
interface ApiRestController {
fun <T> response(function: () -> T): ApiResponse<T> {
suspend fun <T> response(function: suspend () -> T): ApiResponse<T> {
return try {
val data = function()
ApiResponse(true, data)

View File

@@ -7,6 +7,7 @@ import eu.fosil.okupamicoche.entities.*
import eu.fosil.okupamicoche.repositories.TravelRepository
import eu.fosil.okupamicoche.repositories.UserRepository
import eu.fosil.okupamicoche.spring.services.AuthService
import eu.fosil.okupamicoche.spring.services.UseCaseService
import eu.fosil.okupamicoche.usecases.travel.*
import org.springframework.data.repository.findByIdOrNull
import org.springframework.validation.annotation.Validated
@@ -17,31 +18,33 @@ import org.springframework.web.bind.annotation.*
class PrivateTravelRestController(
private val authService: AuthService,
private val userRepository: UserRepository,
private val travelRepository: TravelRepository
private val travelRepository: TravelRepository,
private val useCaseService: UseCaseService
) : ApiRestController {
@RequestMapping("/create")
fun createTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
suspend fun createTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
return response {
val driver = userRepository.findByIdOrNull(authService.currentUser().id)
?: throw UserIdNotFoundException("Current user not found.")
if (travel.id != null && (travelRepository.findByIdOrNull(travel.id) != null))
throw CannotDuplicateIdException("Travel id already exists.")
println("travel des=${travel.description}")
travel.driverInfo = UserInfoDto(driver)
CreateTravel(travelRepository).createTravel(travel.toTravel(userRepository))
val createTravel = useCaseService.getCreateTravel()
createTravel.createTravel(travel.toTravel(userRepository))
}
}
@RequestMapping("/cancel")
fun cancelTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
suspend fun cancelTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
CancelTravel(travelRepository).cancelTravel(travelId)
}
}
@RequestMapping("/delete")
fun deleteTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
suspend fun deleteTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
if (!authService.canEditTravel(travelId))
throw InsufficientPermissions("Only admins and travel driver can delete this travel.")
@@ -50,7 +53,7 @@ class PrivateTravelRestController(
}
@RequestMapping("/edit")
fun editTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
suspend fun editTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
return response {
if (!authService.canEditTravel(travel.id))
throw InsufficientPermissions("Only admins and travel driver can edit this travel.")
@@ -59,7 +62,7 @@ class PrivateTravelRestController(
}
@RequestMapping("/listusertravels")
fun listUserTravels(): ApiResponse<ListDto<TravelDto>> {
suspend fun listUserTravels(): ApiResponse<ListDto<TravelDto>> {
return response {
val userId = authService.currentUser().id
val useCase = ListUserTravels(travelRepository)
@@ -70,7 +73,7 @@ class PrivateTravelRestController(
}
@RequestMapping("/join")
fun join(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
suspend fun join(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
val userId = authService.currentUser().id
AddTraveler(userRepository, travelRepository).addTraveler(travelId, userId)
@@ -78,7 +81,7 @@ class PrivateTravelRestController(
}
@RequestMapping("/leave")
fun leave(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
suspend fun leave(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
val userId = authService.currentUser().id
RemoveTraveler(userRepository, travelRepository).removeTraveler(travelId, userId)
@@ -86,7 +89,7 @@ class PrivateTravelRestController(
}
@RequestMapping("/addtraveler")
fun addTraveler(
suspend fun addTraveler(
@RequestParam @Validated travelId: TravelId,
@RequestParam @Validated userId: UserId
): ApiResponse<Unit> {
@@ -96,7 +99,7 @@ class PrivateTravelRestController(
}
@RequestMapping("/removetraveler")
fun removeTraveler(
suspend fun removeTraveler(
@RequestParam @Validated travelId: TravelId,
@RequestParam @Validated userId: UserId
): ApiResponse<Unit> {

View File

@@ -22,7 +22,7 @@ class PrivateUserRestController(
) : ApiRestController {
@RequestMapping("/user")
fun getCurrentUserCreateIfNeeded(): ApiResponse<UserDto> {
suspend fun getCurrentUserCreateIfNeeded(): ApiResponse<UserDto> {
return response {
val userKeycloak = authService.currentUser()
var user = userRepository.findByIdOrNull(userKeycloak.id)
@@ -37,7 +37,7 @@ class PrivateUserRestController(
}
@RequestMapping("/create")
fun createUser(@RequestBody @Validated createUserDto: CreateUserDto): ApiResponse<Unit> {
suspend fun createUser(@RequestBody @Validated createUserDto: CreateUserDto): ApiResponse<Unit> {
return response {
if (!authService.currentUser().admin)
throw InsufficientPermissions("Only admins can create users.")
@@ -46,28 +46,28 @@ class PrivateUserRestController(
}
@RequestMapping("/get")
fun getUser(@RequestBody @Validated userId: UserId): ApiResponse<UserDto?> {
suspend fun getUser(@RequestBody @Validated userId: UserId): ApiResponse<UserDto?> {
return response {
GetUser(userRepository).getUser(userId)?.let { UserDto(it) }
}
}
@RequestMapping("/delete")
fun deleteUser(@RequestBody @Validated userId: UserId): ApiResponse<Unit> {
suspend fun deleteUser(@RequestBody @Validated userId: UserId): ApiResponse<Unit> {
return response {
DeleteUser(userRepository).deleteUser(userId)
}
}
@RequestMapping("/edit")
fun editUser(@RequestBody @Validated userDto: UserDto): ApiResponse<Unit> {
suspend fun editUser(@RequestBody @Validated userDto: UserDto): ApiResponse<Unit> {
return response {
EditUser(userRepository).editUser(userDto.toUser(userRepository))
}
}
@RequestMapping("/list")
fun listUsers(): ApiResponse<List<UserDto>> {
suspend fun listUsers(): ApiResponse<List<UserDto>> {
return response {
ListUsers(userRepository).listUsers().map { UserDto(it) }
}

View File

@@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.RestController
class PublicRestController(private val travelRepository: TravelRepository) : ApiRestController {
@RequestMapping("/list")
fun listTravels(
suspend fun listTravels(
@RequestParam @Validated filter: String?,
@RequestParam @Validated sortColumn: String?,
@RequestParam @Validated sortAscending: Boolean?,
@@ -36,7 +36,7 @@ class PublicRestController(private val travelRepository: TravelRepository) : Api
}
@RequestMapping("/travel")
fun getTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<TravelDto?> {
suspend fun getTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<TravelDto?> {
return response {
travelRepository.findByIdOrNull(travelId)?.let { TravelDto(it) }
}

View File

@@ -1,17 +1,31 @@
package eu.fosil.okupamicoche.spring.services
import eu.fosil.okupamicoche.usecases.MatrixApi
import eu.fosil.okupamicoche.entities.User
import eu.fosil.okupamicoche.usecases.matrix.MatrixApi
import mu.KotlinLogging
import net.folivo.matrix.core.model.MatrixId
import net.folivo.matrix.restclient.MatrixClient
import org.springframework.stereotype.Service
import java.util.stream.Collectors
@Service
class MatrixService(val matrixClient: MatrixClient): MatrixApi {
class MatrixService(private val matrixClient: MatrixClient): MatrixApi {
private val logger = KotlinLogging.logger {}
override suspend fun createRoom(name: String) {
logger.debug { "Creating room name" }
matrixClient.roomsApi.createRoom(name = name, roomAliasId = MatrixId.RoomAliasId("#$name:synapse"))
override suspend fun createRoom(name: String, alias: String, usersToInvite: Set<User>): String {
logger.debug { "Creating room name=$name alias=$alias" }
val usersToInviteId = usersToInvite.stream().map { user -> MatrixId.UserId(user.matrixId) }
val roomId = matrixClient.roomsApi.createRoom(
name = name,
roomAliasId = MatrixId.RoomAliasId("#$alias:synapse"),
invite = usersToInviteId.collect(Collectors.toSet())
)
return roomId.full
}
override suspend fun inviteUser(roomId: String, user: User) {
val matrixUserId = MatrixId.UserId(user.matrixId)
logger.debug { "Invite user $matrixUserId to room $roomId" }
matrixClient.roomsApi.inviteUser(MatrixId.RoomId(roomId), matrixUserId)
}
}

View File

@@ -0,0 +1,21 @@
package eu.fosil.okupamicoche.spring.services
import eu.fosil.okupamicoche.repositories.TravelRepository
import eu.fosil.okupamicoche.usecases.UseCaseFactory
import eu.fosil.okupamicoche.usecases.matrix.CreateRoomForTravel
import eu.fosil.okupamicoche.usecases.travel.CreateTravel
import org.springframework.stereotype.Service
@Service
class UseCaseService(
private val travelRepository: TravelRepository,
private val matrixService: MatrixService
): UseCaseFactory {
override fun getCreateTravel(): CreateTravel {
return CreateTravel(travelRepository, getCreateRoomForTravel())
}
override fun getCreateRoomForTravel(): CreateRoomForTravel {
return CreateRoomForTravel(matrixService)
}
}

View File

@@ -1,5 +0,0 @@
package eu.fosil.okupamicoche.usecases
interface MatrixApi {
suspend fun createRoom(name: String)
}

View File

@@ -0,0 +1,10 @@
package eu.fosil.okupamicoche.usecases
import eu.fosil.okupamicoche.usecases.matrix.CreateRoomForTravel
import eu.fosil.okupamicoche.usecases.travel.CreateTravel
interface UseCaseFactory {
fun getCreateTravel(): CreateTravel
fun getCreateRoomForTravel(): CreateRoomForTravel
}

View File

@@ -0,0 +1,23 @@
package eu.fosil.okupamicoche.usecases.matrix
import eu.fosil.okupamicoche.entities.Travel
const val ROOM_NAME_PREFIX = "Viaje"
const val ROOM_ALIAS_PREFIX = "viaje"
class CreateRoomForTravel(private val matrixApi: MatrixApi) {
suspend fun createRoomForTravel(travel: Travel): String {
val name = createRoomName(travel)
val alias = createRoomAlias(travel)
return matrixApi.createRoom(name, alias, travel.users())
}
private fun createRoomName(travel: Travel): String {
return "$ROOM_NAME_PREFIX ${travel.origin} - ${travel.destination}"
}
private fun createRoomAlias(travel: Travel): String {
val alias = "$ROOM_ALIAS_PREFIX-${travel.origin}-${travel.destination}"
return alias.decapitalize().replace(' ', '_')
}
}

View File

@@ -0,0 +1,13 @@
package eu.fosil.okupamicoche.usecases.matrix
import eu.fosil.okupamicoche.entities.User
interface MatrixApi {
/**
* Create room and invite users.
* @return Room Id.
*/
suspend fun createRoom(name: String, alias: String, usersToInvite: Set<User>): String
suspend fun inviteUser(roomId: String, user: User)
}

View File

@@ -2,9 +2,12 @@ package eu.fosil.okupamicoche.usecases.travel
import eu.fosil.okupamicoche.entities.Travel
import eu.fosil.okupamicoche.repositories.TravelRepository
import eu.fosil.okupamicoche.usecases.matrix.CreateRoomForTravel
class CreateTravel(private val travelRepository: TravelRepository) {
fun createTravel(travel: Travel) {
class CreateTravel(private val travelRepository: TravelRepository, private val createRoomForTravel: CreateRoomForTravel) {
suspend fun createTravel(travel: Travel) {
val roomId = createRoomForTravel.createRoomForTravel(travel)
travel.matrixRoomId = roomId
travelRepository.save(travel)
}
}

View File

@@ -57,4 +57,4 @@ matrix:
# (optional) Use http or https. Default is true (so uses https).
secure: false
# The token to authenticate against the Homeserver.
token: MDAxNWxvY2F0aW9uIHN5bmFwc2UKMDAxM2lkZW50aWZpZXIga2V5CjAwMTBjaWQgZ2VuID0gMQowMDIwY2lkIHVzZXJfaWQgPSBAdGVzdDpzeW5hcHNlCjAwMTZjaWQgdHlwZSA9IGFjY2VzcwowMDIxY2lkIG5vbmNlID0gLktxS1E0elVTSi10LDJQTwowMDJmc2lnbmF0dXJlIJ3TmHyJSZw9H0AsCgn3mK4MUhIfRljU-rwxHm5e1N46Cg
token: MDAxNWxvY2F0aW9uIHN5bmFwc2UKMDAxM2lkZW50aWZpZXIga2V5CjAwMTBjaWQgZ2VuID0gMQowMDIwY2lkIHVzZXJfaWQgPSBAdGVzdDpzeW5hcHNlCjAwMTZjaWQgdHlwZSA9IGFjY2VzcwowMDIxY2lkIG5vbmNlID0gMGNZN240LTBVVF43LDAqaQowMDJmc2lnbmF0dXJlII52qsn8HZL-aZJ_7xdWTK7-JHqdRklbn9MZ77ieh-hBCg