Skip to content

Commit

Permalink
feature#25: Deposit endpoint and Transaction validator in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
ClementGib committed Feb 20, 2024
1 parent 3f23024 commit 783be73
Show file tree
Hide file tree
Showing 17 changed files with 641 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@
import com.cdx.bas.domain.bank.account.BankAccount;
import com.cdx.bas.domain.bank.account.BankAccountControllerPort;
import com.cdx.bas.domain.bank.account.BankAccountPersistencePort;
import com.cdx.bas.domain.transaction.Transaction;
import com.cdx.bas.domain.transaction.TransactionServicePort;
import com.cdx.bas.domain.transaction.validation.TransactionValidator;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;

@Path("/accounts")
@ApplicationScoped
public class BankAccountResource implements BankAccountControllerPort {
@Inject
BankAccountPersistencePort bankAccountPersistencePort;

@Inject
TransactionValidator transactionValidator;

@Inject
TransactionServicePort transactionServicePort;

Expand All @@ -29,16 +31,12 @@ public BankAccount findById(long id) {

@POST
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Override
public BankAccount deposite(@PathParam("id") Long id, Long amount, String currency) {
BankAccount currentAccount = null;
// Optional<BankAccount> bankAccountOptional = bankAccountPersistencePort.findById(id);
// if(bankAccountOptional.isPresent()) {
// currentAccount = bankAccountOptional.get();
// Transaction transaction = new Transaction(id,"EUR", new BigDecimal(amount), TransactionType.CREDIT);
// currentAccount.getIssuedTransactions().add(transaction);
// bankAccountPersistencePort.update(currentAccount);
// }
return currentAccount;
public Transaction deposite(@PathParam("id") Long id, Transaction depositTransaction) {
transactionValidator.validateNewTransaction(depositTransaction);

return depositTransaction;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.cdx.bas.client.bank.account;

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.h2.H2DatabaseTestResource;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

@QuarkusTest
@QuarkusTestResource(H2DatabaseTestResource.class)
class BankAccountResourceTest {

@Test
public void deposite_shouldCreateDepositeTransaction_whenBankAccountFound_andDepositeTransactionIsValid() {

}

@Test
public void deposite_shouldReturnHTTPError_whenBankAccountFound_butDepositeTransactionIsInvalid() {

}

@Test
public void deposite_shouldReturnHTTPError_whenBankAccountFound_butDepositeAmountReach() {

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.cdx.bas.domain.bank.account;

import com.cdx.bas.domain.transaction.Transaction;

public interface BankAccountControllerPort {


Expand All @@ -13,8 +15,10 @@ public interface BankAccountControllerPort {

/**
* make a deposite on bank account
*
* @return List<BankAccount> corresponding to all the bank account
*
* @param id of BankAccount
* @param depositTransaction to add to the BankAccount
* @return deposit Transaction added to the BankAccount
*/
public BankAccount deposite(Long id, Long amount, String currency);
public Transaction deposite(Long id, Transaction depositTransaction);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.cdx.bas.domain.validator;
package com.cdx.bas.domain.currency;

import com.cdx.bas.domain.utils.ExchangeRateUtils;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.cdx.bas.domain.validator;
package com.cdx.bas.domain.currency;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.cdx.bas.domain.transaction;

import com.cdx.bas.domain.validator.ValidCurrency;
import com.cdx.bas.domain.currency.ValidCurrency;
import com.cdx.bas.domain.transaction.validation.*;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
Expand All @@ -11,33 +13,39 @@
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static com.cdx.bas.domain.transaction.TransactionStatus.UNPROCESSED;

@Data
@Builder
@AllArgsConstructor
public class Transaction implements Comparable<Transaction> {

@NotNull(message = "id must not be null.")
@Min(value = 1, message = "id must be positive and greater than 0.")
@Min(value = 1, message = "id must be positive and greater than 0 for existing transaction.", groups = ExistingTransaction.class)
@NotNull(message = "id must not be null for existing transaction.", groups = ExistingTransaction.class)
@Null(message = "id must be null for new transaction.", groups = NewTransaction.class)
private Long id;

@NotNull(message = "accountId must not be null.")
@Null(message = "sender account must be null for cash movement.", groups = CashMovement.class)
@NotNull(message = "sender account id must not be null.", groups = AccountMovement.class)
private Long senderAccountId;

@NotNull(message = "accountId must not be null.")
@NotNull(message = "receiver account id must not be null.")
private Long receiverAccountId;

@Min(value = 1, message = "amount must be positive and greater than 0.")
@Min(value = 10, message = "amount must be greater than 10 for cash movement.", groups = CashMovement.class)
@Min(value = 1, message = "amount must be positive and greater than 0.", groups = AccountMovement.class)
@NotNull(message = "amount must not be null.")
private BigDecimal amount;

@NotNull(message = "currency must not be null.")
@ValidCurrency
@NotNull(message = "currency must not be null.")
private String currency;

@NotNull(message = "type must not be null.")
private TransactionType type;

@ValidStatus(expectedStatus = UNPROCESSED, groups = NewTransaction.class)
@NotNull(message = "status must not be null.")
private TransactionStatus status;

Expand All @@ -47,6 +55,7 @@ public class Transaction implements Comparable<Transaction> {
@NotNull(message = "label must not be null.")
private String label;

@NotNull(message = "bill must be define for cash movements.", groups = CashMovement.class)
private Map<String, String> metadata = new HashMap<>();

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.cdx.bas.domain.transaction;

public enum TransactionType {
CREDIT, DEBIT
CREDIT, DEBIT, DEPOSIT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.cdx.bas.domain.transaction.validation;

/**
* transaction group that move money from an account to another
*/
public interface AccountMovement {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.cdx.bas.domain.transaction.validation;

/**
* transaction group that use cash money to transfer to an account
*/
public interface CashMovement {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.cdx.bas.domain.transaction.validation;

/**
* transaction group for existing transactions
*/
public interface ExistingTransaction {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.cdx.bas.domain.transaction.validation;

/**
* transaction group for new transactions
*/
public interface NewTransaction {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.cdx.bas.domain.transaction.validation;

import com.cdx.bas.domain.transaction.TransactionStatus;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class StatusValidator implements ConstraintValidator<ValidStatus, TransactionStatus> {

private TransactionStatus expectedStatus;
@Override
public void initialize(ValidStatus constraintAnnotation) {
this.expectedStatus = constraintAnnotation.expectedStatus();
}

@Override
public boolean isValid(TransactionStatus value, ConstraintValidatorContext context) {
return value != null && value == expectedStatus;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.cdx.bas.domain.transaction.validation;

import com.cdx.bas.domain.transaction.Transaction;
import com.cdx.bas.domain.transaction.TransactionException;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;

import java.util.HashSet;
import java.util.Set;

import static com.cdx.bas.domain.transaction.TransactionType.DEPOSIT;


@RequestScoped
public class TransactionValidator {

@Inject
Validator validator;

public void validateNewTransaction(Transaction transaction) throws TransactionException {
validateTransaction(transaction, NewTransaction.class);
}

public void validateExistingTransaction(Transaction transaction) throws TransactionException {
validateTransaction(transaction, ExistingTransaction.class);
}

private void validateTransaction(Transaction transaction, Class<?> group) throws TransactionException {
Set<ConstraintViolation<Transaction>> violations = new HashSet<>(validator.validate(transaction));
if (DEPOSIT.equals(transaction.getType())) {
violations.addAll(validator.validate(transaction, CashMovement.class, group));
} else {
violations.addAll(validator.validate(transaction, AccountMovement.class, group));
}
checkConstraintViolation(violations);
}

private static void checkConstraintViolation(Set<ConstraintViolation<Transaction>> violations) {
if (!violations.isEmpty()) {
throw new TransactionException(concatViolations(violations));
}
}

private static String concatViolations(Set<ConstraintViolation<Transaction>> violations) {
StringBuilder violationBuilder = new StringBuilder();
for (ConstraintViolation<Transaction> violation : violations) {
violationBuilder.append(violation.getMessage());
violationBuilder.append("\n");
}
return violationBuilder.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.cdx.bas.domain.transaction.validation;

import com.cdx.bas.domain.transaction.TransactionStatus;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = StatusValidator.class)
@Documented
public @interface ValidStatus {

TransactionStatus expectedStatus() default TransactionStatus.UNPROCESSED;
String message() default "unexpected transaction status.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Loading

0 comments on commit 783be73

Please sign in to comment.