suspend coroutines
This commit is contained in:
@@ -1,16 +1,11 @@
|
|||||||
package space.luminic.budgerapp.configs
|
package space.luminic.budgerapp.configs
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactor.mono
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.http.HttpHeaders
|
import org.springframework.http.HttpHeaders
|
||||||
import org.springframework.http.HttpMethod
|
|
||||||
import org.springframework.http.HttpStatus
|
|
||||||
import org.springframework.security.authentication.BadCredentialsException
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||||
import org.springframework.security.core.AuthenticationException
|
|
||||||
import org.springframework.security.core.GrantedAuthority
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||||
import org.springframework.security.core.context.SecurityContext
|
|
||||||
import org.springframework.security.core.context.SecurityContextImpl
|
import org.springframework.security.core.context.SecurityContextImpl
|
||||||
import org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter
|
import org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
@@ -23,37 +18,36 @@ import space.luminic.budgerapp.services.AuthService
|
|||||||
class BearerTokenFilter(private val authService: AuthService) : SecurityContextServerWebExchangeWebFilter() {
|
class BearerTokenFilter(private val authService: AuthService) : SecurityContextServerWebExchangeWebFilter() {
|
||||||
private val logger = LoggerFactory.getLogger(BearerTokenFilter::class.java)
|
private val logger = LoggerFactory.getLogger(BearerTokenFilter::class.java)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
|
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
|
||||||
val token = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)?.removePrefix("Bearer ")
|
val token = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)?.removePrefix("Bearer ")
|
||||||
|
|
||||||
if (exchange.request.path.value() in listOf("/api/auth/login","/api/auth/register", "/api/auth/tgLogin") || exchange.request.path.value()
|
if (exchange.request.path.value() in listOf(
|
||||||
.startsWith("/api/actuator")
|
"/api/auth/login",
|
||||||
|
"/api/auth/register",
|
||||||
|
"/api/auth/tgLogin"
|
||||||
|
) || exchange.request.path.value().startsWith("/api/actuator")
|
||||||
) {
|
) {
|
||||||
return chain.filter(exchange)
|
return chain.filter(exchange)
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (token != null) {
|
return if (token != null) {
|
||||||
authService.isTokenValid(token)
|
mono {
|
||||||
.flatMap { userDetails ->
|
val userDetails = authService.isTokenValid(token) // suspend вызов
|
||||||
val authorities = userDetails.roles.map { SimpleGrantedAuthority(it) }
|
val authorities = userDetails.roles.map { SimpleGrantedAuthority(it) }
|
||||||
val securityContext = SecurityContextImpl(
|
val securityContext = SecurityContextImpl(
|
||||||
UsernamePasswordAuthenticationToken(
|
UsernamePasswordAuthenticationToken(userDetails.username, null, authorities)
|
||||||
userDetails.username, null, authorities
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
securityContext
|
||||||
|
}.flatMap { securityContext ->
|
||||||
chain.filter(exchange)
|
chain.filter(exchange)
|
||||||
.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))
|
.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))
|
||||||
}
|
}
|
||||||
.onErrorMap(AuthException::class.java) { ex ->
|
|
||||||
BadCredentialsException(ex.message ?: "Unauthorized")
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Mono.error(AuthException("Authorization token is missing"))
|
Mono.error(AuthException("Authorization token is missing"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package space.luminic.budgerapp.controllers
|
package space.luminic.budgerapp.controllers
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactive.awaitFirst
|
||||||
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
import space.luminic.budgerapp.models.User
|
import space.luminic.budgerapp.models.User
|
||||||
@@ -10,16 +15,23 @@ import space.luminic.budgerapp.services.UserService
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/auth")
|
@RequestMapping("/auth")
|
||||||
class AuthController(
|
class AuthController(
|
||||||
|
|
||||||
|
|
||||||
private val userService: UserService,
|
private val userService: UserService,
|
||||||
private val authService: AuthService
|
private val authService: AuthService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val logger = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
|
@GetMapping("/test")
|
||||||
|
fun test(): String {
|
||||||
|
val authentication = SecurityContextHolder.getContext().authentication
|
||||||
|
logger.info("SecurityContext in controller: $authentication")
|
||||||
|
return "Hello, ${authentication.name}"
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
fun login(@RequestBody request: AuthRequest): Mono<Map<String, String>> {
|
suspend fun login(@RequestBody request: AuthRequest): Map<String, String> {
|
||||||
return authService.login(request.username, request.password)
|
return authService.login(request.username, request.password)
|
||||||
.map { token -> mapOf("token" to token) }
|
.map { token -> mapOf("token" to token) }.awaitFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
@@ -34,11 +46,9 @@ class AuthController(
|
|||||||
|
|
||||||
|
|
||||||
@GetMapping("/me")
|
@GetMapping("/me")
|
||||||
fun getMe(@RequestHeader("Authorization") token: String): Mono<User> {
|
suspend fun getMe(): User {
|
||||||
return authService.isTokenValid(token.removePrefix("Bearer "))
|
val securityContext = ReactiveSecurityContextHolder.getContext().awaitSingle()
|
||||||
.flatMap { username ->
|
return userService.getByUserNameWoPass(securityContext.authentication.name)
|
||||||
userService.getByUserNameWoPass(username.username!!)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
package space.luminic.budgerapp.controllers
|
package space.luminic.budgerapp.controllers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingle
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
import org.bson.Document
|
import org.bson.Document
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import org.springframework.web.client.HttpClientErrorException
|
|
||||||
import reactor.core.publisher.Flux
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import space.luminic.budgerapp.controllers.BudgetController.LimitValue
|
import space.luminic.budgerapp.controllers.BudgetController.LimitValue
|
||||||
import space.luminic.budgerapp.controllers.dtos.BudgetCreationDTO
|
import space.luminic.budgerapp.controllers.dtos.BudgetCreationDTO
|
||||||
import space.luminic.budgerapp.models.*
|
import space.luminic.budgerapp.models.*
|
||||||
@@ -33,13 +32,13 @@ class SpaceController(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
fun getSpaces(): Mono<List<Space>> {
|
suspend fun getSpaces(): List<Space> {
|
||||||
return spaceService.getSpaces()
|
return spaceService.getSpaces()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
fun createSpace(@RequestBody space: SpaceCreateDTO): Mono<Space> {
|
suspend fun createSpace(@RequestBody space: SpaceCreateDTO): Space {
|
||||||
return spaceService.createSpace(
|
return spaceService.createSpace(
|
||||||
Space(name = space.name, description = space.description),
|
Space(name = space.name, description = space.description),
|
||||||
space.createCategories
|
space.createCategories
|
||||||
@@ -48,36 +47,37 @@ class SpaceController(
|
|||||||
|
|
||||||
|
|
||||||
@GetMapping("{spaceId}")
|
@GetMapping("{spaceId}")
|
||||||
fun getSpace(@PathVariable spaceId: String): Mono<Space> {
|
suspend fun getSpace(@PathVariable spaceId: String): Space {
|
||||||
return spaceService.getSpace(spaceId)
|
return spaceService.getSpace(spaceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}")
|
@DeleteMapping("/{spaceId}")
|
||||||
fun deleteSpace(@PathVariable spaceId: String): Mono<Void> {
|
suspend fun deleteSpace(@PathVariable spaceId: String) {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
return spaceService.deleteSpace(spaceService.isValidRequest(spaceId))
|
||||||
spaceService.deleteSpace(it)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/invite")
|
@PostMapping("/{spaceId}/invite")
|
||||||
fun inviteSpace(@PathVariable spaceId: String): Mono<SpaceInvite> {
|
suspend fun inviteSpace(@PathVariable spaceId: String): SpaceInvite {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
return spaceService.createInviteSpace(spaceId)
|
return spaceService.createInviteSpace(spaceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/invite/{code}")
|
@PostMapping("/invite/{code}")
|
||||||
fun acceptInvite(@PathVariable code: String): Mono<Space> {
|
suspend fun acceptInvite(@PathVariable code: String): Space {
|
||||||
return spaceService.acceptInvite(code)
|
return spaceService.acceptInvite(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/leave")
|
@DeleteMapping("/{spaceId}/leave")
|
||||||
fun leaveSpace(@PathVariable spaceId: String): Mono<Void> {
|
suspend fun leaveSpace(@PathVariable spaceId: String) {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
return spaceService.leaveSpace(spaceId)
|
return spaceService.leaveSpace(spaceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/members/kick/{username}")
|
@DeleteMapping("/{spaceId}/members/kick/{username}")
|
||||||
fun kickMembers(@PathVariable spaceId: String, @PathVariable username: String): Mono<Void> {
|
suspend fun kickMembers(@PathVariable spaceId: String, @PathVariable username: String) {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
return spaceService.kickMember(spaceId, username)
|
return spaceService.kickMember(spaceId, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,49 +86,46 @@ class SpaceController(
|
|||||||
//Budgets API
|
//Budgets API
|
||||||
//
|
//
|
||||||
@GetMapping("/{spaceId}/budgets")
|
@GetMapping("/{spaceId}/budgets")
|
||||||
fun getBudgets(@PathVariable spaceId: String): Mono<List<Budget>> {
|
suspend fun getBudgets(@PathVariable spaceId: String): List<Budget> {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
spaceService.isValidRequest(spaceId)
|
||||||
financialService.getBudgets(spaceId)
|
return financialService.getBudgets(spaceId).awaitSingleOrNull().orEmpty()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/budgets/{id}")
|
@GetMapping("/{spaceId}/budgets/{id}")
|
||||||
fun getBudget(@PathVariable spaceId: String, @PathVariable id: String): Mono<BudgetDTO> {
|
suspend fun getBudget(@PathVariable spaceId: String, @PathVariable id: String): BudgetDTO? {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
spaceService.isValidRequest(spaceId)
|
||||||
financialService.getBudget(spaceId, id)
|
return financialService.getBudget(spaceId, id)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/budgets")
|
@PostMapping("/{spaceId}/budgets")
|
||||||
fun createBudget(
|
suspend fun createBudget(
|
||||||
@PathVariable spaceId: String,
|
@PathVariable spaceId: String,
|
||||||
@RequestBody budgetCreationDTO: BudgetCreationDTO,
|
@RequestBody budgetCreationDTO: BudgetCreationDTO,
|
||||||
): Mono<Budget> {
|
): Budget? {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
return financialService.createBudget(
|
||||||
financialService.createBudget(it, budgetCreationDTO.budget, budgetCreationDTO.createRecurrent)
|
spaceService.isValidRequest(spaceId),
|
||||||
}
|
budgetCreationDTO.budget,
|
||||||
|
budgetCreationDTO.createRecurrent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/budgets/{id}")
|
@DeleteMapping("/{spaceId}/budgets/{id}")
|
||||||
fun deleteBudget(@PathVariable spaceId: String, @PathVariable id: String): Mono<Void> {
|
suspend fun deleteBudget(@PathVariable spaceId: String, @PathVariable id: String) {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
spaceService.isValidRequest(spaceId)
|
||||||
financialService.deleteBudget(spaceId, id)
|
financialService.deleteBudget(spaceId, id)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/budgets/{budgetId}/categories/{catId}/limit")
|
@PostMapping("/{spaceId}/budgets/{budgetId}/categories/{catId}/limit")
|
||||||
fun setCategoryLimit(
|
suspend fun setCategoryLimit(
|
||||||
@PathVariable spaceId: String,
|
@PathVariable spaceId: String,
|
||||||
@PathVariable budgetId: String,
|
@PathVariable budgetId: String,
|
||||||
@PathVariable catId: String,
|
@PathVariable catId: String,
|
||||||
@RequestBody limit: LimitValue,
|
@RequestBody limit: LimitValue,
|
||||||
): Mono<BudgetCategory> {
|
): BudgetCategory {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
spaceService.isValidRequest(spaceId)
|
||||||
financialService.setCategoryLimit(it.id!!, budgetId, catId, limit.limit)
|
return financialService.setCategoryLimit(spaceId, budgetId, catId, limit.limit)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -164,123 +161,102 @@ class SpaceController(
|
|||||||
|
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/transactions/{id}")
|
@GetMapping("/{spaceId}/transactions/{id}")
|
||||||
fun getTransaction(
|
suspend fun getTransaction(
|
||||||
@PathVariable spaceId: String,
|
@PathVariable spaceId: String,
|
||||||
@PathVariable id: String
|
@PathVariable id: String
|
||||||
): ResponseEntity<Any> {
|
): Transaction {
|
||||||
try {
|
return financialService.getTransactionById(id)
|
||||||
return ResponseEntity.ok(financialService.getTransactionById(id))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
return ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/transactions")
|
@PostMapping("/{spaceId}/transactions")
|
||||||
fun createTransaction(@PathVariable spaceId: String, @RequestBody transaction: Transaction): Mono<Transaction> {
|
suspend fun createTransaction(@PathVariable spaceId: String, @RequestBody transaction: Transaction): Transaction {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
financialService.createTransaction(it, transaction)
|
return financialService.createTransaction(space, transaction)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{spaceId}/transactions/{id}")
|
@PutMapping("/{spaceId}/transactions/{id}")
|
||||||
fun editTransaction(
|
suspend fun editTransaction(
|
||||||
@PathVariable spaceId: String, @PathVariable id: String, @RequestBody transaction: Transaction
|
@PathVariable spaceId: String, @PathVariable id: String, @RequestBody transaction: Transaction
|
||||||
): Mono<Transaction> {
|
): Transaction {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
transaction.space = it
|
transaction.space = space
|
||||||
financialService.editTransaction(transaction)
|
return financialService.editTransaction(transaction)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/transactions/{id}")
|
@DeleteMapping("/{spaceId}/transactions/{id}")
|
||||||
fun deleteTransaction(@PathVariable spaceId: String, @PathVariable id: String): Mono<Void> {
|
suspend fun deleteTransaction(@PathVariable spaceId: String, @PathVariable id: String) {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
spaceService.isValidRequest(spaceId)
|
||||||
financialService.getTransactionById(id).flatMap { financialService.deleteTransaction(it) }
|
val transaction = financialService.getTransactionById(id)
|
||||||
}
|
financialService.deleteTransaction(transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Categories API
|
// Categories API
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/categories")
|
@GetMapping("/{spaceId}/categories")
|
||||||
fun getCategories(
|
suspend fun getCategories(
|
||||||
@PathVariable spaceId: String,
|
@PathVariable spaceId: String,
|
||||||
@RequestParam("type") type: String? = null,
|
@RequestParam("type") type: String? = null,
|
||||||
@RequestParam("sort") sortBy: String = "name",
|
@RequestParam("sort") sortBy: String = "name",
|
||||||
@RequestParam("direction") direction: String = "ASC"
|
@RequestParam("direction") direction: String = "ASC"
|
||||||
): Mono<List<Category>> {
|
): List<Category> {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
spaceService.isValidRequest(spaceId)
|
||||||
categoryService.getCategories(spaceId, type, sortBy, direction)
|
return categoryService.getCategories(spaceId, type, sortBy, direction).awaitSingleOrNull().orEmpty()
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/categories/types")
|
@GetMapping("/{spaceId}/categories/types")
|
||||||
fun getCategoriesTypes(@PathVariable spaceId: String): ResponseEntity<Any> {
|
fun getCategoriesTypes(@PathVariable spaceId: String): List<CategoryType> {
|
||||||
return try {
|
return categoryService.getCategoryTypes()
|
||||||
ResponseEntity.ok(categoryService.getCategoryTypes())
|
|
||||||
} catch (e: Exception) {
|
|
||||||
ResponseEntity(HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR), HttpStatus.INTERNAL_SERVER_ERROR)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/categories")
|
@PostMapping("/{spaceId}/categories")
|
||||||
fun createCategory(
|
suspend fun createCategory(
|
||||||
@PathVariable spaceId: String, @RequestBody category: Category
|
@PathVariable spaceId: String, @RequestBody category: Category
|
||||||
): Mono<Category> {
|
): Category {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
financialService.createCategory(it, category)
|
return financialService.createCategory(space, category).awaitSingle()
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{spaceId}/categories/{categoryId}")
|
@PutMapping("/{spaceId}/categories/{categoryId}")
|
||||||
fun editCategory(
|
suspend fun editCategory(
|
||||||
@PathVariable categoryId: String,
|
@PathVariable categoryId: String,
|
||||||
@RequestBody category: Category,
|
@RequestBody category: Category,
|
||||||
@PathVariable spaceId: String
|
@PathVariable spaceId: String
|
||||||
): Mono<Category> {
|
): Category {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
categoryService.editCategory(it, category)
|
return categoryService.editCategory(space, category)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/categories/{categoryId}")
|
@DeleteMapping("/{spaceId}/categories/{categoryId}")
|
||||||
fun deleteCategory(@PathVariable categoryId: String, @PathVariable spaceId: String): Mono<String> {
|
suspend fun deleteCategory(@PathVariable categoryId: String, @PathVariable spaceId: String) {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
categoryService.deleteCategory(it, categoryId)
|
categoryService.deleteCategory(space, categoryId)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/categories/tags")
|
@GetMapping("/{spaceId}/categories/tags")
|
||||||
fun getTags(@PathVariable spaceId: String): Mono<List<Tag>> {
|
suspend fun getTags(@PathVariable spaceId: String): List<Tag> {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
spaceService.getTags(it)
|
return spaceService.getTags(space)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/categories/tags")
|
@PostMapping("/{spaceId}/categories/tags")
|
||||||
fun createTags(@PathVariable spaceId: String, @RequestBody tag: Tag): Mono<Tag> {
|
suspend fun createTags(@PathVariable spaceId: String, @RequestBody tag: Tag): Tag {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
spaceService.createTag(it, tag)
|
return spaceService.createTag(space, tag)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/categories/tags/{tagId}")
|
@DeleteMapping("/{spaceId}/categories/tags/{tagId}")
|
||||||
fun deleteTags(@PathVariable spaceId: String, @PathVariable tagId: String): Mono<Void> {
|
suspend fun deleteTags(@PathVariable spaceId: String, @PathVariable tagId: String) {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
spaceService.deleteTag(it, tagId)
|
return spaceService.deleteTag(space, tagId)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/analytics/by-month")
|
@GetMapping("/{spaceId}/analytics/by-month")
|
||||||
fun getCategoriesSumsByMonthsV2(@PathVariable spaceId: String): Mono<List<Document>> {
|
suspend fun getCategoriesSumsByMonthsV2(@PathVariable spaceId: String): List<Document> {
|
||||||
return financialService.getCategorySummaries(spaceId, LocalDate.now().minusMonths(6))
|
return financialService.getCategorySummaries(spaceId, LocalDate.now().minusMonths(6))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,46 +266,40 @@ class SpaceController(
|
|||||||
//
|
//
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/recurrents")
|
@GetMapping("/{spaceId}/recurrents")
|
||||||
fun getRecurrents(@PathVariable spaceId: String): Mono<List<Recurrent>> {
|
suspend fun getRecurrents(@PathVariable spaceId: String): List<Recurrent> {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
spaceService.isValidRequest(spaceId)
|
||||||
recurrentService.getRecurrents(it.id!!)
|
return recurrentService.getRecurrents(spaceId).awaitSingleOrNull().orEmpty()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/recurrents/{id}")
|
@GetMapping("/{spaceId}/recurrents/{id}")
|
||||||
fun getRecurrent(@PathVariable spaceId: String, @PathVariable id: String): Mono<Recurrent> {
|
suspend fun getRecurrent(@PathVariable spaceId: String, @PathVariable id: String): Recurrent {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
recurrentService.getRecurrentById(it, id)
|
return recurrentService.getRecurrentById(space, id).awaitSingle()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/recurrent")
|
@PostMapping("/{spaceId}/recurrent")
|
||||||
fun createRecurrent(@PathVariable spaceId: String, @RequestBody recurrent: Recurrent): Mono<Recurrent> {
|
suspend fun createRecurrent(@PathVariable spaceId: String, @RequestBody recurrent: Recurrent): Recurrent {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
recurrentService.createRecurrent(it, recurrent)
|
return recurrentService.createRecurrent(space, recurrent).awaitSingle()
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{spaceId}/recurrent/{id}")
|
@PutMapping("/{spaceId}/recurrent/{id}")
|
||||||
fun editRecurrent(
|
suspend fun editRecurrent(
|
||||||
@PathVariable spaceId: String,
|
@PathVariable spaceId: String,
|
||||||
@PathVariable id: String,
|
@PathVariable id: String,
|
||||||
@RequestBody recurrent: Recurrent
|
@RequestBody recurrent: Recurrent
|
||||||
): Mono<Recurrent> {
|
): Recurrent {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
spaceService.isValidRequest(spaceId)
|
||||||
recurrentService.editRecurrent(recurrent)
|
return recurrentService.editRecurrent(recurrent).awaitSingle()
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@DeleteMapping("/{spaceId}/recurrent/{id}")
|
@DeleteMapping("/{spaceId}/recurrent/{id}")
|
||||||
fun deleteRecurrent(@PathVariable spaceId: String, @PathVariable id: String): Mono<Void> {
|
suspend fun deleteRecurrent(@PathVariable spaceId: String, @PathVariable id: String) {
|
||||||
return spaceService.isValidRequest(spaceId).flatMap {
|
spaceService.isValidRequest(spaceId)
|
||||||
recurrentService.deleteRecurrent(id)
|
recurrentService.deleteRecurrent(id).awaitSingle()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @GetMapping("/regen")
|
// @GetMapping("/regen")
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package space.luminic.budgerapp.controllers
|
package space.luminic.budgerapp.controllers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.security.core.Authentication
|
import org.springframework.security.core.Authentication
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.*
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import space.luminic.budgerapp.models.PushMessage
|
import space.luminic.budgerapp.models.PushMessage
|
||||||
import space.luminic.budgerapp.models.SubscriptionDTO
|
import space.luminic.budgerapp.models.SubscriptionDTO
|
||||||
import space.luminic.budgerapp.services.SubscriptionService
|
import space.luminic.budgerapp.services.SubscriptionService
|
||||||
@@ -28,17 +25,13 @@ class SubscriptionController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/subscribe")
|
@PostMapping("/subscribe")
|
||||||
fun subscribe(
|
suspend fun subscribe(
|
||||||
@RequestBody subscription: SubscriptionDTO,
|
@RequestBody subscription: SubscriptionDTO,
|
||||||
authentication: Authentication
|
authentication: Authentication
|
||||||
): Mono<String> {
|
): String {
|
||||||
return userService.getByUserNameWoPass(authentication.name)
|
val securityContext = ReactiveSecurityContextHolder.getContext().awaitSingle()
|
||||||
.flatMap { user ->
|
val user = userService.getByUserNameWoPass(securityContext.authentication.name)
|
||||||
subscriptionService.subscribe(subscription, user)
|
return subscriptionService.subscribe(subscription, user)
|
||||||
.thenReturn("Subscription successful")
|
|
||||||
}
|
|
||||||
.switchIfEmpty(Mono.just("User not found"))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/notifyAll")
|
@PostMapping("/notifyAll")
|
||||||
|
|||||||
@@ -11,13 +11,16 @@ class BudgetMapper(private val categoryMapper: CategoryMapper) : FromDocumentMap
|
|||||||
|
|
||||||
|
|
||||||
override fun fromDocument(document: Document): Budget {
|
override fun fromDocument(document: Document): Budget {
|
||||||
|
val spaceId = document.get("spaceDetails", Document::class.java)?.getObjectId("_id")?.toString()
|
||||||
|
val categoriesList = document.getList("categories", Document::class.java).orEmpty()
|
||||||
|
val incomeCategoriesList = document.getList("incomeCategories", Document::class.java).orEmpty()
|
||||||
return Budget(
|
return Budget(
|
||||||
id = document.getObjectId("_id").toString(),
|
id = document.getObjectId("_id").toString(),
|
||||||
space = Space(id = document.get("spaceDetails", Document::class.java).getObjectId("_id").toString()),
|
space = Space(id=spaceId),
|
||||||
name = document.getString("name"),
|
name = document.getString("name"),
|
||||||
dateFrom = document.getDate("dateFrom").toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
|
dateFrom = document.getDate("dateFrom").toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
|
||||||
dateTo = document.getDate("dateTo").toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
|
dateTo = document.getDate("dateTo").toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
|
||||||
categories = document.getList("categories", Document::class.java).map { cat ->
|
categories = categoriesList.map { cat ->
|
||||||
val categoryDetailed = document.getList("categoriesDetails", Document::class.java).first {
|
val categoryDetailed = document.getList("categoriesDetails", Document::class.java).first {
|
||||||
it.getObjectId("_id").toString() == cat.get("category", DBRef::class.java).id.toString()
|
it.getObjectId("_id").toString() == cat.get("category", DBRef::class.java).id.toString()
|
||||||
}
|
}
|
||||||
@@ -26,7 +29,7 @@ class BudgetMapper(private val categoryMapper: CategoryMapper) : FromDocumentMap
|
|||||||
currentLimit = cat.getDouble("currentLimit")
|
currentLimit = cat.getDouble("currentLimit")
|
||||||
)
|
)
|
||||||
}.toMutableList(),
|
}.toMutableList(),
|
||||||
incomeCategories = document.getList("incomeCategories", Document::class.java).map { cat ->
|
incomeCategories = incomeCategoriesList.map { cat ->
|
||||||
val categoryDetailed =
|
val categoryDetailed =
|
||||||
document.getList("incomeCategoriesDetails", Document::class.java).first { it ->
|
document.getList("incomeCategoriesDetails", Document::class.java).first { it ->
|
||||||
it.getObjectId("_id").toString() == cat.get("category", DBRef::class.java).id.toString()
|
it.getObjectId("_id").toString() == cat.get("category", DBRef::class.java).id.toString()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package space.luminic.budgerapp.services
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||||
import org.springframework.cache.annotation.Cacheable
|
import org.springframework.cache.annotation.Cacheable
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
@@ -11,14 +12,15 @@ import space.luminic.budgerapp.repos.UserRepo
|
|||||||
import space.luminic.budgerapp.utils.JWTUtil
|
import space.luminic.budgerapp.utils.JWTUtil
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.util.Date
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class AuthService(
|
class AuthService(
|
||||||
private val userRepository: UserRepo,
|
private val userRepository: UserRepo,
|
||||||
private val tokenService: TokenService,
|
private val tokenService: TokenService,
|
||||||
private val jwtUtil: JWTUtil
|
private val jwtUtil: JWTUtil,
|
||||||
|
private val userService: UserService
|
||||||
|
|
||||||
) {
|
) {
|
||||||
private val passwordEncoder = BCryptPasswordEncoder()
|
private val passwordEncoder = BCryptPasswordEncoder()
|
||||||
@@ -82,23 +84,20 @@ class AuthService(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable("tokens")
|
|
||||||
fun isTokenValid(token: String): Mono<User> {
|
@Cacheable(cacheNames = ["tokens"], key = "#token")
|
||||||
return tokenService.getToken(token)
|
suspend fun isTokenValid(token: String): User {
|
||||||
.flatMap { tokenDetails ->
|
val tokenDetails = tokenService.getToken(token).awaitFirstOrNull() ?: throw AuthException("Invalid token")
|
||||||
when {
|
when {
|
||||||
tokenDetails.status == TokenStatus.ACTIVE && tokenDetails.expiresAt.isAfter(LocalDateTime.now()) -> {
|
tokenDetails.status == TokenStatus.ACTIVE && tokenDetails.expiresAt.isAfter(LocalDateTime.now()) -> {
|
||||||
userRepository.findByUsername(tokenDetails.username)
|
return userService.getByUserNameWoPass(tokenDetails.username)
|
||||||
.switchIfEmpty(Mono.error(AuthException("User not found for token")))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
tokenService.revokeToken(token)
|
tokenService.revokeToken(tokenDetails.token)
|
||||||
.then(Mono.error(AuthException("Token expired or inactive")))
|
throw AuthException("Token expired or inactive")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.switchIfEmpty(Mono.error(AuthException("Token not found")))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,25 @@
|
|||||||
package space.luminic.budgerapp.services
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||||
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
import org.bson.Document
|
import org.bson.Document
|
||||||
import org.bson.types.ObjectId
|
import org.bson.types.ObjectId
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.cache.annotation.CacheEvict
|
import org.springframework.cache.annotation.CacheEvict
|
||||||
import org.springframework.cache.annotation.Cacheable
|
import org.springframework.cache.annotation.Cacheable
|
||||||
import org.springframework.context.ApplicationEventPublisher
|
|
||||||
import org.springframework.data.domain.Sort
|
import org.springframework.data.domain.Sort
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
|
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
|
||||||
import org.springframework.data.mongodb.core.query.Criteria
|
import org.springframework.data.mongodb.core.query.Criteria
|
||||||
import org.springframework.data.mongodb.core.query.isEqualTo
|
import org.springframework.data.mongodb.core.query.isEqualTo
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import reactor.core.publisher.Flux
|
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
import space.luminic.budgerapp.mappers.CategoryMapper
|
import space.luminic.budgerapp.mappers.CategoryMapper
|
||||||
import space.luminic.budgerapp.models.*
|
import space.luminic.budgerapp.models.Category
|
||||||
|
import space.luminic.budgerapp.models.CategoryType
|
||||||
|
import space.luminic.budgerapp.models.NotFoundException
|
||||||
|
import space.luminic.budgerapp.models.Space
|
||||||
import space.luminic.budgerapp.repos.BudgetRepo
|
import space.luminic.budgerapp.repos.BudgetRepo
|
||||||
import space.luminic.budgerapp.repos.CategoryRepo
|
import space.luminic.budgerapp.repos.CategoryRepo
|
||||||
|
|
||||||
@@ -33,13 +36,12 @@ class CategoryService(
|
|||||||
private val logger = LoggerFactory.getLogger(javaClass)
|
private val logger = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun findCategory(
|
||||||
fun findCategory(
|
|
||||||
space: Space? = null,
|
space: Space? = null,
|
||||||
id: String? = null,
|
id: String? = null,
|
||||||
name: String? = null,
|
name: String? = null,
|
||||||
tagCode: String? = null
|
tagCode: String? = null
|
||||||
): Mono<Category> {
|
): Category {
|
||||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||||
val unwindSpace = unwind("spaceDetails")
|
val unwindSpace = unwind("spaceDetails")
|
||||||
val matchCriteria = mutableListOf<Criteria>()
|
val matchCriteria = mutableListOf<Criteria>()
|
||||||
@@ -51,7 +53,6 @@ class CategoryService(
|
|||||||
|
|
||||||
val match = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
val match = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||||
|
|
||||||
|
|
||||||
// val project = project("_id", "type", "name", "description", "icon")
|
// val project = project("_id", "type", "name", "description", "icon")
|
||||||
|
|
||||||
val aggregationBuilder = mutableListOf(
|
val aggregationBuilder = mutableListOf(
|
||||||
@@ -63,10 +64,9 @@ class CategoryService(
|
|||||||
val aggregation = newAggregation(aggregationBuilder)
|
val aggregation = newAggregation(aggregationBuilder)
|
||||||
return mongoTemplate.aggregate(
|
return mongoTemplate.aggregate(
|
||||||
aggregation, "categories", Document::class.java
|
aggregation, "categories", Document::class.java
|
||||||
).next()
|
).map { doc ->
|
||||||
.map { doc ->
|
|
||||||
categoryMapper.fromDocument(doc)
|
categoryMapper.fromDocument(doc)
|
||||||
}
|
}.awaitFirstOrNull() ?: throw NotFoundException("Category not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -122,25 +122,22 @@ class CategoryService(
|
|||||||
|
|
||||||
|
|
||||||
@CacheEvict(cacheNames = ["getAllCategories"], allEntries = true)
|
@CacheEvict(cacheNames = ["getAllCategories"], allEntries = true)
|
||||||
fun editCategory(space: Space, category: Category): Mono<Category> {
|
suspend fun editCategory(space: Space, category: Category): Category {
|
||||||
return findCategory(space, id = category.id) // Возвращаем Mono<Category>
|
val oldCategory = findCategory(space, id = category.id)
|
||||||
.flatMap { oldCategory ->
|
|
||||||
if (oldCategory.type.code != category.type.code) {
|
if (oldCategory.type.code != category.type.code) {
|
||||||
return@flatMap Mono.error<Category>(IllegalArgumentException("You cannot change category type"))
|
throw IllegalArgumentException("You cannot change category type")
|
||||||
}
|
}
|
||||||
category.space = space
|
category.space = space
|
||||||
categoryRepo.save(category) // Сохраняем категорию, если тип не изменился
|
return categoryRepo.save(category).awaitSingle() // Сохраняем категорию, если тип не изменился
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteCategory(space: Space, categoryId: String): Mono<String> {
|
suspend fun deleteCategory(space: Space, categoryId: String) {
|
||||||
return findCategory(space, categoryId).switchIfEmpty(
|
findCategory(space, categoryId)
|
||||||
Mono.error(IllegalArgumentException("Category with id: $categoryId not found"))
|
val transactions = financialService.getTransactions(space.id!!, categoryId = categoryId).awaitSingle()
|
||||||
).flatMap { categoryToDelete ->
|
val otherCategory = try {
|
||||||
financialService.getTransactions(space.id!!, categoryId = categoryId)
|
|
||||||
.flatMapMany { transactions ->
|
|
||||||
findCategory(space, name = "Другое")
|
findCategory(space, name = "Другое")
|
||||||
.switchIfEmpty(
|
} catch (nfe: NotFoundException) {
|
||||||
categoryRepo.save(
|
categoryRepo.save(
|
||||||
Category(
|
Category(
|
||||||
space = space,
|
space = space,
|
||||||
@@ -149,28 +146,32 @@ class CategoryService(
|
|||||||
description = "Категория для других трат",
|
description = "Категория для других трат",
|
||||||
icon = "🚮"
|
icon = "🚮"
|
||||||
)
|
)
|
||||||
)
|
).awaitSingle()
|
||||||
)
|
}
|
||||||
.flatMapMany { otherCategory ->
|
transactions.map { transaction ->
|
||||||
Flux.fromIterable(transactions).flatMap { transaction ->
|
|
||||||
transaction.category = otherCategory
|
transaction.category = otherCategory
|
||||||
financialService.editTransaction(transaction)
|
financialService.editTransaction(transaction)
|
||||||
}
|
}
|
||||||
}
|
val budgets = financialService.findProjectedBudgets(
|
||||||
}
|
ObjectId(space.id),
|
||||||
.then(
|
projectKeys = arrayOf(
|
||||||
financialService.findProjectedBudgets(ObjectId(space.id))
|
"_id",
|
||||||
.flatMapMany { budgets ->
|
"name",
|
||||||
Flux.fromIterable(budgets).flatMap { budget ->
|
"dateFrom",
|
||||||
|
"dateTo",
|
||||||
|
"space",
|
||||||
|
"spaceDetails",
|
||||||
|
"categories",
|
||||||
|
"categoriesDetails",
|
||||||
|
"incomeCategories",
|
||||||
|
"incomeCategoriesDetails"
|
||||||
|
)
|
||||||
|
).awaitSingle()
|
||||||
|
budgets.map { budget ->
|
||||||
budget.categories.removeIf { it.category.id == categoryId }
|
budget.categories.removeIf { it.category.id == categoryId }
|
||||||
budgetRepo.save(budget)
|
budgetRepo.save(budget)
|
||||||
}
|
}
|
||||||
}.collectList()
|
categoryRepo.deleteById(categoryId).awaitSingle()
|
||||||
)
|
|
||||||
.then(categoryRepo.deleteById(categoryId)) // Удаление категории
|
|
||||||
.thenReturn(categoryId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
|||||||
package space.luminic.budgerapp.services
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactive.awaitLast
|
||||||
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
import org.bson.Document
|
import org.bson.Document
|
||||||
import org.bson.types.ObjectId
|
import org.bson.types.ObjectId
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -13,7 +16,6 @@ import org.springframework.data.mongodb.core.aggregation.Aggregation.*
|
|||||||
import org.springframework.data.mongodb.core.query.Criteria
|
import org.springframework.data.mongodb.core.query.Criteria
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import reactor.core.publisher.Flux
|
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
import space.luminic.budgerapp.mappers.RecurrentMapper
|
import space.luminic.budgerapp.mappers.RecurrentMapper
|
||||||
import space.luminic.budgerapp.models.*
|
import space.luminic.budgerapp.models.*
|
||||||
@@ -73,26 +75,15 @@ class RecurrentService(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRecurrentsForBudget(space: Space, budget: Budget): Mono<Void> {
|
suspend fun createRecurrentsForBudget(space: Space, budget: Budget) {
|
||||||
val currentYearMonth = YearMonth.of(budget.dateFrom.year, budget.dateFrom.monthValue)
|
val currentYearMonth = YearMonth.of(budget.dateFrom.year, budget.dateFrom.monthValue)
|
||||||
val daysInCurrentMonth = currentYearMonth.lengthOfMonth()
|
val daysInCurrentMonth = currentYearMonth.lengthOfMonth()
|
||||||
|
|
||||||
val context = ReactiveSecurityContextHolder.getContext()
|
val context = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
||||||
.doOnNext { println("Security context: $it") }
|
?: throw IllegalStateException("SecurityContext is empty!")
|
||||||
.switchIfEmpty(Mono.error(IllegalStateException("SecurityContext is empty!")))
|
val user = userService.getByUserNameWoPass(context.authentication.name)
|
||||||
|
val recurrents = getRecurrents(space.id!!).awaitSingle()
|
||||||
return context
|
val transactions = recurrents.map { recurrent ->
|
||||||
.map { it.authentication }
|
|
||||||
.flatMap { authentication ->
|
|
||||||
val username = authentication.name
|
|
||||||
userService.getByUsername(username)
|
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
|
||||||
}
|
|
||||||
.flatMapMany { user ->
|
|
||||||
getRecurrents(space.id!!) // Теперь это Mono<List<Recurrent>>
|
|
||||||
.flatMapMany { Flux.fromIterable(it) } // Преобразуем List<Recurrent> в Flux<Recurrent>
|
|
||||||
.map { recurrent ->
|
|
||||||
// Определяем дату транзакции
|
|
||||||
val transactionDate = when {
|
val transactionDate = when {
|
||||||
recurrent.atDay in budget.dateFrom.dayOfMonth..daysInCurrentMonth -> {
|
recurrent.atDay in budget.dateFrom.dayOfMonth..daysInCurrentMonth -> {
|
||||||
currentYearMonth.atDay(recurrent.atDay)
|
currentYearMonth.atDay(recurrent.atDay)
|
||||||
@@ -107,7 +98,6 @@ class RecurrentService(
|
|||||||
currentYearMonth.plusMonths(1).atDay(extraDays)
|
currentYearMonth.plusMonths(1).atDay(extraDays)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем транзакцию
|
// Создаем транзакцию
|
||||||
Transaction(
|
Transaction(
|
||||||
space = space,
|
space = space,
|
||||||
@@ -120,11 +110,7 @@ class RecurrentService(
|
|||||||
type = TransactionType("PLANNED", "Запланированные")
|
type = TransactionType("PLANNED", "Запланированные")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
transactionRepo.saveAll(transactions).awaitLast()
|
||||||
.collectList() // Собираем все транзакции в список
|
|
||||||
.flatMap { transactions ->
|
|
||||||
transactionRepo.saveAll(transactions).then() // Сохраняем все транзакции разом и возвращаем Mono<Void>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -139,6 +125,4 @@ class RecurrentService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
package space.luminic.budgerapp.services
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.reactive.awaitFirst
|
||||||
|
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||||
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
import org.bson.Document
|
import org.bson.Document
|
||||||
import org.bson.types.ObjectId
|
import org.bson.types.ObjectId
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||||
@@ -8,8 +14,7 @@ import org.springframework.data.mongodb.core.query.Criteria
|
|||||||
import org.springframework.data.mongodb.core.query.Query
|
import org.springframework.data.mongodb.core.query.Query
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import reactor.core.publisher.Flux
|
import space.luminic.budgerapp.configs.AuthException
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import space.luminic.budgerapp.models.*
|
import space.luminic.budgerapp.models.*
|
||||||
import space.luminic.budgerapp.repos.*
|
import space.luminic.budgerapp.repos.*
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
@@ -30,43 +35,31 @@ class SpaceService(
|
|||||||
private val tagRepo: TagRepo
|
private val tagRepo: TagRepo
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun isValidRequest(spaceId: String): Mono<Space> {
|
suspend fun isValidRequest(spaceId: String): Space {
|
||||||
return ReactiveSecurityContextHolder.getContext()
|
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
||||||
.map { it.authentication }
|
?: throw AuthException("Authentication failed")
|
||||||
.flatMap { authentication ->
|
val authentication = securityContextHolder.authentication
|
||||||
|
|
||||||
val username = authentication.name
|
val username = authentication.name
|
||||||
// Получаем пользователя по имени
|
// Получаем пользователя по имени
|
||||||
userService.getByUsername(username)
|
val user = userService.getByUsername(username)
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
val space = getSpace(spaceId)
|
||||||
.flatMap { user ->
|
|
||||||
// Получаем пространство по ID
|
|
||||||
getSpace(spaceId)
|
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for id: $spaceId")))
|
|
||||||
.flatMap { space ->
|
|
||||||
// Проверяем доступ пользователя к пространству
|
// Проверяем доступ пользователя к пространству
|
||||||
if (space.users.none { it.id.toString() == user.id }) {
|
return if (space.users.none { it.id.toString() == user.id }) {
|
||||||
return@flatMap Mono.error<Space>(IllegalArgumentException("User does not have access to this Space"))
|
throw IllegalArgumentException("User does not have access to this Space")
|
||||||
}
|
} else space
|
||||||
// Если проверка прошла успешно, возвращаем пространство
|
|
||||||
Mono.just(space)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSpaces(): Mono<List<Space>> {
|
suspend fun getSpaces(): List<Space> {
|
||||||
return ReactiveSecurityContextHolder.getContext()
|
val securityContext = ReactiveSecurityContextHolder.getContext().awaitSingle()
|
||||||
.map { it.authentication }
|
val authentication = securityContext.authentication
|
||||||
.flatMap { authentication ->
|
|
||||||
val username = authentication.name
|
val username = authentication.name
|
||||||
userService.getByUsername(username)
|
val user = userService.getByUsername(username)
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
|
||||||
.flatMap { user ->
|
|
||||||
val userId = ObjectId(user.id!!)
|
val userId = ObjectId(user.id!!)
|
||||||
|
|
||||||
// Поиск пространств пользователя
|
|
||||||
|
|
||||||
|
|
||||||
// Агрегация для загрузки владельца и пользователей
|
// Агрегация для загрузки владельца и пользователей
|
||||||
val lookupOwner = lookup("users", "owner.\$id", "_id", "ownerDetails")
|
val lookupOwner = lookup("users", "owner.\$id", "_id", "ownerDetails")
|
||||||
val unwindOwner = unwind("ownerDetails")
|
val unwindOwner = unwind("ownerDetails")
|
||||||
@@ -77,7 +70,7 @@ class SpaceService(
|
|||||||
val matchStage = match(Criteria.where("usersDetails._id").`is`(userId))
|
val matchStage = match(Criteria.where("usersDetails._id").`is`(userId))
|
||||||
val aggregation = newAggregation(lookupOwner, unwindOwner, lookupUsers, matchStage)
|
val aggregation = newAggregation(lookupOwner, unwindOwner, lookupUsers, matchStage)
|
||||||
|
|
||||||
reactiveMongoTemplate.aggregate(aggregation, "spaces", Document::class.java)
|
return reactiveMongoTemplate.aggregate(aggregation, "spaces", Document::class.java)
|
||||||
.collectList()
|
.collectList()
|
||||||
.map { docs ->
|
.map { docs ->
|
||||||
docs.map { doc ->
|
docs.map { doc ->
|
||||||
@@ -102,79 +95,77 @@ class SpaceService(
|
|||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}.awaitFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getSpace(spaceId: String): Space {
|
||||||
}
|
return spaceRepo.findById(spaceId).awaitSingleOrNull()
|
||||||
}
|
?: throw IllegalArgumentException("SpaceId not found for spaceId: $spaceId")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSpace(spaceId: String): Mono<Space> {
|
suspend fun createSpace(space: Space, createCategories: Boolean): Space {
|
||||||
return spaceRepo.findById(spaceId)
|
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("SpaceId not found for spaceId: $spaceId")))
|
?: throw AuthException("Authentication failed")
|
||||||
}
|
val authentication = securityContextHolder.authentication
|
||||||
|
|
||||||
fun createSpace(space: Space, createCategories: Boolean): Mono<Space> {
|
|
||||||
return ReactiveSecurityContextHolder.getContext()
|
|
||||||
.map { it.authentication }
|
|
||||||
.flatMap { authentication ->
|
|
||||||
val username = authentication.name
|
val username = authentication.name
|
||||||
userService.getByUsername(username)
|
val user = userService.getByUsername(username)
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
|
||||||
.flatMap { user ->
|
|
||||||
space.owner = user
|
space.owner = user
|
||||||
space.users.add(user)
|
space.users.add(user)
|
||||||
spaceRepo.save(space).flatMap { savedSpace ->
|
val savedSpace = spaceRepo.save(space).awaitSingle()
|
||||||
if (!createCategories) {
|
return if (!createCategories) {
|
||||||
return@flatMap Mono.just(savedSpace) // Если не нужно создавать категории, просто возвращаем пространство
|
savedSpace // Если не нужно создавать категории, просто возвращаем пространство
|
||||||
}
|
} else {
|
||||||
|
val categories = reactiveMongoTemplate.find(Query(), Category::class.java, "categories-etalon")
|
||||||
reactiveMongoTemplate.find(Query(), Category::class.java, "categories-etalon")
|
|
||||||
.map { category ->
|
.map { category ->
|
||||||
category.copy(id = null, space = savedSpace) // Создаем новую копию
|
category.copy(id = null, space = savedSpace) // Создаем новую копию
|
||||||
}
|
}
|
||||||
.collectList() // Собираем в список перед сохранением
|
categoryRepo.saveAll(categories).awaitSingle()
|
||||||
.flatMap { categoryRepo.saveAll(it).collectList() } // Сохраняем и возвращаем список
|
savedSpace
|
||||||
.then(Mono.just(savedSpace)) // После сохранения всех категорий, возвращаем пространство
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteSpace(space: Space): Mono<Void> {
|
}
|
||||||
|
|
||||||
|
suspend fun deleteSpace(space: Space) {
|
||||||
val objectId = ObjectId(space.id)
|
val objectId = ObjectId(space.id)
|
||||||
|
|
||||||
return Mono.`when`(
|
coroutineScope {
|
||||||
financialService.findProjectedBudgets(objectId)
|
launch {
|
||||||
.flatMap { budgetRepo.deleteAll(it) },
|
val budgets = financialService.findProjectedBudgets(objectId).awaitFirstOrNull().orEmpty()
|
||||||
|
budgetRepo.deleteAll(budgets).awaitFirstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
financialService.getTransactions(objectId.toString())
|
launch {
|
||||||
.flatMap { transactionRepo.deleteAll(it) },
|
val transactions = financialService.getTransactions(objectId.toString()).awaitFirstOrNull().orEmpty()
|
||||||
|
transactionRepo.deleteAll(transactions).awaitFirstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
categoryService.getCategories(objectId.toString(), null, "name", "ASC")
|
launch {
|
||||||
.flatMap { categoryRepo.deleteAll(it) },
|
val categories =
|
||||||
|
categoryService.getCategories(objectId.toString(), null, "name", "ASC").awaitFirstOrNull().orEmpty()
|
||||||
|
categoryRepo.deleteAll(categories).awaitFirstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
recurrentService.getRecurrents(objectId.toString())
|
launch {
|
||||||
.flatMap { recurrentRepo.deleteAll(it) }
|
val recurrents = recurrentService.getRecurrents(objectId.toString()).awaitFirstOrNull().orEmpty()
|
||||||
).then(spaceRepo.deleteById(space.id!!)) // Удаление Space после завершения всех операций
|
recurrentRepo.deleteAll(recurrents).awaitFirstOrNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spaceRepo.deleteById(space.id!!).awaitFirstOrNull() // Удаляем Space после всех операций
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun createInviteSpace(spaceId: String): Mono<SpaceInvite> {
|
suspend fun createInviteSpace(spaceId: String): SpaceInvite {
|
||||||
return ReactiveSecurityContextHolder.getContext()
|
val securityContext = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
||||||
.map { it.authentication }
|
?: throw AuthException("Authentication failed")
|
||||||
.flatMap { authentication ->
|
val authentication = securityContext.authentication
|
||||||
val username = authentication.name
|
val user = userService.getByUsername(authentication.name)
|
||||||
userService.getByUsername(username)
|
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
|
||||||
.flatMap { user ->
|
|
||||||
spaceRepo.findById(spaceId)
|
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for id: $spaceId")))
|
|
||||||
.flatMap { space ->
|
|
||||||
if (space.users.none { it.id.toString() == user.id }) {
|
|
||||||
return@flatMap Mono.error<SpaceInvite>(IllegalArgumentException("User does not have access to this Space"))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
val space = getSpace(spaceId)
|
||||||
|
if (space.owner?.id != user.id) {
|
||||||
|
throw AuthException("Only owner could create invite into space")
|
||||||
|
}
|
||||||
val invite = SpaceInvite(
|
val invite = SpaceInvite(
|
||||||
UUID.randomUUID().toString().split("-")[0],
|
UUID.randomUUID().toString().split("-")[0],
|
||||||
user,
|
user,
|
||||||
@@ -182,91 +173,60 @@ class SpaceService(
|
|||||||
|
|
||||||
)
|
)
|
||||||
space.invites.add(invite)
|
space.invites.add(invite)
|
||||||
|
spaceRepo.save(space).awaitFirstOrNull()
|
||||||
// Сохраняем изменения и возвращаем созданное приглашение
|
// Сохраняем изменения и возвращаем созданное приглашение
|
||||||
spaceRepo.save(space).thenReturn(invite)
|
return invite
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun acceptInvite(code: String): Mono<Space> {
|
suspend fun acceptInvite(code: String): Space {
|
||||||
return ReactiveSecurityContextHolder.getContext()
|
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
||||||
.map { it.authentication }
|
?: throw AuthException("Authentication failed")
|
||||||
.flatMap { authentication ->
|
val user = userService.getByUsername(securityContextHolder.authentication.name)
|
||||||
val username = authentication.name
|
|
||||||
userService.getByUsername(username)
|
val space = spaceRepo.findSpaceByInvites(code).awaitFirstOrNull()
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
?: throw IllegalArgumentException("Space with invite code: $code not found")
|
||||||
.flatMap { user ->
|
|
||||||
spaceRepo.findSpaceByInvites(code)
|
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space with invite code: $code not found")))
|
|
||||||
.flatMap { space ->
|
|
||||||
val invite = space.invites.find { it.code == code }
|
val invite = space.invites.find { it.code == code }
|
||||||
|
|
||||||
// Проверяем, есть ли инвайт и не истек ли он
|
// Проверяем, есть ли инвайт и не истек ли он
|
||||||
if (invite == null || invite.activeTill.isBefore(LocalDateTime.now())) {
|
if (invite == null || invite.activeTill.isBefore(LocalDateTime.now())) {
|
||||||
return@flatMap Mono.error<Space>(IllegalArgumentException("Invite is invalid or expired"))
|
throw IllegalArgumentException("Invite is invalid or expired")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, не является ли пользователь уже участником
|
// Проверяем, не является ли пользователь уже участником
|
||||||
if (space.users.any { it.id == user.id }) {
|
if (space.users.any { it.id == user.id }) {
|
||||||
return@flatMap Mono.error<Space>(IllegalArgumentException("User is already a member of this Space"))
|
throw IllegalArgumentException("User is already a member of this Space")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем пользователя и удаляем использованный инвайт
|
// Добавляем пользователя и удаляем использованный инвайт
|
||||||
space.users.add(user)
|
space.users.add(user)
|
||||||
space.invites.remove(invite)
|
space.invites.remove(invite)
|
||||||
|
|
||||||
spaceRepo.save(space)
|
return spaceRepo.save(space).awaitFirst()
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun leaveSpace(spaceId: String): Mono<Void> {
|
|
||||||
return ReactiveSecurityContextHolder.getContext()
|
suspend fun leaveSpace(spaceId: String) {
|
||||||
.map { it.authentication }
|
val securityContext = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
||||||
.flatMap { authentication ->
|
?: throw AuthException("Authentication failed")
|
||||||
val username = authentication.name
|
val user = userService.getByUsername(securityContext.authentication.name)
|
||||||
userService.getByUsername(username)
|
val space = getSpace(spaceId)
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
|
||||||
.flatMap { user ->
|
|
||||||
spaceRepo.findById(spaceId)
|
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for id: $spaceId")))
|
|
||||||
.flatMap { space ->
|
|
||||||
if (space.users.none { it.id.toString() == user.id }) {
|
|
||||||
return@flatMap Mono.error<Void>(IllegalArgumentException("User does not have access to this Space"))
|
|
||||||
}
|
|
||||||
// Удаляем пользователя из массива
|
// Удаляем пользователя из массива
|
||||||
space.users.removeIf { it.id == user.id }
|
space.users.removeIf { it.id == user.id }
|
||||||
// Сохраняем изменения
|
// Сохраняем изменения
|
||||||
spaceRepo.save(space).then() // .then() для Mono<Void>
|
spaceRepo.save(space).awaitFirst()
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun kickMember(spaceId: String, kickedUsername: String): Mono<Void> {
|
|
||||||
return ReactiveSecurityContextHolder.getContext()
|
suspend fun kickMember(spaceId: String, kickedUsername: String) {
|
||||||
.map { it.authentication }
|
val securityContext = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
||||||
.flatMap { authentication ->
|
?: throw AuthException("Authentication failed")
|
||||||
val username = authentication.name
|
val currentUser = userService.getByUsername(securityContext.authentication.name)
|
||||||
// Получаем текущего пользователя
|
//проверяем что кикнутый пользователь сушествует
|
||||||
userService.getByUsername(username)
|
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
|
||||||
.flatMap { user ->
|
|
||||||
// Получаем пользователя, которого нужно исключить
|
|
||||||
userService.getByUsername(kickedUsername)
|
userService.getByUsername(kickedUsername)
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $kickedUsername")))
|
val space = getSpace(spaceId)
|
||||||
.flatMap { kickedUser ->
|
if (space.owner?.id != currentUser.id) {
|
||||||
// Получаем пространство
|
throw IllegalArgumentException("Only owners allowed for this action")
|
||||||
spaceRepo.findById(spaceId)
|
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for id: $spaceId")))
|
|
||||||
.flatMap { space ->
|
|
||||||
// Проверяем, является ли текущий пользователь владельцем
|
|
||||||
if (space.owner?.id != user.id) {
|
|
||||||
return@flatMap Mono.error<Void>(IllegalArgumentException("Only owners allowed for this action"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, что пользователь, которого нужно исключить, присутствует в списке пользователей
|
// Проверяем, что пользователь, которого нужно исключить, присутствует в списке пользователей
|
||||||
@@ -275,17 +235,14 @@ class SpaceService(
|
|||||||
// Удаляем пользователя из пространства
|
// Удаляем пользователя из пространства
|
||||||
space.users.removeIf { it.username == kickedUsername }
|
space.users.removeIf { it.username == kickedUsername }
|
||||||
// Сохраняем изменения
|
// Сохраняем изменения
|
||||||
return@flatMap spaceRepo.save(space).then()
|
spaceRepo.save(space).awaitSingle()
|
||||||
} else {
|
} else {
|
||||||
return@flatMap Mono.error<Void>(IllegalArgumentException("User not found in this space"))
|
throw IllegalArgumentException("User not found in this space")
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findTag(space: Space, tagCode: String): Mono<Tag> {
|
|
||||||
|
suspend fun findTag(space: Space, tagCode: String): Tag? {
|
||||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||||
val unwindSpace = unwind("spaceDetails")
|
val unwindSpace = unwind("spaceDetails")
|
||||||
val matchCriteria = mutableListOf<Criteria>()
|
val matchCriteria = mutableListOf<Criteria>()
|
||||||
@@ -310,37 +267,32 @@ class SpaceService(
|
|||||||
code = doc.getString("code"),
|
code = doc.getString("code"),
|
||||||
name = doc.getString("name")
|
name = doc.getString("name")
|
||||||
)
|
)
|
||||||
|
}.awaitSingleOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
suspend fun createTag(space: Space, tag: Tag): Tag {
|
||||||
|
|
||||||
fun createTag(space: Space, tag: Tag): Mono<Tag> {
|
|
||||||
tag.space = space
|
tag.space = space
|
||||||
return findTag(space, tag.code)
|
val existedTag = findTag(space, tag.code)
|
||||||
.flatMap { existingTag ->
|
return existedTag?.let {
|
||||||
Mono.error<Tag>(IllegalArgumentException("Tag with code ${existingTag.code} already exists"))
|
throw IllegalArgumentException("Tag with code ${tag.code} already exists")
|
||||||
}
|
} ?: tagRepo.save(tag).awaitFirst()
|
||||||
.switchIfEmpty(tagRepo.save(tag))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteTag(space: Space, tagCode: String): Mono<Void> {
|
suspend fun deleteTag(space: Space, tagCode: String) {
|
||||||
return findTag(space, tagCode)
|
val existedTag = findTag(space, tagCode) ?: throw NoSuchElementException("Tag with code $tagCode not found")
|
||||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Tag with code $tagCode not found")))
|
val categoriesWithTag =
|
||||||
.flatMap { tag ->
|
categoryService.getCategories(space.id!!, sortBy = "name", direction = "ASC", tagCode = existedTag.code)
|
||||||
categoryService.getCategories(space.id!!, sortBy = "name", direction = "ASC", tagCode = tag.code)
|
.awaitSingleOrNull().orEmpty()
|
||||||
.flatMapMany { cats ->
|
categoriesWithTag.map { cat ->
|
||||||
Flux.fromIterable(cats)
|
|
||||||
.map { cat ->
|
|
||||||
cat.tags.removeIf { it.code == tagCode } // Изменяем список тегов
|
cat.tags.removeIf { it.code == tagCode } // Изменяем список тегов
|
||||||
cat
|
cat
|
||||||
}
|
}
|
||||||
.flatMap { categoryRepo.save(it) } // Сохраняем обновлённые категории
|
categoryRepo.saveAll(categoriesWithTag).awaitFirst() // Сохраняем обновлённые категории
|
||||||
}
|
tagRepo.deleteById(existedTag.id!!).awaitFirst()
|
||||||
.then(tagRepo.deleteById(tag.id!!)) // Удаляем тег только после обновления категорий
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTags(space: Space): Mono<List<Tag>> {
|
|
||||||
|
suspend fun getTags(space: Space): List<Tag> {
|
||||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||||
val unwindSpace = unwind("spaceDetails")
|
val unwindSpace = unwind("spaceDetails")
|
||||||
val matchCriteria = mutableListOf<Criteria>()
|
val matchCriteria = mutableListOf<Criteria>()
|
||||||
@@ -363,27 +315,30 @@ class SpaceService(
|
|||||||
docs.map { doc ->
|
docs.map { doc ->
|
||||||
Tag(
|
Tag(
|
||||||
id = doc.getObjectId("_id").toString(),
|
id = doc.getObjectId("_id").toString(),
|
||||||
space = Space(id = doc.get("spaceDetails", Document::class.java).getObjectId("_id").toString()),
|
space = Space(
|
||||||
|
id = doc.get("spaceDetails", Document::class.java).getObjectId("_id").toString()
|
||||||
|
),
|
||||||
code = doc.getString("code"),
|
code = doc.getString("code"),
|
||||||
name = doc.getString("name")
|
name = doc.getString("name")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.awaitSingleOrNull().orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun regenSpaceCategory(): Mono<Category> {
|
// fun regenSpaceCategory(): Mono<Category> {
|
||||||
return getSpace("67af3c0f652da946a7dd9931")
|
// return getSpace("67af3c0f652da946a7dd9931")
|
||||||
.flatMap { space ->
|
// .flatMap { space ->
|
||||||
categoryService.findCategory(id = "677bc767c7857460a491bd4f")
|
// categoryService.findCategory(id = "677bc767c7857460a491bd4f")
|
||||||
.flatMap { category -> // заменил map на flatMap
|
// .flatMap { category -> // заменил map на flatMap
|
||||||
category.space = space
|
// category.space = space
|
||||||
category.name = "Сбережения"
|
// category.name = "Сбережения"
|
||||||
category.description = "Отчисления в накопления или инвестиционные счета"
|
// category.description = "Отчисления в накопления или инвестиционные счета"
|
||||||
category.icon = "💰"
|
// category.icon = "💰"
|
||||||
categoryRepo.save(category) // теперь возвращаем Mono<Category>
|
// categoryRepo.save(category) // теперь возвращаем Mono<Category>
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// fun regenSpaces(): Mono<List<Space>> {
|
// fun regenSpaces(): Mono<List<Space>> {
|
||||||
// return spaceRepo.findAll()
|
// return spaceRepo.findAll()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package space.luminic.budgerapp.services
|
|||||||
|
|
||||||
import com.interaso.webpush.VapidKeys
|
import com.interaso.webpush.VapidKeys
|
||||||
import com.interaso.webpush.WebPushService
|
import com.interaso.webpush.WebPushService
|
||||||
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -70,7 +71,7 @@ class SubscriptionService(private val subscriptionRepo: SubscriptionRepo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun subscribe(subscriptionDTO: SubscriptionDTO, user: User): Mono<String> {
|
suspend fun subscribe(subscriptionDTO: SubscriptionDTO, user: User): String {
|
||||||
val subscription = Subscription(
|
val subscription = Subscription(
|
||||||
id = null,
|
id = null,
|
||||||
user = user,
|
user = user,
|
||||||
@@ -80,18 +81,15 @@ class SubscriptionService(private val subscriptionRepo: SubscriptionRepo) {
|
|||||||
isActive = true
|
isActive = true
|
||||||
)
|
)
|
||||||
|
|
||||||
return subscriptionRepo.save(subscription)
|
return try {
|
||||||
.flatMap { savedSubscription ->
|
val savedSubscription = subscriptionRepo.save(subscription).awaitSingle()
|
||||||
Mono.just("Subscription created with ID: ${savedSubscription.id}")
|
"Subscription created with ID: ${savedSubscription.id}"
|
||||||
}
|
} catch (e: DuplicateKeyException) {
|
||||||
.onErrorResume(DuplicateKeyException::class.java) {
|
|
||||||
logger.info("Subscription already exists. Skipping.")
|
logger.info("Subscription already exists. Skipping.")
|
||||||
Mono.just("Subscription already exists. Skipping.")
|
"Subscription already exists. Skipping."
|
||||||
}
|
} catch (e: Exception) {
|
||||||
.onErrorResume { e ->
|
|
||||||
logger.error("Error while saving subscription: ${e.message}")
|
logger.error("Error while saving subscription: ${e.message}")
|
||||||
Mono.error(RuntimeException("Error while saving subscription"))
|
throw RuntimeException("Error while saving subscription")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
package space.luminic.budgerapp.services
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
import org.springframework.cache.annotation.CacheEvict
|
import org.springframework.cache.annotation.CacheEvict
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
@@ -25,15 +26,12 @@ class TokenService(private val tokenRepository: TokenRepo) {
|
|||||||
return tokenRepository.findByToken(token)
|
return tokenRepository.findByToken(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
@CacheEvict("tokens", allEntries = true)
|
|
||||||
fun revokeToken(token: String): Mono<Void> {
|
|
||||||
return tokenRepository.findByToken(token)
|
|
||||||
.switchIfEmpty(Mono.error(Exception("Token not found")))
|
|
||||||
.flatMap { existingToken ->
|
|
||||||
val updatedToken = existingToken.copy(status = TokenStatus.REVOKED)
|
|
||||||
tokenRepository.save(updatedToken).then()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fun revokeToken(token: String) {
|
||||||
|
val tokenDetail =
|
||||||
|
tokenRepository.findByToken(token).block()!!
|
||||||
|
val updatedToken = tokenDetail.copy(status = TokenStatus.REVOKED)
|
||||||
|
tokenRepository.save(updatedToken).block()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package space.luminic.budgerapp.services
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.cache.annotation.Cacheable
|
import org.springframework.cache.annotation.Cacheable
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
@@ -14,13 +15,10 @@ class UserService(val userRepo: UserRepo) {
|
|||||||
val logger = LoggerFactory.getLogger(javaClass)
|
val logger = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Cacheable("users", key = "#username")
|
@Cacheable("users", key = "#username")
|
||||||
fun getByUsername(username: String): Mono<User> {
|
suspend fun getByUsername(username: String): User {
|
||||||
return userRepo.findByUsernameWOPassword(username).switchIfEmpty(
|
return userRepo.findByUsernameWOPassword(username).awaitSingleOrNull()
|
||||||
Mono.error(NotFoundException("User with username: $username not found"))
|
?: throw NotFoundException("User with username: $username not found")
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getById(id: String): Mono<User> {
|
fun getById(id: String): Mono<User> {
|
||||||
@@ -33,8 +31,9 @@ class UserService(val userRepo: UserRepo) {
|
|||||||
|
|
||||||
|
|
||||||
@Cacheable("users", key = "#username")
|
@Cacheable("users", key = "#username")
|
||||||
fun getByUserNameWoPass(username: String): Mono<User> {
|
suspend fun getByUserNameWoPass(username: String): User {
|
||||||
return userRepo.findByUsernameWOPassword(username)
|
return userRepo.findByUsernameWOPassword(username).awaitSingleOrNull()
|
||||||
|
?: throw NotFoundException("User with username: $username not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable("usersList")
|
@Cacheable("usersList")
|
||||||
|
|||||||
Reference in New Issue
Block a user