Skip to content

Commit

Permalink
Added notifications for approving new users (#2803)
Browse files Browse the repository at this point in the history
* Added notifications for approving new users
  • Loading branch information
Cheshiriks authored Oct 25, 2023
1 parent 52a2c10 commit 7c2aac3
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 0 deletions.
1 change: 1 addition & 0 deletions db/v-3/core/db.changelog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">

<include file="init/_all_changelogs.xml" relativeToChangelogFile="true"/>
<include file="notification.xml" relativeToChangelogFile="true"/>

</databaseChangeLog>
27 changes: 27 additions & 0 deletions db/v-3/core/notification.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">

<changeSet id="notification-creation" author="saveourtool-dev">
<createTable tableName="notification">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="message" type="TEXT"/>
<column name="user_id" type="bigint">
<constraints foreignKeyName="fk_notification_user" references="user(id)"
nullable="false" deleteCascade="true"/>
</column>
<column name="create_date" type="DATETIME(3)">
<constraints nullable="true"/>
</column>
<column name="update_date" type="DATETIME(3)">
<constraints nullable="true"/>
</column>
</createTable>
</changeSet>

</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -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()
}
}
Original file line number Diff line number Diff line change
@@ -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<Notification>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ interface UserRepository : BaseEntityRepository<User>, ValidateRepository {
*/
fun findByName(username: String): User?

/**
* @param role
* @return users with status
*/
fun findByRole(role: String): List<User>

/**
* @param status
* @return users with status
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Notification>): List<Notification> = notificationRepository.saveAll(notifications)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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?,
)
Original file line number Diff line number Diff line change
@@ -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<NotificationDto>() {
override fun toDto() = NotificationDto(
message = message,
createDate = createDate?.toKotlinLocalDateTime(),
)
}

0 comments on commit 7c2aac3

Please sign in to comment.