Skip to content

Commit

Permalink
Merge pull request #29 from ADORSYS-GIS/feat-create-project-ledgers-d…
Browse files Browse the repository at this point in the history
…eposit-account-rest-api

Implement ledgers deposit rest api
  • Loading branch information
Elwizzy12 authored Oct 8, 2024
2 parents d594355 + ebc81fb commit 76b747a
Show file tree
Hide file tree
Showing 25 changed files with 865 additions and 8 deletions.
22 changes: 22 additions & 0 deletions ledgers-deposit-account/ledgers-deposit-account-rest-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<groupId>de.adorsys.ledgers</groupId>
<artifactId>ledgers-deposit-account</artifactId>
<version>6.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<groupId>de.adorsys.ledgers.deposit.api</groupId>
Expand All @@ -19,4 +20,25 @@
<ruleset.basedir>../..</ruleset.basedir>
</properties>

<dependencies>
<dependency>
<groupId>de.adorsys.ledgers</groupId>
<artifactId>ledgers-deposit-account-service-api</artifactId>
<version>6.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>2.2.22</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.adorsys.ledgers</groupId>
<artifactId>ledgers-utils</artifactId>
<version>6.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,56 @@

package de.adorsys.ledgers.deposit.api.exception;

import de.adorsys.ledgers.util.exception.DepositErrorCode;
import org.springframework.http.HttpStatus;

import java.util.EnumMap;

import static de.adorsys.ledgers.util.exception.DepositErrorCode.*;
import static org.springframework.http.HttpStatus.*;

