From 24f23a9233be5558a82d17937c2fc72d6fbbf489 Mon Sep 17 00:00:00 2001
From: xkollar3 <xkollar3@fi.muni.cz>
Date: Tue, 30 Apr 2024 15:09:10 +0200
Subject: [PATCH] added asynchronous execution of transactions

---
 transaction-service/pom.xml                   |  5 +++
 .../fi/obs/data/dbo/TransactionState.java     |  1 +
 .../facade/TransactionManagementFacade.java   |  5 ++-
 .../java/cz/muni/fi/obs/jms/JmsConsumer.java  | 27 +++++++++++++++
 .../java/cz/muni/fi/obs/jms/JmsProducer.java  | 25 ++++++++++++++
 .../fi/obs/service/TransactionService.java    | 33 ++++++++++++-------
 .../V2__drop_transaction_not_null.sql         |  4 +++
 7 files changed, 87 insertions(+), 13 deletions(-)
 create mode 100644 transaction-service/src/main/java/cz/muni/fi/obs/jms/JmsConsumer.java
 create mode 100644 transaction-service/src/main/java/cz/muni/fi/obs/jms/JmsProducer.java
 create mode 100644 transaction-service/src/main/resources/db.migration/V2__drop_transaction_not_null.sql

diff --git a/transaction-service/pom.xml b/transaction-service/pom.xml
index 80f2de9..9ad106c 100644
--- a/transaction-service/pom.xml
+++ b/transaction-service/pom.xml
@@ -110,6 +110,11 @@
             <artifactId>rest-assured</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-jms</artifactId>
+            <version>6.1.6</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/transaction-service/src/main/java/cz/muni/fi/obs/data/dbo/TransactionState.java b/transaction-service/src/main/java/cz/muni/fi/obs/data/dbo/TransactionState.java
index e4fab66..815d5f4 100644
--- a/transaction-service/src/main/java/cz/muni/fi/obs/data/dbo/TransactionState.java
+++ b/transaction-service/src/main/java/cz/muni/fi/obs/data/dbo/TransactionState.java
@@ -2,6 +2,7 @@ package cz.muni.fi.obs.data.dbo;
 
 public enum TransactionState {
 
+    PROCESSING,
     SUCCESSFUL,
     FAILED;
 }
diff --git a/transaction-service/src/main/java/cz/muni/fi/obs/facade/TransactionManagementFacade.java b/transaction-service/src/main/java/cz/muni/fi/obs/facade/TransactionManagementFacade.java
index 41696dd..35d0944 100644
--- a/transaction-service/src/main/java/cz/muni/fi/obs/facade/TransactionManagementFacade.java
+++ b/transaction-service/src/main/java/cz/muni/fi/obs/facade/TransactionManagementFacade.java
@@ -5,6 +5,7 @@ import cz.muni.fi.obs.api.TransactionCreateDto;
 import cz.muni.fi.obs.data.dbo.AccountDbo;
 import cz.muni.fi.obs.data.dbo.TransactionDbo;
 import cz.muni.fi.obs.exceptions.ResourceNotFoundException;
+import cz.muni.fi.obs.jms.JmsProducer;
 import cz.muni.fi.obs.service.AccountService;
 import cz.muni.fi.obs.service.TransactionService;
 import lombok.extern.slf4j.Slf4j;
