Skip to content

Commit

Permalink
Merge pull request #147 from ADORSYS-GIS/146-implement-transaction-hi…
Browse files Browse the repository at this point in the history
…story-retrieval-in-das-e5

chore(): implement transaction history retrieval in OBS
  • Loading branch information
Arielpetit authored Feb 7, 2025
2 parents fe68942 + 5624399 commit df0c3da
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Tag(name = "Trans", description = "Operations related to Trans processing")
Expand All @@ -18,7 +19,7 @@ public interface TransRestApi {
@ApiResponse(responseCode = "200", description = "Trans successfully retrieved"),
@ApiResponse(responseCode = "400", description = "Invalid request to get Trans")
})
@PostMapping(value = "/trans", consumes = "application/json", produces = "application/json")
String getTrans(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorizationHeader, @RequestBody TransRequest request);
@PostMapping(value = "/transactions", consumes = "application/json", produces = "application/json")
ResponseEntity<String> getTrans(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorizationHeader, @RequestBody TransRequest request);

}
5 changes: 5 additions & 0 deletions obs/obs-rest/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@
<artifactId>obs-rest-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.18.2</version> <!-- Use the latest version -->
</dependency>



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,32 @@

import com.adorsys.webank.obs.dto.TransRequest;
import com.adorsys.webank.obs.service.TransServiceApi;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;

