Skip to content

Commit

Permalink
User visibility changeable.
Browse files Browse the repository at this point in the history
Conference attendence per user is calculated in client to reduce network messages and enable future adjustment via BBB API. #56
  • Loading branch information
snicki13 committed Sep 27, 2021
1 parent f1f4fab commit 2bd7d74
Show file tree
Hide file tree
Showing 37 changed files with 283 additions and 335 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id("org.springframework.boot") version "2.5.4"
id("org.springframework.boot") version "2.4.5"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
idea
kotlin("jvm") version "1.4.32"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package de.thm.mni.ii.classroom.controller

import de.thm.mni.ii.classroom.exception.api.ApiException
import de.thm.mni.ii.classroom.model.api.MessageBBB
import de.thm.mni.ii.classroom.model.api.CreateRoomBBB
import de.thm.mni.ii.classroom.model.api.JoinRoomBBBResponse
import de.thm.mni.ii.classroom.model.api.MessageBBB
import de.thm.mni.ii.classroom.model.api.ReturnCodeBBB
import de.thm.mni.ii.classroom.services.DownstreamApiService
import org.slf4j.LoggerFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ enum class ConferenceAction {
CREATE,
CLOSE,
PUBLISH,
HIDE
HIDE,
USER_CHANGE
}
9 changes: 3 additions & 6 deletions src/main/kotlin/de/thm/mni/ii/classroom/event/UserEvent.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
package de.thm.mni.ii.classroom.event

import de.thm.mni.ii.classroom.model.classroom.User
import de.thm.mni.ii.classroom.model.classroom.UserDisplay

data class UserEvent(
val user: User,
val inConference: Boolean = false,
val conferenceId: String? = null,
val user: UserDisplay,
val userAction: UserAction
) : ClassroomEvent(UserEvent::class.simpleName!!)

