From ade520749a4d95117f257cf69fdb81348e9bcbc5 Mon Sep 17 00:00:00 2001 From: Eneko Date: Fri, 26 Mar 2021 19:01:42 +0100 Subject: [PATCH] CreateTravel creates matrix room and invites driver and travelers --- .../fosil/okupamicoche/dto/CreateUserDto.kt | 3 +++ .../eu/fosil/okupamicoche/dto/UserDto.kt | 2 +- .../eu/fosil/okupamicoche/dto/UserInfoDto.kt | 2 +- .../eu/fosil/okupamicoche/entities/Travel.kt | 8 +++++- .../eu/fosil/okupamicoche/entities/User.kt | 2 +- .../okupamicoche/entities/UserKeycloak.kt | 2 +- .../spring/controller/ApiRestController.kt | 2 +- .../controller/PrivateTravelRestController.kt | 27 ++++++++++--------- .../controller/PrivateUserRestController.kt | 12 ++++----- .../spring/controller/PublicRestController.kt | 4 +-- .../spring/services/MatrixService.kt | 24 +++++++++++++---- .../spring/services/UseCaseService.kt | 21 +++++++++++++++ .../fosil/okupamicoche/usecases/MatrixApi.kt | 5 ---- .../okupamicoche/usecases/UseCaseFactory.kt | 10 +++++++ .../usecases/matrix/CreateRoomForTravel.kt | 23 ++++++++++++++++ .../okupamicoche/usecases/matrix/MatrixApi.kt | 13 +++++++++ .../usecases/travel/CreateTravel.kt | 7 +++-- src/main/resources/application.yml | 2 +- 18 files changed, 130 insertions(+), 39 deletions(-) create mode 100644 src/main/kotlin/eu/fosil/okupamicoche/spring/services/UseCaseService.kt delete mode 100644 src/main/kotlin/eu/fosil/okupamicoche/usecases/MatrixApi.kt create mode 100644 src/main/kotlin/eu/fosil/okupamicoche/usecases/UseCaseFactory.kt create mode 100644 src/main/kotlin/eu/fosil/okupamicoche/usecases/matrix/CreateRoomForTravel.kt create mode 100644 src/main/kotlin/eu/fosil/okupamicoche/usecases/matrix/MatrixApi.kt diff --git a/src/main/kotlin/eu/fosil/okupamicoche/dto/CreateUserDto.kt b/src/main/kotlin/eu/fosil/okupamicoche/dto/CreateUserDto.kt index d20cc57..9f92250 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/dto/CreateUserDto.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/dto/CreateUserDto.kt @@ -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 ) diff --git a/src/main/kotlin/eu/fosil/okupamicoche/dto/UserDto.kt b/src/main/kotlin/eu/fosil/okupamicoche/dto/UserDto.kt index 8eaebda..59f6db7 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/dto/UserDto.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/dto/UserDto.kt @@ -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 = emptyList(), diff --git a/src/main/kotlin/eu/fosil/okupamicoche/dto/UserInfoDto.kt b/src/main/kotlin/eu/fosil/okupamicoche/dto/UserInfoDto.kt index ed6f522..4e8b569 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/dto/UserInfoDto.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/dto/UserInfoDto.kt @@ -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( diff --git a/src/main/kotlin/eu/fosil/okupamicoche/entities/Travel.kt b/src/main/kotlin/eu/fosil/okupamicoche/entities/Travel.kt index 859aa22..052c97a 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/entities/Travel.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/entities/Travel.kt @@ -17,4 +17,10 @@ class Travel( var places: Int, var description: String? = null, var matrixRoomId: String -) \ No newline at end of file +) { + fun users(): Set { + val allUsers = mutableSetOf(driver) + allUsers.addAll(travelers) + return allUsers + } +} \ No newline at end of file diff --git a/src/main/kotlin/eu/fosil/okupamicoche/entities/User.kt b/src/main/kotlin/eu/fosil/okupamicoche/entities/User.kt index 68a766b..f7552f2 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/entities/User.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/entities/User.kt @@ -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") diff --git a/src/main/kotlin/eu/fosil/okupamicoche/entities/UserKeycloak.kt b/src/main/kotlin/eu/fosil/okupamicoche/entities/UserKeycloak.kt index 11fe68c..0cc4778 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/entities/UserKeycloak.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/entities/UserKeycloak.kt @@ -23,7 +23,7 @@ class UserKeycloak( return User( id, username, - "@$username:fosil.eu", + "@$username:synapse", name, email ) diff --git a/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/ApiRestController.kt b/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/ApiRestController.kt index 0aca0d6..e5459c3 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/ApiRestController.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/ApiRestController.kt @@ -4,7 +4,7 @@ import eu.fosil.okupamicoche.entities.ApiErrorResponse import eu.fosil.okupamicoche.entities.ApiResponse interface ApiRestController { - fun response(function: () -> T): ApiResponse { + suspend fun response(function: suspend () -> T): ApiResponse { return try { val data = function() ApiResponse(true, data) diff --git a/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PrivateTravelRestController.kt b/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PrivateTravelRestController.kt index 077f37c..85975d2 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PrivateTravelRestController.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PrivateTravelRestController.kt @@ -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 { + suspend fun createTravel(@RequestBody @Validated travel: TravelDto): ApiResponse { 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 { + suspend fun cancelTravel(@RequestParam @Validated travelId: TravelId): ApiResponse { return response { CancelTravel(travelRepository).cancelTravel(travelId) } } @RequestMapping("/delete") - fun deleteTravel(@RequestParam @Validated travelId: TravelId): ApiResponse { + suspend fun deleteTravel(@RequestParam @Validated travelId: TravelId): ApiResponse { 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 { + suspend fun editTravel(@RequestBody @Validated travel: TravelDto): ApiResponse { 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> { + suspend fun listUserTravels(): ApiResponse> { 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 { + suspend fun join(@RequestParam @Validated travelId: TravelId): ApiResponse { 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 { + suspend fun leave(@RequestParam @Validated travelId: TravelId): ApiResponse { 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 { @@ -96,7 +99,7 @@ class PrivateTravelRestController( } @RequestMapping("/removetraveler") - fun removeTraveler( + suspend fun removeTraveler( @RequestParam @Validated travelId: TravelId, @RequestParam @Validated userId: UserId ): ApiResponse { diff --git a/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PrivateUserRestController.kt b/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PrivateUserRestController.kt index f797380..b93fa8e 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PrivateUserRestController.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PrivateUserRestController.kt @@ -22,7 +22,7 @@ class PrivateUserRestController( ) : ApiRestController { @RequestMapping("/user") - fun getCurrentUserCreateIfNeeded(): ApiResponse { + suspend fun getCurrentUserCreateIfNeeded(): ApiResponse { 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 { + suspend fun createUser(@RequestBody @Validated createUserDto: CreateUserDto): ApiResponse { 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 { + suspend fun getUser(@RequestBody @Validated userId: UserId): ApiResponse { return response { GetUser(userRepository).getUser(userId)?.let { UserDto(it) } } } @RequestMapping("/delete") - fun deleteUser(@RequestBody @Validated userId: UserId): ApiResponse { + suspend fun deleteUser(@RequestBody @Validated userId: UserId): ApiResponse { return response { DeleteUser(userRepository).deleteUser(userId) } } @RequestMapping("/edit") - fun editUser(@RequestBody @Validated userDto: UserDto): ApiResponse { + suspend fun editUser(@RequestBody @Validated userDto: UserDto): ApiResponse { return response { EditUser(userRepository).editUser(userDto.toUser(userRepository)) } } @RequestMapping("/list") - fun listUsers(): ApiResponse> { + suspend fun listUsers(): ApiResponse> { return response { ListUsers(userRepository).listUsers().map { UserDto(it) } } diff --git a/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PublicRestController.kt b/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PublicRestController.kt index b4feecd..2848ede 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PublicRestController.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/spring/controller/PublicRestController.kt @@ -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 { + suspend fun getTravel(@RequestParam @Validated travelId: TravelId): ApiResponse { return response { travelRepository.findByIdOrNull(travelId)?.let { TravelDto(it) } } diff --git a/src/main/kotlin/eu/fosil/okupamicoche/spring/services/MatrixService.kt b/src/main/kotlin/eu/fosil/okupamicoche/spring/services/MatrixService.kt index 852a325..4732aa4 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/spring/services/MatrixService.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/spring/services/MatrixService.kt @@ -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): 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) } } \ No newline at end of file diff --git a/src/main/kotlin/eu/fosil/okupamicoche/spring/services/UseCaseService.kt b/src/main/kotlin/eu/fosil/okupamicoche/spring/services/UseCaseService.kt new file mode 100644 index 0000000..54e3a48 --- /dev/null +++ b/src/main/kotlin/eu/fosil/okupamicoche/spring/services/UseCaseService.kt @@ -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) + } +} \ No newline at end of file diff --git a/src/main/kotlin/eu/fosil/okupamicoche/usecases/MatrixApi.kt b/src/main/kotlin/eu/fosil/okupamicoche/usecases/MatrixApi.kt deleted file mode 100644 index b23cacc..0000000 --- a/src/main/kotlin/eu/fosil/okupamicoche/usecases/MatrixApi.kt +++ /dev/null @@ -1,5 +0,0 @@ -package eu.fosil.okupamicoche.usecases - -interface MatrixApi { - suspend fun createRoom(name: String) -} \ No newline at end of file diff --git a/src/main/kotlin/eu/fosil/okupamicoche/usecases/UseCaseFactory.kt b/src/main/kotlin/eu/fosil/okupamicoche/usecases/UseCaseFactory.kt new file mode 100644 index 0000000..096ff03 --- /dev/null +++ b/src/main/kotlin/eu/fosil/okupamicoche/usecases/UseCaseFactory.kt @@ -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 +} \ No newline at end of file diff --git a/src/main/kotlin/eu/fosil/okupamicoche/usecases/matrix/CreateRoomForTravel.kt b/src/main/kotlin/eu/fosil/okupamicoche/usecases/matrix/CreateRoomForTravel.kt new file mode 100644 index 0000000..82fbee5 --- /dev/null +++ b/src/main/kotlin/eu/fosil/okupamicoche/usecases/matrix/CreateRoomForTravel.kt @@ -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(' ', '_') + } +} \ No newline at end of file diff --git a/src/main/kotlin/eu/fosil/okupamicoche/usecases/matrix/MatrixApi.kt b/src/main/kotlin/eu/fosil/okupamicoche/usecases/matrix/MatrixApi.kt new file mode 100644 index 0000000..5845cbd --- /dev/null +++ b/src/main/kotlin/eu/fosil/okupamicoche/usecases/matrix/MatrixApi.kt @@ -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): String + + suspend fun inviteUser(roomId: String, user: User) +} \ No newline at end of file diff --git a/src/main/kotlin/eu/fosil/okupamicoche/usecases/travel/CreateTravel.kt b/src/main/kotlin/eu/fosil/okupamicoche/usecases/travel/CreateTravel.kt index 075c447..df755fe 100644 --- a/src/main/kotlin/eu/fosil/okupamicoche/usecases/travel/CreateTravel.kt +++ b/src/main/kotlin/eu/fosil/okupamicoche/usecases/travel/CreateTravel.kt @@ -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) } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a3a3315..5cd5f4a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -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 \ No newline at end of file + token: MDAxNWxvY2F0aW9uIHN5bmFwc2UKMDAxM2lkZW50aWZpZXIga2V5CjAwMTBjaWQgZ2VuID0gMQowMDIwY2lkIHVzZXJfaWQgPSBAdGVzdDpzeW5hcHNlCjAwMTZjaWQgdHlwZSA9IGFjY2VzcwowMDIxY2lkIG5vbmNlID0gMGNZN240LTBVVF43LDAqaQowMDJmc2lnbmF0dXJlII52qsn8HZL-aZJ_7xdWTK7-JHqdRklbn9MZ77ieh-hBCg \ No newline at end of file