402 lines
14 KiB
Kotlin
402 lines
14 KiB
Kotlin
package space.luminic.budgerapp.controllers
|
|
|
|
import com.opencsv.CSVWriter
|
|
import kotlinx.coroutines.reactor.awaitSingle
|
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
|
import org.apache.commons.io.IOUtils.writer
|
|
import org.bson.Document
|
|
import org.slf4j.LoggerFactory
|
|
import org.springframework.http.HttpHeaders
|
|
import org.springframework.http.HttpStatus
|
|
import org.springframework.http.MediaType
|
|
import org.springframework.http.ResponseEntity
|
|
import org.springframework.web.bind.annotation.*
|
|
import space.luminic.budgerapp.controllers.BudgetController.LimitValue
|
|
import space.luminic.budgerapp.controllers.dtos.BudgetCreationDTO
|
|
import space.luminic.budgerapp.models.*
|
|
import space.luminic.budgerapp.services.*
|
|
import java.io.ByteArrayOutputStream
|
|
import java.io.OutputStreamWriter
|
|
import java.time.LocalDate
|
|
|
|
|
|
@RestController
|
|
@RequestMapping("/spaces")
|
|
class SpaceController(
|
|
private val spaceService: SpaceService,
|
|
private val financialService: FinancialService,
|
|
private val categoryService: CategoryService,
|
|
private val recurrentService: RecurrentService,
|
|
private val authService: AuthService
|
|
) {
|
|
|
|
private val log = LoggerFactory.getLogger(SpaceController::class.java)
|
|
|
|
|
|
data class SpaceCreateDTO(
|
|
val name: String,
|
|
val description: String,
|
|
val createCategories: Boolean,
|
|
)
|
|
|
|
@GetMapping
|
|
suspend fun getSpaces(): List<Space> {
|
|
return spaceService.getSpaces()
|
|
}
|
|
|
|
|
|
@PostMapping
|
|
suspend fun createSpace(@RequestBody space: SpaceCreateDTO): Space {
|
|
return spaceService.createSpace(
|
|
Space(name = space.name, description = space.description),
|
|
space.createCategories
|
|
)
|
|
}
|
|
|
|
|
|
@GetMapping("{spaceId}")
|
|
suspend fun getSpace(@PathVariable spaceId: String): Space {
|
|
return spaceService.getSpace(spaceId)
|
|
}
|
|
|
|
|
|
@DeleteMapping("/{spaceId}")
|
|
suspend fun deleteSpace(@PathVariable spaceId: String) {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return spaceService.deleteSpace(space)
|
|
}
|
|
|
|
|
|
@PostMapping("/{spaceId}/invite")
|
|
suspend fun inviteSpace(@PathVariable spaceId: String): SpaceInvite {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return spaceService.createInviteSpace(spaceId)
|
|
}
|
|
|
|
@PostMapping("/invite/{code}")
|
|
suspend fun acceptInvite(@PathVariable code: String): Space {
|
|
return spaceService.acceptInvite(code)
|
|
}
|
|
|
|
@DeleteMapping("/{spaceId}/leave")
|
|
suspend fun leaveSpace(@PathVariable spaceId: String) {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return spaceService.leaveSpace(spaceId)
|
|
}
|
|
|
|
@DeleteMapping("/{spaceId}/members/kick/{username}")
|
|
suspend fun kickMembers(@PathVariable spaceId: String, @PathVariable username: String) {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return spaceService.kickMember(spaceId, username)
|
|
}
|
|
|
|
|
|
//
|
|
//Budgets API
|
|
//
|
|
@GetMapping("/{spaceId}/budgets")
|
|
suspend fun getBudgets(@PathVariable spaceId: String): List<Budget> {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return financialService.getBudgets(spaceId).awaitSingleOrNull().orEmpty()
|
|
|
|
}
|
|
|
|
@GetMapping("/{spaceId}/budgets/{id}")
|
|
suspend fun getBudget(@PathVariable spaceId: String, @PathVariable id: String): BudgetDTO? {
|
|
log.info("Getting budget for spaceId=$spaceId, id=$id")
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return financialService.getBudget(spaceId, id)
|
|
}
|
|
|
|
@PostMapping("/{spaceId}/budgets")
|
|
suspend fun createBudget(
|
|
@PathVariable spaceId: String,
|
|
@RequestBody budgetCreationDTO: BudgetCreationDTO,
|
|
): Budget? {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return financialService.createBudget(
|
|
space,
|
|
budgetCreationDTO.budget,
|
|
budgetCreationDTO.createRecurrent
|
|
)
|
|
}
|
|
|
|
@DeleteMapping("/{spaceId}/budgets/{id}")
|
|
suspend fun deleteBudget(@PathVariable spaceId: String, @PathVariable id: String) {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
financialService.deleteBudget(spaceId, id)
|
|
|
|
}
|
|
|
|
@PostMapping("/{spaceId}/budgets/{budgetId}/categories/{catId}/limit")
|
|
suspend fun setCategoryLimit(
|
|
@PathVariable spaceId: String,
|
|
@PathVariable budgetId: String,
|
|
@PathVariable catId: String,
|
|
@RequestBody limit: LimitValue,
|
|
): BudgetCategory {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return financialService.setCategoryLimit(spaceId, budgetId, catId, limit.limit)
|
|
}
|
|
|
|
//
|
|
// Transactions API
|
|
//
|
|
@GetMapping("/{spaceId}/transactions")
|
|
fun getTransactions(
|
|
@PathVariable spaceId: String,
|
|
@RequestParam(value = "transaction_type") transactionType: String? = null,
|
|
@RequestParam(value = "category_type") categoryType: String? = null,
|
|
@RequestParam(value = "user_id") userId: String? = null,
|
|
@RequestParam(value = "is_child") isChild: Boolean? = null,
|
|
@RequestParam(value = "limit") limit: Int = 10,
|
|
@RequestParam(value = "offset") offset: Int = 0
|
|
): ResponseEntity<Any> {
|
|
try {
|
|
return ResponseEntity.ok(
|
|
financialService.getTransactions(
|
|
spaceId = spaceId,
|
|
transactionType = transactionType,
|
|
categoryType = categoryType,
|
|
userId = userId,
|
|
isChild = isChild,
|
|
limit = limit,
|
|
offset = offset
|
|
)
|
|
)
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
return ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
|
|
}
|
|
}
|
|
|
|
@GetMapping("/{spaceId}/transactions/csv")
|
|
suspend fun getTransactionsCSV(
|
|
@PathVariable spaceId: String,
|
|
@RequestParam(value = "transaction_type") transactionType: String? = null,
|
|
@RequestParam(value = "category_type") categoryType: String? = null,
|
|
@RequestParam(value = "user_id") userId: String? = null,
|
|
@RequestParam(value = "is_child") isChild: Boolean? = null,
|
|
@RequestParam(value = "limit") limit: Int = 20000,
|
|
@RequestParam(value = "offset") offset: Int = 0
|
|
): ResponseEntity<Any> {
|
|
try {
|
|
val bos = ByteArrayOutputStream()
|
|
val writer = CSVWriter(OutputStreamWriter(bos))
|
|
val CSVHeaders = arrayOf("id", "name", "category")
|
|
writer.writeNext(CSVHeaders)
|
|
financialService.getTransactions(
|
|
spaceId = spaceId,
|
|
transactionType = transactionType,
|
|
categoryType = categoryType,
|
|
userId = userId,
|
|
isChild = isChild,
|
|
limit = limit,
|
|
offset = offset
|
|
).awaitSingle().map {
|
|
val data = arrayOf(it.id, it.comment, it.category.name)
|
|
writer.writeNext(data)
|
|
}
|
|
writer.close()
|
|
|
|
val csvData = bos.toByteArray()
|
|
val headers = HttpHeaders()
|
|
headers.contentType = MediaType.parseMediaType("text/csv")
|
|
headers.setContentDispositionFormData("attachment", "pojos.csv")
|
|
|
|
return ResponseEntity(csvData, headers, HttpStatus.OK)
|
|
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
return ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
|
|
}
|
|
}
|
|
|
|
@GetMapping("/{spaceId}/category-predict")
|
|
suspend fun getTransactionCategoryPredict(
|
|
@PathVariable spaceId: String,
|
|
@RequestParam comment: String
|
|
): List<Category> {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return categoryService.getCategories(
|
|
"67af3c0f652da946a7dd9931",
|
|
"EXPENSE",
|
|
sortBy = "name",
|
|
direction = "ASC",
|
|
predict = comment
|
|
)
|
|
}
|
|
|
|
@GetMapping("/{spaceId}/transactions/{id}")
|
|
suspend fun getTransaction(
|
|
@PathVariable spaceId: String,
|
|
@PathVariable id: String
|
|
): Transaction {
|
|
return financialService.getTransactionById(id)
|
|
}
|
|
|
|
|
|
@PostMapping("/{spaceId}/transactions")
|
|
suspend fun createTransaction(@PathVariable spaceId: String, @RequestBody transaction: Transaction): Transaction {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return financialService.createTransaction(space, transaction)
|
|
|
|
|
|
}
|
|
|
|
@PutMapping("/{spaceId}/transactions/{id}")
|
|
suspend fun editTransaction(
|
|
@PathVariable spaceId: String, @PathVariable id: String, @RequestBody transaction: Transaction
|
|
): Transaction {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
transaction.space = space
|
|
return financialService.editTransaction(transaction, user)
|
|
}
|
|
|
|
@DeleteMapping("/{spaceId}/transactions/{id}")
|
|
suspend fun deleteTransaction(@PathVariable spaceId: String, @PathVariable id: String) {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
val transaction = financialService.getTransactionById(id)
|
|
financialService.deleteTransaction(transaction)
|
|
}
|
|
|
|
//
|
|
// Categories API
|
|
//
|
|
@GetMapping("/{spaceId}/categories")
|
|
suspend fun getCategories(
|
|
@PathVariable spaceId: String,
|
|
@RequestParam("type") type: String? = null,
|
|
@RequestParam("sort") sortBy: String = "name",
|
|
@RequestParam("direction") direction: String = "ASC"
|
|
): List<Category> {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return categoryService.getCategories(spaceId, type, sortBy, direction)
|
|
}
|
|
|
|
@GetMapping("/{spaceId}/categories/types")
|
|
fun getCategoriesTypes(@PathVariable spaceId: String): List<CategoryType> {
|
|
return categoryService.getCategoryTypes()
|
|
}
|
|
|
|
@PostMapping("/{spaceId}/categories")
|
|
suspend fun createCategory(
|
|
@PathVariable spaceId: String, @RequestBody category: Category
|
|
): Category {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return financialService.createCategory(space, category).awaitSingle()
|
|
}
|
|
|
|
@PutMapping("/{spaceId}/categories/{categoryId}")
|
|
suspend fun editCategory(
|
|
@PathVariable categoryId: String,
|
|
@RequestBody category: Category,
|
|
@PathVariable spaceId: String
|
|
): Category {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return categoryService.editCategory(space, category)
|
|
}
|
|
|
|
@DeleteMapping("/{spaceId}/categories/{categoryId}")
|
|
suspend fun deleteCategory(@PathVariable categoryId: String, @PathVariable spaceId: String) {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
categoryService.deleteCategory(space, categoryId, user)
|
|
}
|
|
|
|
@GetMapping("/{spaceId}/categories/tags")
|
|
suspend fun getTags(@PathVariable spaceId: String): List<Tag> {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return spaceService.getTags(space)
|
|
|
|
}
|
|
|
|
@PostMapping("/{spaceId}/categories/tags")
|
|
suspend fun createTags(@PathVariable spaceId: String, @RequestBody tag: Tag): Tag {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return spaceService.createTag(space, tag)
|
|
|
|
}
|
|
|
|
@DeleteMapping("/{spaceId}/categories/tags/{tagId}")
|
|
suspend fun deleteTags(@PathVariable spaceId: String, @PathVariable tagId: String) {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return spaceService.deleteTag(space, tagId)
|
|
}
|
|
|
|
@GetMapping("/{spaceId}/analytics/by-month")
|
|
suspend fun getCategoriesSumsByMonthsV2(@PathVariable spaceId: String): List<Document> {
|
|
return financialService.getCategorySummaries(spaceId, LocalDate.now().minusMonths(6))
|
|
}
|
|
|
|
|
|
//
|
|
// Recurrents API
|
|
//
|
|
|
|
@GetMapping("/{spaceId}/recurrents")
|
|
suspend fun getRecurrents(@PathVariable spaceId: String): List<Recurrent> {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return recurrentService.getRecurrents(spaceId)
|
|
|
|
}
|
|
|
|
|
|
@GetMapping("/{spaceId}/recurrents/{id}")
|
|
suspend fun getRecurrent(@PathVariable spaceId: String, @PathVariable id: String): Recurrent {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return recurrentService.getRecurrentById(space, id).awaitSingle()
|
|
}
|
|
|
|
@PostMapping("/{spaceId}/recurrent")
|
|
suspend fun createRecurrent(@PathVariable spaceId: String, @RequestBody recurrent: Recurrent): Recurrent {
|
|
val user = authService.getSecurityUser()
|
|
val space = spaceService.isValidRequest(spaceId, user)
|
|
return recurrentService.createRecurrent(space, recurrent).awaitSingle()
|
|
}
|
|
|
|
@PutMapping("/{spaceId}/recurrent/{id}")
|
|
suspend fun editRecurrent(
|
|
@PathVariable spaceId: String,
|
|
@PathVariable id: String,
|
|
@RequestBody recurrent: Recurrent
|
|
): Recurrent {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
return recurrentService.editRecurrent(recurrent).awaitSingle()
|
|
}
|
|
|
|
|
|
@DeleteMapping("/{spaceId}/recurrent/{id}")
|
|
suspend fun deleteRecurrent(@PathVariable spaceId: String, @PathVariable id: String) {
|
|
val user = authService.getSecurityUser()
|
|
spaceService.isValidRequest(spaceId, user)
|
|
recurrentService.deleteRecurrent(id).awaitSingle()
|
|
}
|
|
|
|
// @GetMapping("/regen")
|
|
// fun regenSpaces(): Mono<Category> {
|
|
// return spaceService.regenSpaceCategory()
|
|
// }
|
|
} |