enum class UserAction {
JOIN,
JOIN_CONFERENCE,
LEAVE_CONFERENCE,
VISIBILITY_CHANGE,
LEAVE
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ class ConferenceStorage {
return getConferencesOfUser(user).lastOrNull()
}

fun leaveConference(user: User, conference: Conference) {
fun leaveConference(user: User, conference: Conference): Conference {
this.conferences[conference.conferenceId]!!.attendees.remove(user)
this.usersConference[user]?.remove(conference)
return this.conferences[conference.conferenceId]!!
}

fun deleteConference(conference: Conference): Mono<Conference> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ class DigitalClassroom(

private val logger = LoggerFactory.getLogger(DigitalClassroom::class.java)

private val users = HashMap<User, RSocketRequester?>()
private val users = HashMap<UserDisplay, RSocketRequester?>()
private val tickets = HashSet<Ticket>()
private val nextTicketId = AtomicLong(10000L)
private val conferenceStorage = ConferenceStorage()

val creationTimestamp: ZonedDateTime = ZonedDateTime.now()

fun hasUserJoined() = users.filterValues { it != null }.isNotEmpty()
fun hasUserJoined() = users.isNotEmpty()
fun hasBeenForciblyEnded() = false
fun getDuration() = ChronoUnit.MINUTES.between(creationTimestamp, ZonedDateTime.now())

Expand All @@ -52,8 +52,10 @@ class DigitalClassroom(
}
}

fun connectSocket(user: User, socketRequester: RSocketRequester) {
users[user] = socketRequester
fun connectSocket(user: User, socketRequester: RSocketRequester): Mono<UserDisplay> {
val userDisplay = UserDisplay(user, true)
users[userDisplay] = socketRequester
return Mono.just(userDisplay)
}

fun disconnectSocket(user: User) {
Expand Down Expand Up @@ -92,20 +94,14 @@ class DigitalClassroom(
.map { Pair(it, this) }
}

fun getUsers(): Set<User> {
fun getUsers(): Set<UserDisplay> {
return users.keys
}

fun getUsersFlux(): Flux<User> {
fun getUsersFlux(): Flux<UserDisplay> {
return getUsers().toFlux()
}

fun getUserDisplays(): Flux<UserDisplay> {
return this.getUsersFlux().map { user ->
UserDisplay(user, conferenceStorage.getConferencesOfUser(user).lastOrNull())
}
}

fun getConferencesOfUser(user: User): Flux<Conference> {
return Flux.fromIterable(conferenceStorage.getConferencesOfUser(user))
}
Expand Down Expand Up @@ -139,17 +135,22 @@ class DigitalClassroom(
.switchIfEmpty(Mono.error(ConferenceNotFoundException(conferenceId)))
}

fun leaveConference(user: User, conference: Conference) {
this.conferenceStorage.leaveConference(user, conference)
fun leaveConference(user: User, conference: Conference): Mono<Conference> {
return this.conferenceStorage.leaveConference(user, conference).toMono()
}

fun getUsersOfConference(conference: Conference): Flux<User> {
return Flux.fromIterable(conferenceStorage.getUsersOfConference(conference))
}

fun getLatestConferenceOfUser(user: User) = conferenceStorage.getLatestConferenceOfUser(user).toMono()

fun deleteConference(conference: Conference): Mono<Conference> {
return this.conferenceStorage.deleteConference(conference)
}

fun changeVisibility(user: UserDisplay): Mono<UserDisplay> {
return this.users.keys
.find { it == user }
.also { it?.visible = user.visible }
.toMono()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ open class User(
override val classroomId: String,
val userId: String,
val fullName: String,
var userRole: UserRole
var userRole: UserRole,
) : Principal, UserDetails, ClassroomDependent {

@JsonIgnore
fun isPrivileged(): Boolean = userRole == UserRole.TEACHER || userRole == UserRole.TUTOR

@JsonIgnore
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package de.thm.mni.ii.classroom.model.classroom

import de.thm.mni.ii.classroom.model.ClassroomDependent
import com.fasterxml.jackson.annotation.JsonIgnore

data class UserDisplay(
override val classroomId: String,
val userId: String,
val fullName: String,
val userRole: UserRole,
var inConference: Boolean = false,
var conferenceId: String?
) : ClassroomDependent {
constructor(user: User, conference: Conference?) : this(
class UserDisplay(
classroomId: String,
userId: String,
fullName: String,
userRole: UserRole,
var visible: Boolean,
val conferences: List<ConferenceInfo> = listOf()
) : User(classroomId, userId, fullName, userRole) {
constructor(user: User, visible: Boolean) : this(
user.classroomId,
user.userId,
user.fullName,
user.userRole,
conference?.visible ?: false,
if (conference?.visible == true) conference.conferenceId else null
visible
)

@JsonIgnore
fun getUser(): User = this
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHa
import org.springframework.security.config.annotation.rsocket.RSocketSecurity
import org.springframework.security.messaging.handler.invocation.reactive.AuthenticationPrincipalArgumentResolver
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder
import org.springframework.security.oauth2.server.resource.authentication.*
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor

@Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import com.nimbusds.jose.JWSHeader
import com.nimbusds.jose.JWSObject
import com.nimbusds.jose.Payload
import com.nimbusds.jose.crypto.MACSigner
import com.nimbusds.jose.shaded.json.JSONObject
import de.thm.mni.ii.classroom.model.classroom.User
import de.thm.mni.ii.classroom.properties.JwtProperties
import de.thm.mni.ii.classroom.util.repeatLength
import net.minidev.json.JSONObject
import org.springframework.security.oauth2.jose.jws.MacAlgorithm
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package de.thm.mni.ii.classroom.services

import de.thm.mni.ii.classroom.event.*
import de.thm.mni.ii.classroom.event.ClassroomEvent
import de.thm.mni.ii.classroom.event.ConferenceAction
import de.thm.mni.ii.classroom.event.ConferenceEvent
import de.thm.mni.ii.classroom.event.MessageEvent
import de.thm.mni.ii.classroom.event.TicketAction
import de.thm.mni.ii.classroom.event.TicketEvent
import de.thm.mni.ii.classroom.event.UserAction
import de.thm.mni.ii.classroom.event.UserEvent
import de.thm.mni.ii.classroom.model.classroom.User
import org.slf4j.Logger
import org.slf4j.LoggerFactory
Expand All @@ -20,12 +27,21 @@ class ClassroomEventReceiverService(
is MessageEvent -> messageEventReceived(user, event)
is TicketEvent -> ticketEventReceived(user, event)
is ConferenceEvent -> conferenceEventReceived(user, event)
is UserEvent -> userEventReceived(user, event)
else -> {
logger.info("Received unknown event! ${event.javaClass.name}")
}
}
}

private fun userEventReceived(user: User, event: UserEvent) {
when (event.userAction) {
UserAction.JOIN -> {}
UserAction.LEAVE -> {}
UserAction.VISIBILITY_CHANGE -> userService.changeVisibility(user, event)
}
}

private fun messageEventReceived(user: User, messageEvent: MessageEvent): Mono<Void> {
logger.info("Received message ${messageEvent.message} from ${user.fullName}")
return Mono.empty()
Expand All @@ -45,6 +61,7 @@ class ClassroomEventReceiverService(
ConferenceAction.CLOSE -> conferenceService.endConference(user, conferenceEvent.conferenceInfo)
ConferenceAction.HIDE -> conferenceService.hideConference(user, conferenceEvent.conferenceInfo)
ConferenceAction.PUBLISH -> conferenceService.publishConference(user, conferenceEvent.conferenceInfo)
ConferenceAction.USER_CHANGE -> logger.error("Received USER_CHANGE event from ${user.fullName}! This should never happen")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import de.thm.mni.ii.classroom.event.TicketAction
import de.thm.mni.ii.classroom.event.TicketEvent
import de.thm.mni.ii.classroom.event.UserAction
import de.thm.mni.ii.classroom.event.UserEvent
import de.thm.mni.ii.classroom.model.classroom.*
import de.thm.mni.ii.classroom.model.classroom.ClassroomInfo
import de.thm.mni.ii.classroom.model.classroom.ConferenceInfo
import de.thm.mni.ii.classroom.model.classroom.Ticket
import de.thm.mni.ii.classroom.model.classroom.User
import de.thm.mni.ii.classroom.model.classroom.UserDisplay
import de.thm.mni.ii.classroom.security.exception.UnauthorizedException
import de.thm.mni.ii.classroom.util.component1
import de.thm.mni.ii.classroom.util.component2
import org.slf4j.LoggerFactory
import org.springframework.messaging.rsocket.RSocketRequester
import org.springframework.stereotype.Service
Expand All @@ -24,16 +30,15 @@ class ClassroomUserService(

fun userConnected(user: User, socketRequester: RSocketRequester): Mono<Void> {
return classroomInstanceService.getClassroomInstance(user.classroomId)
.doOnNext { classroom ->
classroom.connectSocket(user, socketRequester)
}.doOnNext { classroom ->
senderService.sendToAll(classroom, UserEvent(user, userAction = UserAction.JOIN)).subscribe()
.flatMap { classroom ->
Mono.zip(Mono.just(classroom), classroom.connectSocket(user, socketRequester))
}.doOnNext { (classroom, userDisplay) ->
senderService.sendToAll(classroom, UserEvent(userDisplay, UserAction.JOIN)).subscribe()
}.doOnSuccess {
logger.info("${user.userId}/${user.fullName} connected to ${user.classroomId}!")
}.flatMap {
socketRequester.rsocketClient().source()
}
.doOnNext {
}.doOnNext {
it.onClose().doOnSuccess {
userDisconnected(user)
}.doOnError { exception ->
Expand All @@ -47,7 +52,7 @@ class ClassroomUserService(
.doOnNext { classroom ->
classroom.disconnectSocket(user)
}.doOnNext { classroom ->
senderService.sendToAll(classroom, UserEvent(user, userAction = UserAction.LEAVE)).subscribe()
senderService.sendToAll(classroom, UserEvent(UserDisplay(user, true), userAction = UserAction.LEAVE)).subscribe()
}.doOnNext {
if (throwable == null) {
logger.info("${user.userId} / ${user.fullName} disconnected from ${user.classroomId}!")
Expand Down Expand Up @@ -108,7 +113,7 @@ class ClassroomUserService(
}.doOnNext { (ticket, classroom) ->
senderService.sendToAll(classroom, TicketEvent(ticket, TicketAction.CLOSE)).subscribe()
}.doOnSuccess { (ticket, classroom) ->
logger.info("Ticket ${classroom.classroomName}/${ticket.ticketId} assigned to ${ticket.assignee!!.fullName}!")
logger.info("Ticket ${classroom.classroomName}/${ticket.ticketId} assigned to ${ticket.assignee?.fullName ?: "N/A"}!")
}.subscribe()
}

Expand All @@ -121,7 +126,7 @@ class ClassroomUserService(
fun getUserDisplays(user: User): Flux<UserDisplay> {
return classroomInstanceService
.getClassroomInstance(user.classroomId)
.flatMapMany { it.getUserDisplays() }
.flatMapMany { it.getUsersFlux() }
}

fun getClassroomInfo(user: User): Mono<ClassroomInfo> {
Expand All @@ -137,4 +142,15 @@ class ClassroomUserService(
classroom.getConferences()
}.map(::ConferenceInfo)
}

fun changeVisibility(user: User, event: UserEvent) {
assert(user == event.user)
classroomInstanceService
.getClassroomInstance(user.classroomId)
.flatMap { classroom ->
Mono.zip(Mono.just(classroom), classroom.changeVisibility(event.user))
}.flatMap { (classroom, user) ->
senderService.sendToAll(classroom, UserEvent(user, UserAction.VISIBILITY_CHANGE))
}.subscribe()
}
}
Loading

0 comments on commit 2bd7d74

Please sign in to comment.