@RestController
@RequestMapping("/api/accounts/balance")
public class TransRest implements TransRestApi {

private final TransServiceApi transService;

@Autowired
public TransRest(TransServiceApi transService) {

this.transService = transService;
}
private TransServiceApi transService;

@Override
public String getTrans( String authorizationHeader, TransRequest request) {
return transService.getTrans(request.getAccountID());
public ResponseEntity<String> getTrans(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorizationHeader, @RequestBody TransRequest request) {
try {
String jwtToken = extractJwtFromHeader(authorizationHeader);
String result = transService.getTrans(request, jwtToken) ;
return ResponseEntity.status(HttpStatus.CREATED).body(result);
} catch (Exception e) {
// Log the exception (optional)
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred while processing the request.");
}
}
private String extractJwtFromHeader(String authorizationHeader) {
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
throw new IllegalArgumentException("Authorization header must start with 'Bearer '");
}
return authorizationHeader.substring(7); // Remove "Bearer " prefix
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.adorsys.webank.obs.service;

import com.adorsys.webank.obs.dto.TransRequest;

public interface TransServiceApi {
String getTrans(String accountID);
String getTrans(TransRequest transRequest, String accountCertificateJwt);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,74 @@
package com.adorsys.webank.obs.serviceimpl;

import com.adorsys.webank.obs.dto.TransRequest;
import com.adorsys.webank.obs.security.JwtCertValidator;
import com.adorsys.webank.obs.service.TransServiceApi;
import de.adorsys.ledgers.postings.api.service.LedgerService;
import de.adorsys.webank.bank.api.domain.BankAccountBO;
import de.adorsys.webank.bank.api.domain.TransactionDetailsBO;
import de.adorsys.webank.bank.api.service.BankAccountService;
import de.adorsys.webank.bank.api.service.BankAccountTransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class TransServiceImpl implements TransServiceApi {

private final BankAccountService bankAccountService;
private final JwtCertValidator jwtCertValidator;


@Autowired
public TransServiceImpl(BankAccountService bankAccountService, JwtCertValidator jwtCertValidator) {
this.bankAccountService = bankAccountService;
this.jwtCertValidator = jwtCertValidator;
}

@Override
public String getTrans(String accountID) {
// Mock implementation: Replace with actual logic to retrieve trans details
return "Trans details for ID: " + accountID;
public String getTrans(TransRequest transRequest, String accountCertificateJwt) {
try {
//Validate the JWT certificate
boolean isValid = jwtCertValidator.validateJWT(accountCertificateJwt);
if (!isValid) {
return "Invalid certificate or JWT. Transaction retrieval failed.";
}

// Extract the account ID from the request
String accountId = transRequest.getAccountID();

// Fetch the account details
BankAccountBO bankAccount = bankAccountService.getAccountById(accountId);
if (bankAccount == null) {
return "Bank account not found for ID: " + accountId;
}

// Define the date range for transactions (default to last month)
LocalDateTime dateFrom = LocalDateTime.now().minusMonths(1);
LocalDateTime dateTo = LocalDateTime.now();


// Fetch the transactions using the ledger service
List<TransactionDetailsBO> postingLines = bankAccountService.getTransactionsByDates(accountId, dateFrom, dateTo);

// If no transactions found
if (postingLines.isEmpty()) {
return "No transactions found for the given account and date range.";
}

// Map the posting lines to a string of transaction details
List<String> transactionDetails = postingLines.stream()
.map(postingLine -> "Transaction ID: " + postingLine.getTransactionId() + ", Informations: " + postingLine.getAdditionalInformation() + ", Amount: " + postingLine.getTransactionAmount().getAmount())
.collect(Collectors.toList());

return String.join(", ", transactionDetails);

} catch (Exception e) {
return "An error occurred while processing the request: " + e.getMessage();
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package com.adorsys.webank.obs.serviceimpl;

import com.adorsys.webank.obs.dto.TransRequest;
import com.adorsys.webank.obs.security.JwtCertValidator;
import de.adorsys.webank.bank.api.domain.AmountBO;
import de.adorsys.webank.bank.api.domain.BankAccountBO;
import de.adorsys.webank.bank.api.domain.TransactionDetailsBO;
import de.adorsys.webank.bank.api.service.BankAccountService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Currency;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class TransServiceImplTest {

@Mock
private BankAccountService bankAccountService;

@Mock
private JwtCertValidator jwtCertValidator;

@InjectMocks
private TransServiceImpl transService;

private TransRequest transRequest;
private String accountCertificateJwt;

@BeforeEach
void setUp() {
transRequest = new TransRequest();
transRequest.setAccountID("12345");
// For this test we assume the date range is defined internally as one month ago to now.
accountCertificateJwt = "valid-jwt";
}

@Test
void testGetTrans_SuccessfulTransactionRetrieval() {
// Arrange
when(jwtCertValidator.validateJWT(accountCertificateJwt)).thenReturn(true);

// Return a non-null BankAccountBO (details not important for this test)
BankAccountBO bankAccount = new BankAccountBO();
when(bankAccountService.getAccountById("12345")).thenReturn(bankAccount);

// Create a dummy TransactionDetailsBO with an AmountBO value
TransactionDetailsBO transaction = new TransactionDetailsBO();
transaction.setTransactionId("txn-001");
// Create an AmountBO instance with EUR and an amount of 100.50
AmountBO amount = new AmountBO(Currency.getInstance("EUR"), BigDecimal.valueOf(100.50));
transaction.setTransactionAmount(amount);

List<TransactionDetailsBO> transactions = List.of(transaction);
when(bankAccountService.getTransactionsByDates(anyString(), any(LocalDateTime.class), any(LocalDateTime.class)))
.thenReturn(transactions);

// Act
String result = transService.getTrans(transRequest, accountCertificateJwt);

// Assert
String expected = "Transaction ID: txn-001, Informations: null, Amount: 100.5";
assertEquals(expected, result);
}

@Test
void testGetTrans_InvalidJWT() {
// Arrange
when(jwtCertValidator.validateJWT(accountCertificateJwt)).thenReturn(false);

// Act
String result = transService.getTrans(transRequest, accountCertificateJwt);

// Assert
assertEquals("Invalid certificate or JWT. Transaction retrieval failed.", result);
}

@Test
void testGetTrans_AccountNotFound() {
// Arrange
when(jwtCertValidator.validateJWT(accountCertificateJwt)).thenReturn(true);
when(bankAccountService.getAccountById("12345")).thenReturn(null);

// Act
String result = transService.getTrans(transRequest, accountCertificateJwt);

// Assert
assertEquals("Bank account not found for ID: 12345", result);
}

@Test
void testGetTrans_NoTransactionsFound() {
// Arrange
when(jwtCertValidator.validateJWT(accountCertificateJwt)).thenReturn(true);
when(bankAccountService.getAccountById("12345")).thenReturn(new BankAccountBO());
when(bankAccountService.getTransactionsByDates(anyString(), any(LocalDateTime.class), any(LocalDateTime.class)))
.thenReturn(Collections.emptyList());

// Act
String result = transService.getTrans(transRequest, accountCertificateJwt);

// Assert
assertEquals("No transactions found for the given account and date range.", result);
}

@Test
void testGetTrans_ExceptionHandling() {
// Arrange
when(jwtCertValidator.validateJWT(accountCertificateJwt)).thenReturn(true);
when(bankAccountService.getAccountById("12345")).thenThrow(new RuntimeException("Database error"));

// Act
String result = transService.getTrans(transRequest, accountCertificateJwt);

// Assert
assertEquals("An error occurred while processing the request: Database error", result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,29 @@

package com.adorsys.webank.mockbank;

@org.springframework.context.annotation.Configuration
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config {
@org.springframework.context.annotation.Bean
public MockBankConfigSource configSource() {
return new MockBankConfigSource();
}

@org.springframework.context.annotation.Bean
public java.security.Principal getPrincipal(){
@Bean
public MockBankConfigSource configSource() {
return new MockBankConfigSource();
}

@Bean
public java.security.Principal getPrincipal() {
return () -> "anonymous";
}
@org.springframework.context.annotation.Bean
public com.fasterxml.jackson.databind.ObjectMapper objectMapper() {
return new com.fasterxml.jackson.databind.ObjectMapper()
.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);


@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.registerModule(new JavaTimeModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
}
13 changes: 12 additions & 1 deletion online-banking-app/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,15 @@ server.private.key=${SERVER_PRIVATE_KEY_JSON}
# Server public key in JWK format
server.public.key=${SERVER_PUBLIC_KEY_JSON}

server.port=8081
server.port=8081
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
# Enable the H2 console
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

spring.jackson.serialization.write-dates-as-timestamps=false
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ clearingAccounts:
accountNbr: 11034
bankParentAccount: 2332

#CashACCOUNTS
cashAccount: 1000


### Marker used to prevent repeated processing of this config file.
updateMarkerAccountNbr: 2320

0 comments on commit df0c3da

Please sign in to comment.