From 7c2aac3d692c77be197ab449a2b5414b44a55e63 Mon Sep 17 00:00:00 2001 From: Vladislav Frolov <50615459+Cheshiriks@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:49:45 +0300 Subject: [PATCH] Added notifications for approving new users (#2803) * Added notifications for approving new users --- db/v-3/core/db.changelog.xml | 1 + db/v-3/core/notification.xml | 27 +++++++++++ .../save/backend/event/UserListener.kt | 46 +++++++++++++++++++ .../repository/NotificationRepository.kt | 11 +++++ .../save/backend/repository/UserRepository.kt | 6 +++ .../backend/service/NotificationService.kt | 29 ++++++++++++ .../backend/service/UserDetailsService.kt | 9 ++++ .../save/entities/NotificationDto.kt | 14 ++++++ .../saveourtool/save/entities/Notification.kt | 27 +++++++++++ 9 files changed, 170 insertions(+) create mode 100644 db/v-3/core/notification.xml create mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/event/UserListener.kt create mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/NotificationRepository.kt create mode 100644 save-backend/src/main/kotlin/com/saveourtool/save/backend/service/NotificationService.kt create mode 100644 save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/entities/NotificationDto.kt create mode 100644 save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/entities/Notification.kt diff --git a/db/v-3/core/db.changelog.xml b/db/v-3/core/db.changelog.xml index c25f331ec7..87363a1804 100644 --- a/db/v-3/core/db.changelog.xml +++ b/db/v-3/core/db.changelog.xml @@ -6,5 +6,6 @@ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd"> + \ No newline at end of file diff --git a/db/v-3/core/notification.xml b/db/v-3/core/notification.xml new file mode 100644 index 0000000000..16d840e5e5 --- /dev/null +++ b/db/v-3/core/notification.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/event/UserListener.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/event/UserListener.kt new file mode 100644 index 0000000000..b866aebcd2 --- /dev/null +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/event/UserListener.kt @@ -0,0 +1,46 @@ +package com.saveourtool.save.backend.event + +import com.saveourtool.save.backend.service.NotificationService +import com.saveourtool.save.backend.service.UserDetailsService +import com.saveourtool.save.domain.Role +import com.saveourtool.save.entities.Notification +import com.saveourtool.save.entities.User +import com.saveourtool.save.info.UserStatus +import org.springframework.context.event.EventListener +import org.springframework.stereotype.Component + +/** + * A user listener for sending notifications. + */ +@Component +class UserListener( + private val userDetailsService: UserDetailsService, + private val notificationService: NotificationService, +) { + /** + * @param user new user + */ + @EventListener + fun createUser(user: User) { + if (user.status == UserStatus.NOT_APPROVED) { + val recipients = userDetailsService.findByRole(Role.SUPER_ADMIN.asSpringSecurityRole()) + val notifications = recipients.map { + Notification( + message = messageNewUser(user), + user = it, + ) + } + notificationService.saveAll(notifications) + } + } + + companion object { + /** + * @param user + * @return message + */ + fun messageNewUser(user: User) = """ + New user: ${user.name} is waiting for approve of his account. + """.trimIndent() + } +} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/NotificationRepository.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/NotificationRepository.kt new file mode 100644 index 0000000000..26f91d257d --- /dev/null +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/NotificationRepository.kt @@ -0,0 +1,11 @@ +package com.saveourtool.save.backend.repository + +import com.saveourtool.save.entities.Notification +import com.saveourtool.save.spring.repository.BaseEntityRepository +import org.springframework.stereotype.Repository + +/** + * Repository to access data about user notification + */ +@Repository +interface NotificationRepository : BaseEntityRepository diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/UserRepository.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/UserRepository.kt index 044bdba0be..bbb57e877e 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/UserRepository.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/repository/UserRepository.kt @@ -18,6 +18,12 @@ interface UserRepository : BaseEntityRepository, ValidateRepository { */ fun findByName(username: String): User? + /** + * @param role + * @return users with status + */ + fun findByRole(role: String): List + /** * @param status * @return users with status diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/NotificationService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/NotificationService.kt new file mode 100644 index 0000000000..0b93bfe970 --- /dev/null +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/NotificationService.kt @@ -0,0 +1,29 @@ +package com.saveourtool.save.backend.service + +import com.saveourtool.save.backend.repository.NotificationRepository +import com.saveourtool.save.entities.Notification +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +/** + * Service for notifications + */ +@Service +@Transactional(readOnly = true) +class NotificationService( + private val notificationRepository: NotificationRepository, +) { + /** + * @param notification + * @return saved notification + */ + @Transactional + fun save(notification: Notification): Notification = notificationRepository.save(notification) + + /** + * @param notifications + * @return saved notifications + */ + @Transactional + fun saveAll(notifications: List): List = notificationRepository.saveAll(notifications) +} diff --git a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt index 7356e29834..e2d4b5bb1b 100644 --- a/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt +++ b/save-backend/src/main/kotlin/com/saveourtool/save/backend/service/UserDetailsService.kt @@ -14,6 +14,7 @@ import com.saveourtool.save.entities.User import com.saveourtool.save.info.UserStatus import com.saveourtool.save.utils.* import org.slf4j.Logger +import org.springframework.context.ApplicationEventPublisher import org.springframework.data.repository.findByIdOrNull import org.springframework.security.core.Authentication @@ -34,6 +35,7 @@ class UserDetailsService( private val lnkUserOrganizationRepository: LnkUserOrganizationRepository, private val lnkUserProjectRepository: LnkUserProjectRepository, private val avatarStorage: AvatarStorage, + private val applicationEventPublisher: ApplicationEventPublisher, ) { /** * @param user user for update @@ -47,6 +49,12 @@ class UserDetailsService( */ fun findByName(username: String) = userRepository.findByName(username) + /** + * @param role + * @return spring's UserDetails retrieved from save's user found by provided values + */ + fun findByRole(role: String) = userRepository.findByRole(role) + /** * @param name * @return found [User] or exception @@ -121,6 +129,7 @@ class UserDetailsService( */ @Transactional fun saveUser(newUser: User, oldName: String?, oldUserStatus: UserStatus): UserSaveStatus { + applicationEventPublisher.publishEvent(newUser) val isNameFreeAndNotTaken = userRepository.validateName(newUser.name) != 0L // if we are registering new user (updating just name and status to NOT_APPROVED): return if (oldUserStatus == UserStatus.CREATED && newUser.status == UserStatus.NOT_APPROVED) { diff --git a/save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/entities/NotificationDto.kt b/save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/entities/NotificationDto.kt new file mode 100644 index 0000000000..d71c890f07 --- /dev/null +++ b/save-cloud-common/src/commonMain/kotlin/com/saveourtool/save/entities/NotificationDto.kt @@ -0,0 +1,14 @@ +package com.saveourtool.save.entities + +import kotlinx.datetime.LocalDateTime +import kotlinx.serialization.Serializable + +/** + * @property message + * @property createDate + */ +@Serializable +class NotificationDto( + val message: String, + val createDate: LocalDateTime?, +) diff --git a/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/entities/Notification.kt b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/entities/Notification.kt new file mode 100644 index 0000000000..ed07cd3331 --- /dev/null +++ b/save-cloud-common/src/jvmMain/kotlin/com/saveourtool/save/entities/Notification.kt @@ -0,0 +1,27 @@ +package com.saveourtool.save.entities + +import com.saveourtool.save.spring.entity.BaseEntityWithDateAndDto + +import javax.persistence.Entity +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne + +import kotlinx.datetime.toKotlinLocalDateTime + +/** + * @property message + * @property user + */ +@Entity +class Notification( + var message: String, + + @ManyToOne + @JoinColumn(name = "user_id") + var user: User, +) : BaseEntityWithDateAndDto() { + override fun toDto() = NotificationDto( + message = message, + createDate = createDate?.toKotlinLocalDateTime(), + ) +}