@@ -22,11 +23,13 @@ public class TransactionManagementFacade {
 
 	private final TransactionService transactionService;
 	private final AccountService accountService;
+	private final JmsProducer jmsProducer;
 
 	@Autowired
-	public TransactionManagementFacade(TransactionService transactionService, AccountService accountService) {
+	public TransactionManagementFacade(TransactionService transactionService, AccountService accountService, JmsProducer jmsProducer) {
 		this.transactionService = transactionService;
 		this.accountService = accountService;
+		this.jmsProducer = jmsProducer;
 	}
 
 	public Optional<TransactionDbo> getTransactionById(String id) {
diff --git a/transaction-service/src/main/java/cz/muni/fi/obs/jms/JmsConsumer.java b/transaction-service/src/main/java/cz/muni/fi/obs/jms/JmsConsumer.java
new file mode 100644
index 0000000..6061cf1
--- /dev/null
+++ b/transaction-service/src/main/java/cz/muni/fi/obs/jms/JmsConsumer.java
@@ -0,0 +1,27 @@
+package cz.muni.fi.obs.jms;
+
+import cz.muni.fi.obs.service.TransactionService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jms.annotation.JmsListener;
+import org.springframework.stereotype.Component;
+
+import static cz.muni.fi.obs.jms.JmsProducer.TRANSACTION_QUEUE_NAME;
+
+@Component
+@Slf4j
+public class JmsConsumer {
+
+    private final TransactionService transactionService;
+
+    @Autowired
+    public JmsConsumer(TransactionService transactionService) {
+        this.transactionService = transactionService;
+    }
+
+    @JmsListener(destination = TRANSACTION_QUEUE_NAME, concurrency = "1-4")
+    public void listenTransactions(String transactionId) {
+        log.info("Executing transaction with id: " + transactionId);
+        transactionService.executeTransaction(transactionId);
+    }
+}
diff --git a/transaction-service/src/main/java/cz/muni/fi/obs/jms/JmsProducer.java b/transaction-service/src/main/java/cz/muni/fi/obs/jms/JmsProducer.java
new file mode 100644
index 0000000..e9106f8
--- /dev/null
+++ b/transaction-service/src/main/java/cz/muni/fi/obs/jms/JmsProducer.java
@@ -0,0 +1,25 @@
+package cz.muni.fi.obs.jms;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jms.core.JmsTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class JmsProducer {
+
+    private final JmsTemplate jmsTemplate;
+    public final static String TRANSACTION_QUEUE_NAME = "transactions";
+
+
+    @Autowired
+    public JmsProducer(JmsTemplate jmsTemplate) {
+        this.jmsTemplate = jmsTemplate;
+    }
+
+    public void sendMessage(String transactionId) {
+        log.info("Attempting to send message: " + transactionId);
+        jmsTemplate.convertAndSend(TRANSACTION_QUEUE_NAME, transactionId);
+    }
+}
diff --git a/transaction-service/src/main/java/cz/muni/fi/obs/service/TransactionService.java b/transaction-service/src/main/java/cz/muni/fi/obs/service/TransactionService.java
index 8671edb..a8e8781 100644
--- a/transaction-service/src/main/java/cz/muni/fi/obs/service/TransactionService.java
+++ b/transaction-service/src/main/java/cz/muni/fi/obs/service/TransactionService.java
@@ -22,6 +22,7 @@ import java.util.Optional;
 import java.util.UUID;
 
 import static cz.muni.fi.obs.data.dbo.TransactionState.FAILED;
+import static cz.muni.fi.obs.data.dbo.TransactionState.PROCESSING;
 import static cz.muni.fi.obs.data.dbo.TransactionState.SUCCESSFUL;
 
 @Slf4j
@@ -71,29 +72,37 @@ public class TransactionService {
 		AccountDbo depositsToAccount = accountRepository.findAccountDboByAccountNumber(transaction.depositsToAccountNumber())
 				.orElseThrow(() -> new ResourceNotFoundException(AccountDbo.class, transaction.depositsToAccountNumber()));
 
-		CurrencyExchangeRequest request = CurrencyExchangeRequest.builder()
-				.from(transaction.withdrawsFromAccountNumber())
-				.to(transaction.depositsToAccountNumber())
-				.amount(transaction.withdrawAmount())
-				.build();
-
-		CurrencyExchangeResult exchangeResult = callCurrencyClient(request);
-
-		var transactionDbo = TransactionDbo.builder()
+		TransactionDbo transactionDbo = TransactionDbo.builder()
 				.id(UUID.randomUUID().toString())
 				.withdrawsFrom(withdrawsFromAccount)
 				.note(transaction.note())
 				.depositsTo(depositsToAccount)
-				.depositAmount(exchangeResult.destAmount())
 				.withdrawAmount(transaction.withdrawAmount())
 				.variableSymbol(transaction.variableSymbol())
-				.conversionRate(exchangeResult.exchangeRate())
-				.transactionState(computeTransactionState(withdrawsFromAccount.getId(), transaction.withdrawAmount()))
+				.transactionState(PROCESSING)
 				.build();
 
 		return repository.save(transactionDbo);
 	}
 
+	public void executeTransaction(String transactionId) {
+		TransactionDbo transaction = repository.findById(transactionId)
+				.orElseThrow(() -> new ResourceNotFoundException(TransactionDbo.class, transactionId));
+
+		CurrencyExchangeRequest request = CurrencyExchangeRequest.builder()
+				.from(transaction.getWithdrawsFrom().getCurrencyCode())
+				.to(transaction.getDepositsTo().getCurrencyCode())
+				.amount(transaction.getWithdrawAmount())
+				.build();
+
+		CurrencyExchangeResult exchangeResult = callCurrencyClient(request);
+
+		transaction.setConversionRate(exchangeResult.exchangeRate());
+		transaction.setDepositAmount(exchangeResult.destAmount());
+		transaction.setTransactionState(computeTransactionState(transaction.getWithdrawsFrom().getId(), transaction.getWithdrawAmount()));
+		repository.save(transaction);
+	}
+
 	private TransactionState computeTransactionState(String id, BigDecimal withdrawAmount) {
 		return calculateAccountBalance(id).compareTo(withdrawAmount) > 0 ? SUCCESSFUL : FAILED;
 	}
diff --git a/transaction-service/src/main/resources/db.migration/V2__drop_transaction_not_null.sql b/transaction-service/src/main/resources/db.migration/V2__drop_transaction_not_null.sql
new file mode 100644
index 0000000..6074b17
--- /dev/null
+++ b/transaction-service/src/main/resources/db.migration/V2__drop_transaction_not_null.sql
@@ -0,0 +1,4 @@
+ALTER TABLE transactions
+    ALTER COLUMN deposited_amount drop not null;
+ALTER TABLE transactions
+    ALTER COLUMN conversion_rate drop not null;
-- 
GitLab