WIP: Convert budget package to Java
This commit is contained in:
parent
3d19155836
commit
fbf9ea68a7
10 changed files with 392 additions and 52 deletions
23
api/src/main/java/com/wbrawner/budgetserver/Utils.java
Normal file
23
api/src/main/java/com/wbrawner/budgetserver/Utils.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package com.wbrawner.budgetserver;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
public final class Utils {
|
||||
private static int[] CALENDAR_FIELDS = new int[]{
|
||||
Calendar.MILLISECOND,
|
||||
Calendar.SECOND,
|
||||
Calendar.MINUTE,
|
||||
Calendar.HOUR_OF_DAY,
|
||||
Calendar.DATE
|
||||
};
|
||||
|
||||
public static Date getFirstOfMonth() {
|
||||
GregorianCalendar calendar = new GregorianCalendar();
|
||||
for (int field : CALENDAR_FIELDS) {
|
||||
calendar.set(field, calendar.getActualMinimum(field));
|
||||
}
|
||||
return calendar.getTime();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.wbrawner.budgetserver.budget;
|
||||
|
||||
import com.wbrawner.budgetserver.category.Category;
|
||||
import com.wbrawner.budgetserver.transaction.Transaction;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@Entity
|
||||
public class Budget {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
private String name;
|
||||
private String description;
|
||||
private String currencyCode;
|
||||
@OneToMany(mappedBy = "budget")
|
||||
private final Set<Transaction> transactions = new TreeSet<>();
|
||||
@OneToMany(mappedBy = "budget")
|
||||
private final Set<Category> categories = new TreeSet<>();
|
||||
@OneToMany(mappedBy = "budget")
|
||||
private final Set<Transaction> users = new HashSet<>();
|
||||
|
||||
public Budget() {}
|
||||
|
||||
public Budget(String name, String description) {
|
||||
this(name, description, "USD");
|
||||
}
|
||||
|
||||
public Budget(String name, String description, String currencyCode) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.currencyCode = currencyCode;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return currencyCode;
|
||||
}
|
||||
|
||||
public void setCurrencyCode(String currencyCode) {
|
||||
this.currencyCode = currencyCode;
|
||||
}
|
||||
|
||||
public Set<Transaction> getTransactions() {
|
||||
return transactions;
|
||||
}
|
||||
|
||||
public Set<Category> getCategories() {
|
||||
return categories;
|
||||
}
|
||||
|
||||
public Set<Transaction> getUsers() {
|
||||
return users;
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package com.wbrawner.budgetserver.budget
|
||||
|
||||
import com.wbrawner.budgetserver.category.Category
|
||||
import com.wbrawner.budgetserver.permission.UserPermission
|
||||
import com.wbrawner.budgetserver.permission.UserPermissionRequest
|
||||
import com.wbrawner.budgetserver.permission.UserPermissionResponse
|
||||
import com.wbrawner.budgetserver.transaction.Transaction
|
||||
import java.util.*
|
||||
import javax.persistence.*
|
||||
|
||||
@Entity
|
||||
data class Budget(
|
||||
@Id @GeneratedValue(strategy = GenerationType.AUTO) val id: Long? = null,
|
||||
val name: String = "",
|
||||
val description: String? = null,
|
||||
val currencyCode: String? = null,
|
||||
@OneToMany(mappedBy = "budget") val transactions: MutableSet<Transaction> = TreeSet(),
|
||||
@OneToMany(mappedBy = "budget") val categories: MutableSet<Category> = TreeSet(),
|
||||
@OneToMany(mappedBy = "budget") val users: MutableSet<UserPermission> = mutableSetOf()
|
||||
)
|
||||
|
||||
data class NewBudgetRequest(val name: String, val description: String?, val users: Set<UserPermissionRequest>)
|
||||
|
||||
data class UpdateBudgetRequest(val name: String?, val description: String?, val users: Set<UserPermissionRequest>?)
|
||||
|
||||
data class BudgetResponse(val id: Long, val name: String, val description: String?, val users: List<UserPermissionResponse>) {
|
||||
constructor(budget: Budget, users: List<UserPermission>) : this(
|
||||
budget.id!!,
|
||||
budget.name,
|
||||
budget.description,
|
||||
users.map { UserPermissionResponse(it) }
|
||||
)
|
||||
}
|
||||
|
||||
data class BudgetBalanceResponse(val id: Long, val balance: Long)
|
|
@ -0,0 +1,11 @@
|
|||
package com.wbrawner.budgetserver.budget;
|
||||
|
||||
public class BudgetBalanceResponse {
|
||||
public final long id;
|
||||
public final long balance;
|
||||
|
||||
public BudgetBalanceResponse(long id, long balance) {
|
||||
this.id = id;
|
||||
this.balance = balance;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
package com.wbrawner.budgetserver.budget;
|
||||
|
||||
import com.wbrawner.budgetserver.permission.Permission;
|
||||
import com.wbrawner.budgetserver.permission.UserPermission;
|
||||
import com.wbrawner.budgetserver.permission.UserPermissionRepository;
|
||||
import com.wbrawner.budgetserver.transaction.TransactionRepository;
|
||||
import com.wbrawner.budgetserver.user.User;
|
||||
import com.wbrawner.budgetserver.user.UserRepository;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.Authorization;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.wbrawner.budgetserver.Utils.getFirstOfMonth;
|
||||
import static com.wbrawner.budgetserver.UtilsKt.getCurrentUser;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/budgets")
|
||||
@Api(value = "Budgets", tags = {"Budgets"}, authorizations = {@Authorization(value = "basic")})
|
||||
@Transactional
|
||||
public class BudgetController {
|
||||
private final BudgetRepository budgetRepository;
|
||||
private final TransactionRepository transactionRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final UserPermissionRepository userPermissionsRepository;
|
||||
|
||||
public BudgetController(
|
||||
BudgetRepository budgetRepository,
|
||||
TransactionRepository transactionRepository,
|
||||
UserRepository userRepository,
|
||||
UserPermissionRepository userPermissionsRepository
|
||||
) {
|
||||
this.budgetRepository = budgetRepository;
|
||||
this.transactionRepository = transactionRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.userPermissionsRepository = userPermissionsRepository;
|
||||
}
|
||||
|
||||
@GetMapping(value = "", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
@ApiOperation(value = "getBudgets", nickname = "getBudgets", tags = {"Budgets"})
|
||||
public ResponseEntity<List<BudgetResponse>> getBudgets(Integer page, Integer count) {
|
||||
User user = getCurrentUser();
|
||||
if (user == null) {
|
||||
return ResponseEntity.status(401).build();
|
||||
}
|
||||
|
||||
List<BudgetResponse> budgets = userPermissionsRepository.findAllByUser(
|
||||
getCurrentUser(),
|
||||
PageRequest.of(
|
||||
page != null ? page : 0,
|
||||
count != null ? count : 1000
|
||||
)
|
||||
)
|
||||
.stream()
|
||||
.map(userPermission -> {
|
||||
Budget budget = userPermission.getBudget();
|
||||
if (budget == null) {
|
||||
return null;
|
||||
}
|
||||
// Hibernate.initialize(budget);
|
||||
return new BudgetResponse(budget, userPermissionsRepository.findAllByBudget(budget, null));
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(budgets);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/{id}", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
@ApiOperation(value = "getBudget", nickname = "getBudget", tags = {"Budgets"})
|
||||
public ResponseEntity<BudgetResponse> getBudget(@PathVariable long id) {
|
||||
var user = getCurrentUser();
|
||||
if (user == null) {
|
||||
return ResponseEntity.status(401).build();
|
||||
}
|
||||
|
||||
var userPermission = userPermissionsRepository.findAllByUserAndBudget_Id(user, id, null).get(0);
|
||||
if (userPermission == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
var budget = userPermission.getBudget();
|
||||
if (budget == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(new BudgetResponse(budget, userPermissionsRepository.findAllByBudget(budget, null)));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/{id}/balance", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
@ApiOperation(value = "getBudgetBalance", nickname = "getBudgetBalance", tags = {"Budgets"})
|
||||
public ResponseEntity<BudgetBalanceResponse> getBudgetBalance(@PathVariable long id) {
|
||||
var user = getCurrentUser();
|
||||
if (user == null) {
|
||||
return ResponseEntity.status(401).build();
|
||||
}
|
||||
|
||||
var userPermission = userPermissionsRepository.findAllByUserAndBudget_Id(user, id, null).get(0);
|
||||
if (userPermission == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
var budget = userPermission.getBudget();
|
||||
if (budget == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
var balance = transactionRepository.sumBalanceByBudgetId(budget.getId(), getFirstOfMonth());
|
||||
return ResponseEntity.ok(new BudgetBalanceResponse(budget.getId(), balance));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/new", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
@ApiOperation(value = "newBudget", nickname = "newBudget", tags = {"Budgets"})
|
||||
public ResponseEntity<BudgetResponse> newBudget(@RequestBody BudgetRequest request) {
|
||||
final var budget = budgetRepository.save(new Budget(request.name, request.description));
|
||||
var users = request.getUsers()
|
||||
.stream()
|
||||
.map(userPermissionRequest -> {
|
||||
var user = userRepository.findById(userPermissionRequest.getUser()).orElse(null);
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return userPermissionsRepository.save(
|
||||
new UserPermission(budget, user, userPermissionRequest.getPermission())
|
||||
);
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
var currentUserIncluded = users.stream().anyMatch(userPermission ->
|
||||
userPermission.getUser().getId().equals(getCurrentUser().getId())
|
||||
);
|
||||
if (!currentUserIncluded) {
|
||||
users.add(
|
||||
userPermissionsRepository.save(
|
||||
new UserPermission(budget, getCurrentUser(), Permission.OWNER)
|
||||
)
|
||||
);
|
||||
}
|
||||
return ResponseEntity.ok(new BudgetResponse(budget, new ArrayList<>(users)));
|
||||
}
|
||||
|
||||
@PutMapping(value = "/{id}", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
@ApiOperation(value = "updateBudget", nickname = "updateBudget", tags = {"Budgets"})
|
||||
public ResponseEntity<BudgetResponse> updateBudget(@PathVariable long id, BudgetRequest request) {
|
||||
var user = getCurrentUser();
|
||||
if (user == null) {
|
||||
return ResponseEntity.status(401).build();
|
||||
}
|
||||
|
||||
var userPermission = userPermissionsRepository.findAllByUserAndBudget_Id(user, id, null).get(0);
|
||||
if (userPermission == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
var budget = userPermission.getBudget();
|
||||
if (budget == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
if (request.name != null) {
|
||||
budget.setName(request.name);
|
||||
}
|
||||
|
||||
if (request.description != null) {
|
||||
budget.setDescription(request.description);
|
||||
}
|
||||
|
||||
if (!request.getUsers().isEmpty()) {
|
||||
request.getUsers().forEach(userPermissionRequest -> {
|
||||
var requestedUser = userRepository.findById(userPermissionRequest.getUser()).orElse(null);
|
||||
if (requestedUser != null) {
|
||||
userPermissionsRepository.save(new UserPermission(budget, requestedUser, userPermissionRequest.getPermission()));
|
||||
}
|
||||
});
|
||||
}
|
||||
userPermissionsRepository.findAllByUserAndBudget(getCurrentUser() !!, budget, null)
|
||||
return ResponseEntity.ok(BudgetResponse(budgetRepository.save(budget), users))
|
||||
}
|
||||
|
||||
@DeleteMapping(value = "/{id}", produces = {MediaType.TEXT_PLAIN_VALUE})
|
||||
@ApiOperation(value = "deleteBudget", nickname = "deleteBudget", tags = {"Budgets"})
|
||||
open fun
|
||||
|
||||
deleteBudget(@PathVariable id:Long):ResponseEntity<Unit>
|
||||
|
||||
{
|
||||
val budget = userPermissionsRepository.findAllByUserAndBudget_Id(getCurrentUser() !!, id, null)
|
||||
.firstOrNull()
|
||||
?.budget
|
||||
?:return ResponseEntity.notFound().build()
|
||||
budgetRepository.delete(budget)
|
||||
return ResponseEntity.ok().build()
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ import javax.transaction.Transactional
|
|||
@RequestMapping("/budgets")
|
||||
@Api(value = "Budgets", tags = ["Budgets"], authorizations = [Authorization("basic")])
|
||||
@Transactional
|
||||
open class BudgetController(
|
||||
open class BudgetControllerKt(
|
||||
private val budgetRepository: BudgetRepository,
|
||||
private val transactionRepository: TransactionRepository,
|
||||
private val userRepository: UserRepository,
|
||||
|
@ -60,8 +60,8 @@ open class BudgetController(
|
|||
|
||||
@PostMapping("/new", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
@ApiOperation(value = "newBudget", nickname = "newBudget", tags = ["Budgets"])
|
||||
open fun newBudget(@RequestBody request: NewBudgetRequest): ResponseEntity<BudgetResponse> {
|
||||
val budget = budgetRepository.save(Budget(name = request.name, description = request.description))
|
||||
open fun newBudget(@RequestBody request: BudgetRequest): ResponseEntity<BudgetResponse> {
|
||||
val budget = budgetRepository.save(Budget(request.name, request.description))
|
||||
val users = request.users
|
||||
.mapNotNull {
|
||||
userRepository.findById(it.user).orElse(null)?.let { user ->
|
||||
|
@ -78,21 +78,21 @@ open class BudgetController(
|
|||
)
|
||||
)
|
||||
}
|
||||
return ResponseEntity.ok(BudgetResponse(budget, users.toList()))
|
||||
return ResponseEntity.ok(BudgetResponse(budget, users.toMutableList()))
|
||||
}
|
||||
|
||||
@PutMapping("/{id}", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||
@ApiOperation(value = "updateBudget", nickname = "updateBudget", tags = ["Budgets"])
|
||||
open fun updateBudget(@PathVariable id: Long, request: UpdateBudgetRequest): ResponseEntity<BudgetResponse> {
|
||||
open fun updateBudget(@PathVariable id: Long, request: BudgetRequest): ResponseEntity<BudgetResponse> {
|
||||
var budget = userPermissionsRepository.findAllByUserAndBudget_Id(getCurrentUser()!!, id, null)
|
||||
.firstOrNull()
|
||||
?.budget
|
||||
?: return ResponseEntity.notFound().build()
|
||||
request.name?.let {
|
||||
budget = budget.copy(name = it)
|
||||
budget.name = it
|
||||
}
|
||||
request.description?.let {
|
||||
budget = budget.copy(description = request.description)
|
||||
budget.description = it
|
||||
}
|
||||
val users = request.users?.mapNotNull { req ->
|
||||
userRepository.findById(req.user).orElse(null)?.let {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package com.wbrawner.budgetserver.budget;
|
||||
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
public interface BudgetRepository extends PagingAndSortingRepository<Budget, Long> {
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package com.wbrawner.budgetserver.budget
|
||||
|
||||
import org.springframework.data.repository.PagingAndSortingRepository
|
||||
|
||||
interface BudgetRepository: PagingAndSortingRepository<Budget, Long> {
|
||||
fun findAllByIdIn(ids: List<Long>): List<Budget>
|
||||
// fun findByUsersContainsAndId(user: User, id: Long): Optional<Budget>
|
||||
// fun findByUsersContainsAndTransactionsContains(user: User, transaction: Transaction): Optional<Budget>
|
||||
// fun findByUsersContainsAndCategoriesContains(user: User, category: Category): Optional<Budget>
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.wbrawner.budgetserver.budget;
|
||||
|
||||
import com.wbrawner.budgetserver.permission.UserPermissionRequest;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class BudgetRequest {
|
||||
public final String name;
|
||||
public final String description;
|
||||
private final Set<UserPermissionRequest> users = new HashSet<>();
|
||||
|
||||
public BudgetRequest(String name, String description, Set<UserPermissionRequest> users) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.users.addAll(users);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<UserPermissionRequest> getUsers() {
|
||||
return Set.copyOf(users);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.wbrawner.budgetserver.budget;
|
||||
|
||||
import com.wbrawner.budgetserver.permission.UserPermission;
|
||||
import com.wbrawner.budgetserver.permission.UserPermissionResponse;
|
||||
import com.wbrawner.budgetserver.user.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BudgetResponse {
|
||||
public final long id;
|
||||
public final String name;
|
||||
public final String description;
|
||||
private final List<UserPermissionResponse> users;
|
||||
|
||||
public BudgetResponse(long id, String name, String description, List<UserPermissionResponse> users) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
public BudgetResponse(Budget budget, List<UserPermission> users) {
|
||||
this(
|
||||
Objects.requireNonNull(budget.getId()),
|
||||
budget.getName(),
|
||||
budget.getDescription(),
|
||||
users.stream()
|
||||
.map(UserPermissionResponse::new)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
|
||||
public List<UserPermissionResponse> getUsers() {
|
||||
return List.copyOf(users);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue