wishlists + statics + some fixes

This commit is contained in:
xds
2025-03-03 10:38:07 +03:00
parent db0ada5ee8
commit 3b9f0e566c
16 changed files with 566 additions and 14 deletions

View File

@@ -9,7 +9,7 @@ import org.springframework.scheduling.annotation.EnableAsync
import org.springframework.scheduling.annotation.EnableScheduling
import java.util.TimeZone
@SpringBootApplication
@SpringBootApplication(scanBasePackages = ["space.luminic.budgerapp"])
@EnableCaching
@EnableAsync
@EnableScheduling

View File

@@ -1,7 +1,6 @@
package space.luminic.budgerapp.configs
import kotlinx.coroutines.reactor.mono
import org.slf4j.LoggerFactory
import org.springframework.http.HttpHeaders
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.authority.SimpleGrantedAuthority
@@ -16,8 +15,7 @@ import space.luminic.budgerapp.services.AuthService
@Component
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> {
@@ -27,7 +25,10 @@ class BearerTokenFilter(private val authService: AuthService) : SecurityContextS
"/api/auth/login",
"/api/auth/register",
"/api/auth/tgLogin"
) || exchange.request.path.value().startsWith("/api/actuator")
) || exchange.request.path.value().startsWith("/api/actuator") || exchange.request.path.value()
.startsWith("/api/static/")
|| exchange.request.path.value()
.startsWith("/api/wishlistexternal/")
) {
return chain.filter(exchange)
}

View File

