WIP: Convert budget package to Java

This commit is contained in:
William Brawner 2020-05-14 19:39:30 -07:00
parent 3d19155836
commit fbf9ea68a7
10 changed files with 392 additions and 52 deletions

View 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();
}
}

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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()
}
}

View file

@ -20,7 +20,7 @@ import javax.transaction.Transactional
@RequestMapping("/budgets") @RequestMapping("/budgets")
@Api(value = "Budgets", tags = ["Budgets"], authorizations = [Authorization("basic")]) @Api(value = "Budgets", tags = ["Budgets"], authorizations = [Authorization("basic")])
@Transactional @Transactional
open class BudgetController( open class BudgetControllerKt(
private val budgetRepository: BudgetRepository, private val budgetRepository: BudgetRepository,
private val transactionRepository: TransactionRepository, private val transactionRepository: TransactionRepository,
private val userRepository: UserRepository, private val userRepository: UserRepository,
@ -60,8 +60,8 @@ open class BudgetController(
@PostMapping("/new", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE]) @PostMapping("/new", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE])
@ApiOperation(value = "newBudget", nickname = "newBudget", tags = ["Budgets"]) @ApiOperation(value = "newBudget", nickname = "newBudget", tags = ["Budgets"])
open fun newBudget(@RequestBody request: NewBudgetRequest): ResponseEntity<BudgetResponse> { open fun newBudget(@RequestBody request: BudgetRequest): ResponseEntity<BudgetResponse> {
val budget = budgetRepository.save(Budget(name = request.name, description = request.description)) val budget = budgetRepository.save(Budget(request.name, request.description))
val users = request.users val users = request.users
.mapNotNull { .mapNotNull {
userRepository.findById(it.user).orElse(null)?.let { user -> 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]) @PutMapping("/{id}", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE])
@ApiOperation(value = "updateBudget", nickname = "updateBudget", tags = ["Budgets"]) @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) var budget = userPermissionsRepository.findAllByUserAndBudget_Id(getCurrentUser()!!, id, null)
.firstOrNull() .firstOrNull()
?.budget ?.budget
?: return ResponseEntity.notFound().build() ?: return ResponseEntity.notFound().build()
request.name?.let { request.name?.let {
budget = budget.copy(name = it) budget.name = it
} }
request.description?.let { request.description?.let {
budget = budget.copy(description = request.description) budget.description = it
} }
val users = request.users?.mapNotNull { req -> val users = request.users?.mapNotNull { req ->
userRepository.findById(req.user).orElse(null)?.let { userRepository.findById(req.user).orElse(null)?.let {

View file

@ -0,0 +1,6 @@
package com.wbrawner.budgetserver.budget;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface BudgetRepository extends PagingAndSortingRepository<Budget, Long> {
}

View file

@ -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>
}

View file

@ -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);
}
}

View file

@ -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);
}
}