public class DepositHttpStatusResolver {
private static final EnumMap<DepositErrorCode, HttpStatus> container = new EnumMap<>(DepositErrorCode.class);

private DepositHttpStatusResolver() {
}

static {
//404 Block
container.put(DEPOSIT_ACCOUNT_NOT_FOUND, NOT_FOUND);
container.put(PAYMENT_NOT_FOUND, NOT_FOUND);

//400 Block

// This error is thrown when there is a failure in performing a deposit operation, such as adding or removing funds from an account.
container.put(DEPOSIT_OPERATION_FAILURE, BAD_REQUEST);

// This error is thrown when a currency that is not supported by the system is used in a transaction.
container.put(CURRENCY_NOT_SUPPORTED, BAD_REQUEST);

// This error is thrown when an unsupported credit limit is set, either exceeding the allowed limit or being invalid for the account.
container.put(UNSUPPORTED_CREDIT_LIMIT, BAD_REQUEST);

//403 Block
// This error is thrown when there are insufficient funds in the deposit account to complete the requested transaction.
container.put(INSUFFICIENT_FUNDS, FORBIDDEN);

//417 Block

container.put(COULD_NOT_EXECUTE_STATEMENT, EXPECTATION_FAILED);

//500
// This error is thrown when a failure occurs during the processing of a payment, typically due to server-side issues.
container.put(PAYMENT_PROCESSING_FAILURE, INTERNAL_SERVER_ERROR);

//409
// This error is thrown when an attempt is made to process a payment with an ID that already exists in the system.
container.put(PAYMENT_WITH_ID_EXISTS, CONFLICT);
// This error is thrown when attempting to create a deposit account that already exists.
container.put(DEPOSIT_ACCOUNT_EXISTS, CONFLICT);
}
public static HttpStatus resolveHttpStatusByCode(DepositErrorCode code) {
return container.getOrDefault(code, BAD_REQUEST) ;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2018-2024 adorsys GmbH and Co. KG
* All rights are reserved.
*/

package de.adorsys.ledgers.deposit.api.exception;

import de.adorsys.ledgers.util.exception.DepositModuleException;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Slf4j
@ControllerAdvice
public class ExceptionAdvisor {
private static final String MESSAGE = "message";
private static final String DEV_MESSAGE = "devMessage";
private static final String CODE = "code";
private static final String ERROR_CODE = "errorCode";
private static final String DATE_TIME = "dateTime";

@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String,String>> globalExceptionHandler(Exception ex) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
Map<String, String> body = getHandlerContent(status, null, null, "Something went wrong during execution of your request.");
log.error("INTERNAL SERVER ERROR", ex);
return new ResponseEntity<>(body, status);
}


@ExceptionHandler(DepositModuleException.class)
public ResponseEntity<Map<String,String>> handleDepositModuleException(DepositModuleException ex) {
HttpStatus status = DepositHttpStatusResolver.resolveHttpStatusByCode(ex.getErrorCode());
Map<String, String> body = getHandlerContent(status, ex.getErrorCode().name(), null, ex.getDevMsg());
log.error(ex.getDevMsg());
return new ResponseEntity<>(body, status);
}

@ExceptionHandler(FeignException.class)
public ResponseEntity<Map<String,String>> handleFeignException(FeignException ex) {
HttpStatus status = HttpStatus.CONFLICT;
Map<String, String> body = getHandlerContent(status, "Internal Rest Exception!", null, "Something went wrong during server internal interaction. \nPlease consult your bank for details.");
log.error(ex.contentUTF8());
return new ResponseEntity<>(body, status);
}


//TODO Consider a separate Class for this with a builder?
private Map<String, String> getHandlerContent(HttpStatus status, String errorCode, String message, String devMessage) {
Map<String, String> error = new HashMap<>();
error.put(CODE, String.valueOf(status.value()));
Optional.ofNullable(errorCode).ifPresent(e -> error.put(ERROR_CODE, e));
error.put(MESSAGE, message);
error.put(DEV_MESSAGE, devMessage);
error.put(DATE_TIME, LocalDateTime.now().toString());
return error;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2018-2024 adorsys GmbH and Co. KG
* All rights are reserved.
*/

package de.adorsys.ledgers.deposit.api.resource;

import de.adorsys.ledgers.deposit.api.domain.account.AccountDetailsTO;
import de.adorsys.ledgers.deposit.api.domain.payment.AmountTO;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import static de.adorsys.ledgers.deposit.api.utils.Constants.*;

@Tag(name = "LDG022 - Accounts (Deposit Account)", description = "Provides access to the deposit account resource for members.")
public interface AccountMgmResourceAPI {

String BASE_PATH = "/AccountManagement";



/**
* Creates a new deposit account for a user specified by ID
* Account is created for the same branch as user
*
* @param userId user for who account is created
* @param accountDetailsTO account details
* @return Void
*/
@Operation(summary = "Registers a new Deposit Account for a user with specified ID",
description = "Registers a new deposit account and assigns account access OWNER to the current user.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Account creation successful"),
@ApiResponse(responseCode = "404", description = "User with this ID not found"),
@ApiResponse(responseCode = "409", description = "Account with given IBAN already exists.")
})
@PostMapping
ResponseEntity<Boolean> createDepositAccountForUser(@RequestParam(name = USER_ID) String userId,
@RequestBody AccountDetailsTO accountDetailsTO);


@GetMapping("/{accountId}")
ResponseEntity<AccountDetailsTO> getAccountDetailsById(@Parameter(name = ACCOUNT_ID) @PathVariable(ACCOUNT_ID) String accountId);

/**
* Operation deposits cash to the deposit account
*
* @param accountId Account ID in Ledgers
* @param amount Amount to be deposited
* @return Void
*/
@Operation(summary = "Deposit Cash",
description = "Operation for a member to register cash in the deposit account")

@ApiResponses(value = {
@ApiResponse(responseCode = "202", description = "Operation was successful")
})
@PostMapping("/{accountId}/cash")
ResponseEntity<Void> depositCash(@PathVariable(ACCOUNT_ID) String accountId, @RequestBody AmountTO amount);

@Operation(summary = "Load Extended Account Details by AccountId",
description = "Returns extended account details information for the given account id. "
+ "User must have access to the target account. This is also accessible to other token types like tpp token (DELEGATED_ACESS)")

@PostMapping("/{accountId}/status")
ResponseEntity<Boolean> changeStatus(@PathVariable(ACCOUNT_ID) String accountId);}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,76 @@

package de.adorsys.ledgers.deposit.api.resource;

import de.adorsys.ledgers.deposit.api.domain.account.AccountBalanceTO;
import de.adorsys.ledgers.deposit.api.domain.account.TransactionTO;
import de.adorsys.ledgers.deposit.api.domain.account.AccountDetailsTO;
import de.adorsys.ledgers.util.domain.CustomPageImpl;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.List;
import static de.adorsys.ledgers.deposit.api.utils.Constants.*;

@Tag(name = "LDG003 - Accounts", description = "Provides access to a deposit account. This interface does not provide any endpoint to list all accounts.")
public interface AccountRestAPI {
String BASE_PATH = "/accounts";


@GetMapping("/{accountId}")
@Operation(summary = "Load Account by AccountId",
description = "Returns account details information for the given account id. "
+ "User must have access to the target account. This is also accessible to other token types like tpp token (DELEGATED_ACESS)")

@ApiResponses(value = {
@ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = AccountDetailsTO.class)),
description = "Account details.")
})
ResponseEntity<AccountDetailsTO> getAccountDetailsById(@Parameter(name = ACCOUNT_ID) @PathVariable(ACCOUNT_ID) String accountId);
@GetMapping("/{accountId}/balances")
@Operation(summary = "Read balances",
description = "Returns balances of the deposit account with the given accountId. "
+ "User must have access to the target account. This is also accessible to other token types like tpp token (DELEGATED_ACESS)")