@@ -26,7 +26,8 @@ class SecurityConfig(
.logout { it.disable() }
.authorizeExchange {
it.pathMatchers(HttpMethod.POST, "/auth/login", "/auth/register", "/auth/tgLogin").permitAll()
it.pathMatchers("/actuator/**").permitAll()
it.pathMatchers("/actuator/**", "/static/**").permitAll()
it.pathMatchers("/wishlistexternal/**").permitAll()
it.anyExchange().authenticated()
}
.addFilterAt(

View File

@@ -0,0 +1,18 @@
package space.luminic.budgerapp.configs
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Configuration
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
@Configuration
class StorageConfig(@Value("\${storage.location}") location: String) {
val rootLocation: Path = Paths.get(location)
init {
Files.createDirectories(rootLocation) // Создаем папку, если её нет
}
}

View File

@@ -0,0 +1,64 @@
package space.luminic.budgerapp.controllers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.springframework.core.io.PathResource
import org.springframework.core.io.Resource
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.http.codec.multipart.FilePart
import org.springframework.web.bind.annotation.*
import space.luminic.budgerapp.configs.StorageConfig
import space.luminic.budgerapp.models.NotFoundException
import space.luminic.budgerapp.services.StaticService
import java.nio.file.Files
import java.nio.file.Paths
@RestController
@RequestMapping("/static/{spaceId}/wishlists/{wishListItemId}")
class ImageController(private val staticService: StaticService, private val storageConfig: StorageConfig) {
@GetMapping("/{resourceId}")
suspend fun downloadFile(
@PathVariable spaceId: String,
@PathVariable wishListItemId: String,
@PathVariable resourceId: String
): ResponseEntity<Resource> {
return withContext(Dispatchers.IO) {
// val filePath = staticService.generatePathToFile(spaceId, wishListItemId, resourceId)
val filePath =
Paths.get(storageConfig.rootLocation.toString(), spaceId, "wishlists", wishListItemId, resourceId)
if (!Files.exists(filePath) || !Files.isReadable(filePath)) {
throw NotFoundException("File $filePath not found")
}
val resource = PathResource(filePath)
val contentType = Files.probeContentType(filePath) ?: "application/octet-stream"
ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.body(resource)
}
}
@PostMapping
suspend fun addImage(
@PathVariable spaceId: String,
@PathVariable wishListItemId: String,
@RequestPart("file") file: FilePart,
@RequestHeader("Content-Length") contentLength: Long,
): ResponseEntity<String> {
val maxSize = 5L * 1024 * 1024 // 5MB
println(file.headers().contentType)
if (contentLength > maxSize) {
return ResponseEntity
.status(HttpStatus.PAYLOAD_TOO_LARGE)
.body("Размер файла превышает 5MB")
}
return ResponseEntity.ok(staticService.saveFile(spaceId, wishListItemId, file))
}
}

View File

@@ -0,0 +1,90 @@
package space.luminic.budgerapp.controllers
import org.springframework.web.bind.annotation.*
import space.luminic.budgerapp.models.WishList
import space.luminic.budgerapp.models.WishListItem
import space.luminic.budgerapp.services.SpaceService
import space.luminic.budgerapp.services.WishListService
@RestController
@RequestMapping("/spaces/{spaceId}/wishlists")
class WishListController(
private val wishListService: WishListService,
private val spaceService: SpaceService
) {
@GetMapping
suspend fun findWishList(@PathVariable spaceId: String): List<WishList> {
spaceService.isValidRequest(spaceId)
return wishListService.findWishLists(spaceId)
}
@GetMapping("/{wishListId}")
suspend fun getWishList(@PathVariable spaceId: String, @PathVariable wishListId: String): WishList {
spaceService.isValidRequest(spaceId)
return wishListService.getList(wishListId)
}
@PostMapping
suspend fun createWishList(@PathVariable spaceId: String, @RequestBody wishList: WishList): WishList {
val space = spaceService.isValidRequest(spaceId)
return wishListService.createWishList(space, wishList)
}
@PatchMapping("/{wishListId}")
suspend fun updateWishList(
@PathVariable spaceId: String,
@PathVariable wishListId: String,
@RequestBody wishList: WishList
): WishList {
spaceService.isValidRequest(spaceId)
return wishListService.updateWishListInfo(wishList)
}
@PatchMapping("/{wishListId}/items/{itemId}")
suspend fun updateWishListItem(
@PathVariable spaceId: String,
@PathVariable wishListId: String,
@PathVariable itemId: String,
@RequestBody wishListItem: WishListItem
): WishList {
spaceService.isValidRequest(spaceId)
return wishListService.updateWishListItemInfo(wishListId, wishListItem)
}
@PostMapping("/{wishListId}/items")
suspend fun addItemToWishList(
@PathVariable spaceId: String,
@PathVariable wishListId: String,
@RequestBody wishListItem: WishListItem
): WishList {
spaceService.isValidRequest(spaceId)
return wishListService.addItemToWishList(wishListId, wishListItem)
}
@DeleteMapping("/{wishListId}/items/{wishListItemId}")
suspend fun removeItemFromWishList(
@PathVariable spaceId: String,
@PathVariable wishListId: String,
@PathVariable wishListItemId: String
): WishList {
spaceService.isValidRequest(spaceId)
return wishListService.removeItemFromWishList(wishListId, wishListItemId)
}
@DeleteMapping("/{wishListId}")
suspend fun deleteWishList(@PathVariable spaceId: String, @PathVariable wishListId: String) {
spaceService.isValidRequest(spaceId)
wishListService.deleteWishList(wishListId)
}
@PostMapping("/{wishListId}/{wishlistItemId}/reserve/_cancel")
suspend fun cancelReserve(
@PathVariable spaceId: String, @PathVariable wishListId: String,
@PathVariable wishlistItemId: String
) : WishList {
spaceService.isValidRequest(spaceId)
return wishListService.cancelReserve(wishListId, wishlistItemId)
}
}

View File

@@ -0,0 +1,35 @@
package space.luminic.budgerapp.controllers
import org.springframework.web.bind.annotation.*
import space.luminic.budgerapp.models.Reserve
import space.luminic.budgerapp.models.WishList
import space.luminic.budgerapp.models.WishListItem
import space.luminic.budgerapp.services.WishlistExternalService
@RestController
@RequestMapping("/wishlistexternal/{wishListId}")
class WishlistExternalController(private val wishlistExternalService: WishlistExternalService) {
@GetMapping
suspend fun getWishListInfo(@PathVariable wishListId: String): WishList {
return wishlistExternalService.getWishListInfo(wishListId)
}
@PostMapping("/{wishlistItemId}/reserve/_create")
suspend fun reserveItem(
@PathVariable wishListId: String,
@PathVariable wishlistItemId: String,
@RequestBody reservedBy: Reserve
): WishListItem {
return wishlistExternalService.reserveWishlistItem(wishListId, wishlistItemId, reservedBy)
}
@PostMapping("/{wishlistItemId}/reserve/_cancel")
suspend fun cancelReserve(
@PathVariable wishListId: String,
@PathVariable wishlistItemId: String,
@RequestBody reservedBy: Reserve
): WishListItem {
return wishlistExternalService.cancelReserve(wishListId, wishlistItemId, reservedBy)
}
}

View File

@@ -8,8 +8,6 @@ import java.time.ZoneId
@Component
class TransactionsMapper : FromDocumentMapper {
override fun fromDocument(document: Document): Transaction {
val categoryDocument = document.get("categoryDetails", Document::class.java)
val categoryTypeDocument = categoryDocument["type"] as Document

View File

@@ -0,0 +1,46 @@
package space.luminic.budgerapp.mappers
import org.bson.Document
import org.bson.types.ObjectId
import org.springframework.stereotype.Component
import space.luminic.budgerapp.models.*
import java.time.ZoneId
@Component
class WishListMapper : FromDocumentMapper {
override fun fromDocument(document: Document): WishList {
val spaceDoc = document.get("spaceDetails", Document::class.java)
val ownerDoc = document.get("ownerDetails", Document::class.java)
val itemsDocList = document.getList("itemsDetails", Document::class.java).orEmpty()
return WishList(
id = document.get("_id", ObjectId::class.java).toString(),
name = document.get("name", String::class.java),
description = document.get("description", String::class.java),
space = Space(id = spaceDoc.getObjectId("_id").toString()),
isPrivate = document.getBoolean("isPrivate"),
owner = User(
ownerDoc.getObjectId("_id").toString(),
firstName = ownerDoc.getString("firstName").toString()
),
items = itemsDocList.map {
val reserveDoc = it.get("reservedBy", Document::class.java)
WishListItem(
id = it.getObjectId("_id").toString(),
name = it.getString("name"),
description = it.getString("description"),
price = it.getDouble("price"),
link = it.getString("link"),
images = it.getList("images", String::class.java),
reservedBy = if (reserveDoc != null) Reserve(
reserveDoc.getString("aid"),
reserveDoc.getString("name")
) else null,
updatedAt = it.getDate("updatedAt").toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
createdAt = it.getDate("createdAt").toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
)
}.toMutableList(),
updatedAt = document.getDate("updatedAt").toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
createdAt = document.getDate("createdAt").toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
)
}
}

View File

@@ -0,0 +1,39 @@
package space.luminic.budgerapp.models
import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.DBRef
import org.springframework.data.mongodb.core.mapping.Document
import java.time.LocalDateTime
@Document(collection = "wishlists")
data class WishList(
@Id val id: String? = null,
@DBRef var space: Space? = null,
var name: String,
var description: String,
var isPrivate: Boolean,
@DBRef var owner: User? = null,
@DBRef var items: MutableList<WishListItem> = mutableListOf(),
var updatedAt: LocalDateTime = LocalDateTime.now(),
val createdAt: LocalDateTime = LocalDateTime.now()
)
@Document(collection = "wishlistItems")
data class WishListItem(
@Id val id: String? = null,
var name: String,
var description: String,
var price: Double,
var link: String,
var images: MutableList<String> = mutableListOf(),
var reservedBy: Reserve? = null,
var updatedAt: LocalDateTime = LocalDateTime.now(),
var createdAt: LocalDateTime = LocalDateTime.now()
)
data class Reserve(
val aid: String,
val name: String? = null,
)

View File

@@ -0,0 +1,13 @@
package space.luminic.budgerapp.repos
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.stereotype.Repository
import space.luminic.budgerapp.models.WishList
import space.luminic.budgerapp.models.WishListItem
@Repository
interface WishListRepo : ReactiveMongoRepository<WishList, String> {
}
@Repository
interface WishListItemRepo : ReactiveMongoRepository<WishListItem, String> {}

View File

@@ -23,7 +23,7 @@ import org.springframework.data.mongodb.core.ReactiveMongoTemplate
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
import org.springframework.data.mongodb.core.aggregation.DateOperators.DateToString
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.context.ReactiveSecurityContextHolder
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
@@ -169,16 +169,34 @@ class FinancialService(
}
}
private fun findBudgetCategory(transaction: Transaction, budget: Budget): BudgetCategory {
private suspend fun findBudgetCategory(transaction: Transaction, budget: Budget): BudgetCategory {
return if (transaction.category.type.code == "EXPENSE") {
budget.categories.firstOrNull { it.category.id == transaction.category.id }
?: throw RuntimeException("Budget category not found for expense")
?: addCategoryToBudget(transaction.category, budget)
} else {
budget.incomeCategories.firstOrNull { it.category.id == transaction.category.id }
?: throw RuntimeException("Budget category not found for income")
?:addCategoryToBudget(transaction.category, budget)
}
}
private suspend fun addCategoryToBudget(category: Category, budget: Budget): BudgetCategory {
val sums = getBudgetSumsByCategory(category.id!!, budget)
val categoryBudget = BudgetCategory(
currentSpent = sums.getDouble("instantAmount"),
currentPlanned = sums.getDouble("plannedAmount"),
currentLimit = sums.getDouble("plannedAmount"),
category = category
)
if (category.type.code == "EXPENSE") {
budget.categories.add(categoryBudget)
} else {
budget.incomeCategories.add(categoryBudget)
}
budgetRepo.save(budget).awaitSingle()
return categoryBudget
}
private suspend fun updateBudgetCategory(
transaction: Transaction,
budget: Budget,
@@ -826,7 +844,7 @@ class FinancialService(
suspend fun createTransaction(space: Space, transaction: Transaction): Transaction {
val securityContextHolder = SecurityContextHolder.getContext()
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingle()
val user = userService.getByUserNameWoPass(securityContextHolder.authentication.name)
if (space.users.none { it.id.toString() == user.id }) {
throw IllegalArgumentException("User does not have access to this Space")
@@ -903,7 +921,7 @@ class FinancialService(
suspend fun deleteTransaction(transaction: Transaction) = coroutineScope {
transactionsRepo.deleteById(transaction.id!!).awaitSingle()
transactionsRepo.deleteById(transaction.id!!).awaitFirstOrNull()
launch { updateBudgetOnDelete(transaction) }
}

View File

@@ -0,0 +1,29 @@
package space.luminic.budgerapp.services
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.reactor.awaitSingleOrNull
import kotlinx.coroutines.withContext
import org.springframework.http.codec.multipart.FilePart
import org.springframework.stereotype.Service
import space.luminic.budgerapp.configs.StorageConfig
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*
@Service
class StaticService(private val storageConfig: StorageConfig) {
suspend fun saveFile(spaceId: String, wishListItemId: String, filePart: FilePart): String {
val folder = Paths.get(storageConfig.rootLocation.toString(), spaceId, "wishlists", wishListItemId)
withContext(Dispatchers.IO) {
Files.createDirectories(folder)
}
val filename = UUID.randomUUID().toString().split("-")[0] + "." + filePart.filename().split(".").last()
val filePath =
folder.resolve(filename)
filePart.transferTo(filePath).awaitSingleOrNull()
return filePath.toString()
}
}

View File

@@ -0,0 +1,132 @@
package space.luminic.budgerapp.services
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.reactive.asFlow
import kotlinx.coroutines.reactor.awaitSingle
import kotlinx.coroutines.reactor.awaitSingleOrNull
import org.bson.Document
import org.bson.types.ObjectId
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.security.core.context.ReactiveSecurityContextHolder
import org.springframework.stereotype.Service
import space.luminic.budgerapp.mappers.WishListMapper
import space.luminic.budgerapp.models.NotFoundException
import space.luminic.budgerapp.models.Space
import space.luminic.budgerapp.models.WishList
import space.luminic.budgerapp.models.WishListItem
import space.luminic.budgerapp.repos.WishListItemRepo
import space.luminic.budgerapp.repos.WishListRepo
import java.time.LocalDateTime
@Service
class WishListService(
private val reactiveMongoTemplate: ReactiveMongoTemplate,
private val wishListMapper: WishListMapper,
private val wishListRepo: WishListRepo,
private val wishListItemRepo: WishListItemRepo,
private val userService: UserService
) {
private fun getLookupsAndUnwinds(): List<AggregationOperation> {
val aggregationOperation = mutableListOf<AggregationOperation>()
aggregationOperation.add(lookup("spaces", "space.\$id", "_id", "spaceDetails"))
aggregationOperation.add(unwind("spaceDetails"))
aggregationOperation.add(lookup("users", "owner.\$id", "_id", "ownerDetails"))
aggregationOperation.add(unwind("ownerDetails"))
aggregationOperation.add(lookup("wishlistItems", "items.\$id", "_id", "itemsDetails"))
return aggregationOperation
}
suspend fun findWishLists(spaceId: String): List<WishList> {
val user = userService.getByUserNameWoPass(
ReactiveSecurityContextHolder.getContext().awaitSingle().authentication.name
)
val match = match(
Criteria.where("spaceDetails._id").`is`(ObjectId(spaceId))
.andOperator(
Criteria.where("ownerDetails._id").`is`(ObjectId(user.id))
.orOperator(Criteria.where("isPrivate").`is`(false))
)
)
val aggregation = newAggregation(*(getLookupsAndUnwinds().toTypedArray()), match)
return reactiveMongoTemplate.aggregate(aggregation, "wishlists", Document::class.java)
.asFlow().map {
wishListMapper.fromDocument(it)
}.toList()
}
suspend fun getList(listId: String): WishList {
val user = userService.getByUserNameWoPass(
ReactiveSecurityContextHolder.getContext().awaitSingle().authentication.name
)
val match = match(
Criteria.where("_id").`is`(ObjectId(listId))
.andOperator(Criteria.where("ownerDetails._id").`is`(ObjectId(user.id)))
)
val aggregation = newAggregation(*(getLookupsAndUnwinds().toTypedArray()), match)
// val aggregation = newAggregation(lookupSpace, unwindSpace, lookupOwner, unwindOwner, lookupItems, match)
return reactiveMongoTemplate.aggregate(aggregation, "wishlists", Document::class.java)
.next().map {
wishListMapper.fromDocument(it)
}.awaitSingleOrNull() ?: throw NotFoundException("WishList with id $listId does not exist")
}
suspend fun createWishList(space: Space, wishList: WishList): WishList {
val user = userService.getByUserNameWoPass(
ReactiveSecurityContextHolder.getContext().awaitSingle().authentication.name
)
wishList.owner = user
wishList.space = space
return wishListRepo.save(wishList).awaitSingle()
}
suspend fun updateWishListInfo(wishList: WishList): WishList {
val oldStateOfWishList = getList(wishList.id!!)
val newStateOfWishList = oldStateOfWishList.copy(
name = wishList.name,
description = wishList.description,
isPrivate = wishList.isPrivate,
updatedAt = LocalDateTime.now()
)
return wishListRepo.save(newStateOfWishList).awaitSingle()
}
suspend fun updateWishListItemInfo(wishListId: String, wishListItem: WishListItem): WishList {
wishListItemRepo.save(wishListItem).awaitSingle()
return getList(wishListId)
}
suspend fun addItemToWishList(wishListId: String, item: WishListItem): WishList {
val wishList = getList(wishListId)
val savedItem = wishListItemRepo.save(item).awaitSingle()
wishList.items.add(savedItem)
return wishListRepo.save(wishList).awaitSingle()
}
suspend fun removeItemFromWishList(wishListId: String, itemId: String): WishList {
val wishList = getList(wishListId)
return if (wishList.items.removeIf { it.id == itemId }) wishListRepo.save(wishList).awaitSingle() else wishList
}
suspend fun deleteWishList(wishListId: String) {
wishListRepo.deleteById(wishListId).awaitSingleOrNull()
}
suspend fun cancelReserve(wishListId: String, wishlistItemId: String): WishList {
val wishList = getList(wishListId)
val wishlistItem = wishList.items.first { it.id == wishlistItemId }
return if (wishlistItem.reservedBy != null) {
wishlistItem.reservedBy = null
wishListItemRepo.save(wishlistItem).awaitSingle()
wishList
} else wishList
}
}

View File

@@ -0,0 +1,62 @@
package space.luminic.budgerapp.services
import kotlinx.coroutines.reactor.awaitSingle
import kotlinx.coroutines.reactor.awaitSingleOrNull
import org.bson.Document
import org.bson.types.ObjectId
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.stereotype.Service
import space.luminic.budgerapp.mappers.WishListMapper
import space.luminic.budgerapp.models.NotFoundException
import space.luminic.budgerapp.models.Reserve
import space.luminic.budgerapp.models.WishList
import space.luminic.budgerapp.models.WishListItem
import space.luminic.budgerapp.repos.WishListItemRepo
import java.time.LocalDateTime
@Service
class WishlistExternalService(
private val reactiveMongoTemplate: ReactiveMongoTemplate,
private val wishListMapper: WishListMapper,
private val wishListItemRepo: WishListItemRepo
) {
suspend fun getWishListInfo(wishListId: String): WishList {
val lookupSpace = lookup("spaces", "space.\$id", "_id", "spaceDetails")
val unwindSpace = unwind("spaceDetails")
val lookupOwner = lookup("users", "owner.\$id", "_id", "ownerDetails")
val unwindOwner = unwind("ownerDetails")
val lookupItems = lookup("wishlistItems", "items.\$id", "_id", "itemsDetails")
val match = match(
Criteria.where("_id").`is`(ObjectId(wishListId))
)
val aggregation = newAggregation(lookupSpace, unwindSpace, lookupOwner, unwindOwner, lookupItems, match)
return reactiveMongoTemplate.aggregate(aggregation, "wishlists", Document::class.java)
.next().map {
wishListMapper.fromDocument(it)
}.awaitSingleOrNull() ?: throw NotFoundException("WishList with id $wishListId does not exist")
}
suspend fun reserveWishlistItem(wishListId: String, wishlistItemId: String, reservedBy: Reserve): WishListItem {
val wishlist = getWishListInfo(wishListId)
val wishlistItem = wishlist.items.first { wishlistItem -> wishlistItem.id == wishlistItemId }
return if (wishlistItem.reservedBy == null) {
wishlistItem.reservedBy = reservedBy
wishlistItem.updatedAt = LocalDateTime.now()
wishListItemRepo.save(wishlistItem).awaitSingle()
} else throw IllegalArgumentException("Wishlist item already reserved")
}
suspend fun cancelReserve(wishListId: String, wishlistItemId: String, reservedBy: Reserve): WishListItem {
val wishlist = getWishListInfo(wishListId)
val wishlistItem = wishlist.items.first { wishlistItem -> wishlistItem.id == wishlistItemId }
return if (wishlistItem.reservedBy?.aid == reservedBy.aid) {
wishlistItem.reservedBy = null
wishListItemRepo.save(wishlistItem).awaitSingle()
} else if (wishlistItem.reservedBy == null) {
wishlistItem
} else throw IllegalArgumentException("Вы не можете отменить не свою бронь")
}
}

View File

@@ -20,6 +20,12 @@ logging.level.org.mongodb.driver.protocol.command = INFO
server.compression.enabled=true
server.compression.mime-types=application/json
# ???????????? ?????? ????? (?? ????????? 1 ??)
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
storage.location: static
# Expose prometheus, health, and info endpoints
#management.endpoints.web.exposure.include=prometheus,health,info