From 140edcbb6d443ad6e65c470127ceae99eda7239d Mon Sep 17 00:00:00 2001 From: slon565 Date: Wed, 9 Aug 2023 14:42:44 +0400 Subject: [PATCH] UserInfo receiving implemented --- .../sicamphelper/api/command/Command.kt | 11 ++-- .../api/controller/UsersController.kt | 17 ++--- .../sicamphelper/model/data/UserInput.kt | 4 +- .../sicamphelper/model/info/EntityRef.kt | 7 ++ .../ru/sicamp/sicamphelper/model/info/Info.kt | 10 +++ .../sicamphelper/model/info/UserInfo.kt | 48 ++++++++++++++ .../sicamphelper/repository/UserRepository.kt | 63 ++++++++++++++++++ .../sicamphelper/service/UserService.kt | 66 +++++++------------ .../ru/sicamp/sicamphelper/util/TgUser.kt | 3 + .../ru/sicamp/sicamphelper/util/Utils.kt | 4 +- 10 files changed, 174 insertions(+), 59 deletions(-) create mode 100644 src/main/kotlin/ru/sicamp/sicamphelper/model/info/EntityRef.kt create mode 100644 src/main/kotlin/ru/sicamp/sicamphelper/model/info/Info.kt create mode 100644 src/main/kotlin/ru/sicamp/sicamphelper/model/info/UserInfo.kt create mode 100644 src/main/kotlin/ru/sicamp/sicamphelper/repository/UserRepository.kt create mode 100644 src/main/kotlin/ru/sicamp/sicamphelper/util/TgUser.kt diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/api/command/Command.kt b/src/main/kotlin/ru/sicamp/sicamphelper/api/command/Command.kt index e8704f4..acdfc3e 100644 --- a/src/main/kotlin/ru/sicamp/sicamphelper/api/command/Command.kt +++ b/src/main/kotlin/ru/sicamp/sicamphelper/api/command/Command.kt @@ -3,19 +3,18 @@ package ru.sicamp.sicamphelper.api.command import kotlinx.coroutines.runBlocking import mu.KLogging import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand -import org.telegram.telegrambots.meta.api.methods.send.SendMessage import org.telegram.telegrambots.meta.api.objects.Chat -import org.telegram.telegrambots.meta.api.objects.User import org.telegram.telegrambots.meta.bots.AbsSender -import ru.sicamp.sicamphelper.model.metadata.RequestSource +import ru.sicamp.sicamphelper.db.entity.User import ru.sicamp.sicamphelper.db.table.Users +import ru.sicamp.sicamphelper.model.metadata.RequestSource import ru.sicamp.sicamphelper.model.request.Request import ru.sicamp.sicamphelper.model.request.RequestWrapper -import ru.sicamp.sicamphelper.model.response.Response import ru.sicamp.sicamphelper.model.response.ResponseWrapper import ru.sicamp.sicamphelper.model.response.TelegramResponse import ru.sicamp.sicamphelper.service.UserService import ru.sicamp.sicamphelper.util.Extractor +import ru.sicamp.sicamphelper.util.TgUser abstract class Command( private val command: Commands @@ -24,7 +23,7 @@ abstract class Command( protected abstract suspend fun executeRequest(request: RequestWrapper): ResponseWrapper - override fun execute(absSender: AbsSender?, user: User?, chat: Chat?, arguments: Array?) { + override fun execute(absSender: AbsSender?, user: TgUser?, chat: Chat?, arguments: Array?) { val internalUser = authorizeCommand(user) ?: TODO("Respond unauthorized request") val request = try { RequestWrapper( @@ -49,7 +48,7 @@ abstract class Command( } } - private fun authorizeCommand(user: User?): ru.sicamp.sicamphelper.db.entity.User? = userService.findUserByTgUser( + private fun authorizeCommand(user: TgUser?): User? = userService.findUserByTgUser( user ?: error("No user provided for command $commandIdentifier") ).let { if (it == null) { diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/api/controller/UsersController.kt b/src/main/kotlin/ru/sicamp/sicamphelper/api/controller/UsersController.kt index faee2d1..8cc388b 100644 --- a/src/main/kotlin/ru/sicamp/sicamphelper/api/controller/UsersController.kt +++ b/src/main/kotlin/ru/sicamp/sicamphelper/api/controller/UsersController.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.module.kotlin.convertValue import mu.KLogging import org.springframework.web.bind.annotation.* import ru.sicamp.sicamphelper.model.dto.UserDto +import ru.sicamp.sicamphelper.model.info.UserInfo import ru.sicamp.sicamphelper.service.UserService @RestController @@ -13,19 +14,15 @@ class UsersController( private val objectMapper: ObjectMapper, ) { @PostMapping("/user/create") - fun create(@RequestParam dto: UserDto): String { - val user = userService.createUser(dto) - return user.toString() + fun create(@RequestParam dto: UserDto): UserInfo { + return userService.createUser(dto) } - @GetMapping("/custom/users/find/{login}") - fun getUser(@PathVariable(name = "login", required = true) login: String): String { - logger.info("Getting user with username: $login!!!!!") - return objectMapper.convertValue(userService.loadUserByUsername(login)) + @GetMapping("/user/find/{login}") + fun getUser(@PathVariable(name = "login", required = true) login: String): UserInfo { + logger.info("Getting user with username: $login") + return userService.findUserInfoByLogin(login) } - @GetMapping("/test") - fun test() = "test" - companion object : KLogging() } \ No newline at end of file diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/model/data/UserInput.kt b/src/main/kotlin/ru/sicamp/sicamphelper/model/data/UserInput.kt index a9003dd..0ec8a84 100644 --- a/src/main/kotlin/ru/sicamp/sicamphelper/model/data/UserInput.kt +++ b/src/main/kotlin/ru/sicamp/sicamphelper/model/data/UserInput.kt @@ -1,5 +1,7 @@ package ru.sicamp.sicamphelper.model.data +import ru.sicamp.sicamphelper.util.TgUser + /**Class extracts user from user input. Also used to represent user in views. Format: @tgUsername|$gateLogin|#userId */ class UserInput( input: String @@ -19,6 +21,6 @@ class UserInput( } companion object { - fun fromTgUser(tgUser: org.telegram.telegrambots.meta.api.objects.User) = UserInput(Type.TG_USERNAME.prefix + tgUser.userName) + fun fromTgUser(tgUser: TgUser) = UserInput(Type.TG_USERNAME.prefix + tgUser.userName) } } \ No newline at end of file diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/model/info/EntityRef.kt b/src/main/kotlin/ru/sicamp/sicamphelper/model/info/EntityRef.kt new file mode 100644 index 0000000..1ce885d --- /dev/null +++ b/src/main/kotlin/ru/sicamp/sicamphelper/model/info/EntityRef.kt @@ -0,0 +1,7 @@ +package ru.sicamp.sicamphelper.model.info + +data class EntityRef( + val type: Info.Type, + val id: Long, + val name: String +) \ No newline at end of file diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/model/info/Info.kt b/src/main/kotlin/ru/sicamp/sicamphelper/model/info/Info.kt new file mode 100644 index 0000000..8fb1f02 --- /dev/null +++ b/src/main/kotlin/ru/sicamp/sicamphelper/model/info/Info.kt @@ -0,0 +1,10 @@ +package ru.sicamp.sicamphelper.model.info + +sealed class Info { + abstract val id: Long + abstract val type: Type + + enum class Type { + USER, GROUP, ROOM //TODO add entities + } +} diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/model/info/UserInfo.kt b/src/main/kotlin/ru/sicamp/sicamphelper/model/info/UserInfo.kt new file mode 100644 index 0000000..9bf7a4f --- /dev/null +++ b/src/main/kotlin/ru/sicamp/sicamphelper/model/info/UserInfo.kt @@ -0,0 +1,48 @@ +package ru.sicamp.sicamphelper.model.info + +import ru.sicamp.sicamphelper.db.entity.User +import ru.sicamp.sicamphelper.db.table.Users + +data class UserInfo( + override val id: Long, + override val type: Type, + var login: String, + var name: String, + var supervisor: EntityRef?, + var tgId: Long?, + var tgUsername: String?, + var role: Users.Role, + var group: EntityRef, + var sex: Users.Sex, + var age: Int, + var enabled: Boolean, +) : Info() { + companion object { + fun from(user: User?) = user?.let { + UserInfo( + user.id.value, + Type.USER, + user.login, + user.name, + user.supervisor?.let { + EntityRef( + type = Type.USER, + id = it.id.value, + name = it.name + ) + }, + user.tgId, + user.tgUsername, + user.role, + EntityRef( + type = Type.GROUP, + id = user.group.id.value, + name = user.group.name + ), + user.sex, + user.age, + user.enabled + ) + } + } +} diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/repository/UserRepository.kt b/src/main/kotlin/ru/sicamp/sicamphelper/repository/UserRepository.kt new file mode 100644 index 0000000..60993b6 --- /dev/null +++ b/src/main/kotlin/ru/sicamp/sicamphelper/repository/UserRepository.kt @@ -0,0 +1,63 @@ +package ru.sicamp.sicamphelper.repository + +import mu.KLogging +import org.jetbrains.exposed.dao.with +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.transactions.transaction +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Repository +import ru.sicamp.sicamphelper.db.entity.User +import ru.sicamp.sicamphelper.db.table.Users +import ru.sicamp.sicamphelper.model.dto.UserDto +import ru.sicamp.sicamphelper.util.badArgument +import java.time.Duration +import java.time.LocalDateTime + +@Repository +class UserRepository( + private val passwordEncoder: PasswordEncoder +) { + fun findUserByTgUsernameAndId(id: Long, userName: String) = transaction { + User.find { + (Users.tgId eq id) and + (Users.tgUsername eq userName) + }.firstOrNull() + } + + fun findUserByUsername(username: String) = transaction { + User.find { + Users.login eq username + }.with( + User::supervisor, + User::group + ).firstOrNull() ?: badArgument("Not found user with username $username") + } + + fun findUserById(id: Long) = transaction { + User.findById(id) + } + + fun createUser(userDto: UserDto): User = transaction { + val now = LocalDateTime.now() + logger.info("Creating new user at $now") + return@transaction User.new { + login = userDto.login + _password = passwordEncoder.encode(userDto.password) + role = userDto.role + name = userDto.name + sex = userDto.sex + age = userDto.age + tgId = userDto.tgId + tgUsername = userDto.tgUsername + locked = false + enabled = userDto.enabled + accountExpiration = now + userExpirationTime + credentialsExpiration = now + credentialsExpirationTime + } + } + + companion object : KLogging() { + private val userExpirationTime = Duration.ofDays(60) + private val credentialsExpirationTime = Duration.ofDays(30) + } +} \ No newline at end of file diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/service/UserService.kt b/src/main/kotlin/ru/sicamp/sicamphelper/service/UserService.kt index c79ff57..8e14da2 100644 --- a/src/main/kotlin/ru/sicamp/sicamphelper/service/UserService.kt +++ b/src/main/kotlin/ru/sicamp/sicamphelper/service/UserService.kt @@ -1,53 +1,37 @@ package ru.sicamp.sicamphelper.service -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.transactions.transaction import org.springframework.security.core.userdetails.UserDetailsService -import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.stereotype.Service -import ru.sicamp.sicamphelper.api.controller.UsersController import ru.sicamp.sicamphelper.db.entity.User -import ru.sicamp.sicamphelper.db.table.Users import ru.sicamp.sicamphelper.model.dto.UserDto -import java.time.Duration -import java.time.LocalDateTime +import ru.sicamp.sicamphelper.model.info.UserInfo +import ru.sicamp.sicamphelper.repository.UserRepository +import ru.sicamp.sicamphelper.util.TgUser +import ru.sicamp.sicamphelper.util.badArgument @Service class UserService( - private val passwordEncoder: PasswordEncoder + private val userRepository: UserRepository ) : UserDetailsService { - private val userExpirationTime = Duration.ofDays(60) - private val credentialsExpirationTime = Duration.ofDays(30) - - fun findUserByTgUser(user: org.telegram.telegrambots.meta.api.objects.User): User? = transaction { - User.find { - (Users.tgId eq user.id) and - (Users.tgUsername eq user.userName) - }.firstOrNull() - } + fun findUserByTgUser(user: TgUser): User? = userRepository.findUserByTgUsernameAndId( + id = user.id, + userName = user.userName + ) + + override fun loadUserByUsername(username: String?): User = userRepository.findUserByUsername( + username = username ?: badArgument("Username shouldn't be null") + ) + + fun findUserInfoByLogin(login: String) = UserInfo.from( + userRepository.findUserByUsername(login) + ) ?: badArgument("Not found user with login=$login") + + fun findUserInfoById(id: Long) = UserInfo.from( + userRepository.findUserById(id) + ) + + fun createUser(userDto: UserDto): UserInfo = UserInfo.from( + userRepository.createUser(userDto) + ) ?: error("Failed to create user with login=${userDto.login}") - override fun loadUserByUsername(username: String?): User = transaction { - username?.let { - User.find { Users.login eq it }.firstOrNull() - } ?: error("Not found user with username $username") - } - - fun createUser(userDto: UserDto): User { - val now = LocalDateTime.now() - UsersController.logger.info("Creating new user at $now") - return User.new { - login = userDto.login - _password = passwordEncoder.encode(userDto.password) - role = userDto.role - name = userDto.name - sex = userDto.sex - age = userDto.age - tgId = userDto.tgId - tgUsername = userDto.tgUsername - locked = false - enabled = userDto.enabled - accountExpiration = now + userExpirationTime - credentialsExpiration = now + credentialsExpirationTime - } - } } \ No newline at end of file diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/util/TgUser.kt b/src/main/kotlin/ru/sicamp/sicamphelper/util/TgUser.kt new file mode 100644 index 0000000..3a7bf91 --- /dev/null +++ b/src/main/kotlin/ru/sicamp/sicamphelper/util/TgUser.kt @@ -0,0 +1,3 @@ +package ru.sicamp.sicamphelper.util + +typealias TgUser = org.telegram.telegrambots.meta.api.objects.User \ No newline at end of file diff --git a/src/main/kotlin/ru/sicamp/sicamphelper/util/Utils.kt b/src/main/kotlin/ru/sicamp/sicamphelper/util/Utils.kt index 4f5a3fa..8a2c8b9 100644 --- a/src/main/kotlin/ru/sicamp/sicamphelper/util/Utils.kt +++ b/src/main/kotlin/ru/sicamp/sicamphelper/util/Utils.kt @@ -2,4 +2,6 @@ package ru.sicamp.sicamphelper.util fun R.ifNotNull(obj: T?, block: R.(T) -> R): R = if (obj != null) block(obj) else this -fun R.ifNotEmpty(list: List, block: R.(List) -> R): R = if (list.isNotEmpty()) block(list) else this \ No newline at end of file +fun R.ifNotEmpty(list: List, block: R.(List) -> R): R = if (list.isNotEmpty()) block(list) else this + +fun badArgument(message: String): Nothing = throw IllegalArgumentException(message) \ No newline at end of file