@ApiResponses(value = {
@ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = AccountBalanceTO.class)), description = "List of accounts balances for the given account.")
})

ResponseEntity<List<AccountBalanceTO>> getBalances(@Parameter(name = ACCOUNT_ID) @PathVariable(ACCOUNT_ID) String accountId);

@GetMapping(path = "/{accountId}/transactions", params = {DATE_FROM_QUERY_PARAM, DATE_TO_QUERY_PARAM})
@Operation(summary = "Find Transactions By Date", description = "Returns all transactions for the given account id")

ResponseEntity<List<TransactionTO>> getTransactionByDates(
@Parameter(name = ACCOUNT_ID)
@PathVariable(ACCOUNT_ID) String accountId,
@RequestParam(name = DATE_FROM_QUERY_PARAM, required = false) @DateTimeFormat(pattern = LOCAL_DATE_YYYY_MM_DD_FORMAT) LocalDate dateFrom,
@RequestParam(name = DATE_TO_QUERY_PARAM) @DateTimeFormat(pattern = LOCAL_DATE_YYYY_MM_DD_FORMAT) LocalDate dateTo);

@GetMapping(path = "/{accountId}/transactions/page", params = {DATE_FROM_QUERY_PARAM, DATE_TO_QUERY_PARAM, PAGE, SIZE})
@Operation(summary = "Find Transactions By Date", description = "Returns transactions for the given account id for certain dates, paged view")

ResponseEntity<CustomPageImpl<TransactionTO>> getTransactionByDatesPaged(
@Parameter(name = ACCOUNT_ID)
@PathVariable(name = ACCOUNT_ID) String accountId,
@RequestParam(name = DATE_FROM_QUERY_PARAM, required = false) @DateTimeFormat(pattern = LOCAL_DATE_YYYY_MM_DD_FORMAT) LocalDate dateFrom,
@RequestParam(name = DATE_TO_QUERY_PARAM) @DateTimeFormat(pattern = LOCAL_DATE_YYYY_MM_DD_FORMAT) LocalDate dateTo,
@RequestParam(PAGE) int page,
@RequestParam(SIZE) int size);

@GetMapping("/{accountId}/transactions/{transactionId}")
@Operation(summary = "Load Transaction", description = "Returns the transaction with the given account id and transaction id.")

ResponseEntity<TransactionTO> getTransactionById(
@Parameter(name = ACCOUNT_ID)
@PathVariable(name = ACCOUNT_ID) String accountId,
@Parameter(name = TRANSACTION_ID)
@PathVariable(name = TRANSACTION_ID) String transactionId);}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2023 adorsys GmbH and Co. KG
* All rights are reserved.
*/

package de.adorsys.ledgers.deposit.api.resource;

import de.adorsys.ledgers.deposit.api.domain.account.BookingDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;
import java.util.Map;

@Tag(name = "LDG014 - Transactions upload")
public interface TransactionsResourceAPI {
String BASE_PATH = "/transactions";

/**
* Registers a new user within a given branch.
*
* @return user object without pin
*/
@Operation(summary = "Posts transactions to Ledgers")
@PostMapping
ResponseEntity<Map<String, String>> transactions(@RequestBody List<BookingDetails> data);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,22 @@

package de.adorsys.ledgers.deposit.api.utils;


public abstract class Constants {

public static final String ACCOUNT_ID = "accountId";
public static final String USER_ID = "userId";

public static final String TRANSACTION_ID = "transactionId";

public static final String PAGE = "page";
public static final String SIZE = "size";

public static final String LOCAL_DATE_YYYY_MM_DD_FORMAT = "yyyy-MM-dd";
public static final String DATE_TO_QUERY_PARAM = "dateTo";
public static final String DATE_FROM_QUERY_PARAM = "dateFrom";


private Constants() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2024 adorsys GmbH and Co. KG
* All rights are reserved.
*/

package de.adorsys.ledgers.deposit.api.exception;

import de.adorsys.ledgers.util.exception.DepositErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

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


@Slf4j
class HttpStatusResolverTest {
@Test
void testDepositStatusResolverCoverage() {
List<DepositErrorCode> codes = Arrays.asList(DepositErrorCode.values());
codes.forEach(c -> {
boolean isNull = DepositHttpStatusResolver.resolveHttpStatusByCode(c) == null;
if (isNull) {
log.error("{} is missing in Resolver", c.name());
}
assertFalse(isNull);
});
}

}
Loading

0 comments on commit 76b747a

Please sign in to comment.