Skip to content

Commit

Permalink
Merge pull request #15 from danishjamal104/ph-error
Browse files Browse the repository at this point in the history
Implemented generic exception class and builder pattern for all internal PH errors
  • Loading branch information
fynmanoj authored Jan 19, 2023
2 parents 09cceda + 75287c0 commit 02771cf
Show file tree
Hide file tree
Showing 8 changed files with 383 additions and 0 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.6.2'
compileOnly 'org.hibernate.validator:hibernate-validator:6.0.20.Final'

compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'

}

group = 'org.mifos'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.mifos.connector.common.channel.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class ErrorParameter {

private String key;

private String value;

}
118 changes: 118 additions & 0 deletions src/main/java/org/mifos/connector/common/channel/dto/PhErrorDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.mifos.connector.common.channel.dto;

import lombok.Getter;
import lombok.ToString;
import org.mifos.connector.common.exception.PaymentHubError;
import org.mifos.connector.common.exception.PaymentHubException;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;

@Getter
@ToString
public class PhErrorDTO {

@NotNull
private String errorCategory;
@NotNull
private String errorCode;
@NotNull
private String errorDescription;
private String developerMessage;
private String defaultUserMessage;
private List<ErrorParameter> errorParameters;

private PhErrorDTO(PhErrorDTOBuilder builder) {
this.errorCategory = builder.errorCategory;
this.errorCode = builder.errorCode;
this.errorDescription = builder.errorDescription;
this.developerMessage = builder.developerMessage;
this.defaultUserMessage = builder.defaultUserMessage;
this.errorParameters = builder.errorParameters;
}

/**
* Creates builder to build {@link PhErrorDTO}.
*
* Example:
* new PhErrorDTOBuilder(PaymentHubError.IdNotFound).build()
*
* new PhErrorDTOBuilder("idnotfound).build()
*
* new PhErrorDTOBuilder(PaymentHubError.IdNotFound)
* .addErrorParameter("accountId", "1231231").build()
*/
public static class PhErrorDTOBuilder {

private String errorCategory;
private String errorCode;
private String errorDescription;
private String developerMessage;
private String defaultUserMessage;
private List<ErrorParameter> errorParameters;

// sets not null fields using [PaymentHubError] object
public PhErrorDTOBuilder(PaymentHubError error) {
setupUsingPaymentHubErrorObject(error);
}

// sets not null fields using [PaymentHubException] object
public PhErrorDTOBuilder(PaymentHubException exception) {
PaymentHubError paymentHubError = PaymentHubError.fromCode(exception.getErrorCode());
setupUsingPaymentHubErrorObject(paymentHubError);
}

// sets not null fields using valid error code in [String] format
public PhErrorDTOBuilder(String errorCode) {
PaymentHubError paymentHubError = PaymentHubError.fromCode(errorCode);
setupUsingPaymentHubErrorObject(paymentHubError);
}

// method to set the not null fields using [PaymentHubError] object
private void setupUsingPaymentHubErrorObject(PaymentHubError error) {
this.errorCategory = error.getErrorCategory().name();
this.errorCode = error.getErrorCode();
this.errorDescription = error.getErrorDescription();
}

// validates the not null fields
private void validate() {
if (this.errorCode == null) {
throw new IllegalArgumentException("Error code is required");
}
if (this.defaultUserMessage == null) {
this.defaultUserMessage = "";
}
if (this.developerMessage == null) {
this.developerMessage = "";
}
}

// sets the developer message
public PhErrorDTOBuilder developerMessage(String developerMessage) {
this.developerMessage = developerMessage;
return this;
}

// sets the default user message
public PhErrorDTOBuilder defaultUserMessage(String defaultUserMessage) {
this.defaultUserMessage = defaultUserMessage;
return this;
}

public PhErrorDTOBuilder addErrorParameter(String key, String value) {
if (this.errorParameters == null) {
this.errorParameters = new ArrayList<>();
}
this.errorParameters.add(new ErrorParameter(key, value));
return this;
}

// validates and builds the PhErrorDTO object
public PhErrorDTO build() {
validate();
return new PhErrorDTO(this);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.mifos.connector.common.exception;

import lombok.Getter;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Getter
public enum PaymentHubError {

// Interoperability Errors;
CommunicationError(PaymentHubErrorCategory.Interop, "CommunicationError", "Communication error"),
DestinationCommunicationError(PaymentHubErrorCategory.Interop, "DestinationCommunicationError", "Destination communication error"),
ExtGenericServerError(PaymentHubErrorCategory.Interop, "ExtGenericServerError", "Generic server error"),
ExtInternalServerError(PaymentHubErrorCategory.Interop, "ExtInternalServerError", "Internal server error"),
NotImplemented(PaymentHubErrorCategory.Interop, "NotImplemented", "Not implemented"),
ServiceDenied(PaymentHubErrorCategory.Interop, "ServiceDenied", "Service currently unavailable"),
TimeOut(PaymentHubErrorCategory.Interop, "TimeOut", "Server timed-out"),
ConnectionReset(PaymentHubErrorCategory.Interop, "ConnectionReset", "Connection reset by server/ request cancelled"),
GenericClientError(PaymentHubErrorCategory.Interop, "GenericClientError", "Generic client error"),
UnknownURL(PaymentHubErrorCategory.Interop, "UnknownURL", "Unknown URI"),
AddPartyError(PaymentHubErrorCategory.Interop, "AddPartyError", "Add Party information error"),
ExtValidationError(PaymentHubErrorCategory.Interop, "ExtValidationError", "Generic validation error"),
SyntaxError(PaymentHubErrorCategory.Interop, "SyntaxError", "Malformed syntax"),
RequiredElement(PaymentHubErrorCategory.Interop, "RequiredElement", "Missing mandatory element"),
IdNotFound(PaymentHubErrorCategory.Interop, "IdNotFound", "Generic ID not found"),
PayerFSPIdNotFound(PaymentHubErrorCategory.Interop, "PayerFSPIdNotFound", "Payer FSP ID not found"),
PayeeFSPIdNotFound(PaymentHubErrorCategory.Interop, "PayeeFSPIdNotFound", "Payee FSP ID not found"),
PayerNotFound(PaymentHubErrorCategory.Interop, "PayerNotFound", "Payer not found"),
PayeeNotFound(PaymentHubErrorCategory.Interop, "PayeeNotFound", "Payee not found"),
CurrencyPairNotFound(PaymentHubErrorCategory.Interop, "CurrencyPairNotFound", "Currency pair not found"),
TransactionIdNotFound(PaymentHubErrorCategory.Interop, "TransactionIdNotFound", "Transaction ID not found"),
TransferIdNotFound(PaymentHubErrorCategory.Interop, "TransferIdNotFound", "Transfer ID not found"),
InactiveAccountStatus(PaymentHubErrorCategory.Interop, "InactiveAccountStatus", "Inactive account status"),
TransferExpired(PaymentHubErrorCategory.Interop, "TransferExpired", "Transfer expired"),
PayerCurrencyInvalid(PaymentHubErrorCategory.Interop, "PayerCurrencyInvalid", "Payer currency invalid"),
GenericPayerError(PaymentHubErrorCategory.Interop, "GenericPayerError", "Generic Payer error"),
PayerFSPTransactionTypeInvalid(PaymentHubErrorCategory.Interop, "PayerFSPTransactionTypeInvalid", "Payer FSP Unsupported Transaction type"),
PayerInsufficientBalance(PaymentHubErrorCategory.Interop, "PayerInsufficientBalance", "Payer Party Insufficient Balance"),
PayeeCurrencyInvalid(PaymentHubErrorCategory.Interop, "PayeeCurrencyInvalid", "Payee currency invalid"),
PayeeUnauthorized(PaymentHubErrorCategory.Interop, "PayeeUnauthorized", "Payee permission error"),
PayeeFSPTransactionTypeInvalid(PaymentHubErrorCategory.Interop, "PayeeFSPTransactionTypeInvalid", "Payee FSP unsupported transaction type");

private PaymentHubErrorCategory errorCategory;
private String errorCode;
private String errorDescription;

PaymentHubError(PaymentHubErrorCategory errorCategory, String code, String errorMessage) {
this.errorCategory = errorCategory;
this.errorCode = code;
this.errorDescription = errorMessage;
}

public static PaymentHubErrorCategory getCategory(String errorCode) {
return fromCode(errorCode).getErrorCategory();
}

public static PaymentHubError fromCode(String code) {
return Arrays.stream(values())
.filter(ec -> ec.getErrorCode().equalsIgnoreCase(code))
.findFirst()
.orElseThrow(() -> new RuntimeException("Can not get unknown errorCode: " + code));
}

public static List<PaymentHubError> fromCategory(PaymentHubErrorCategory errorCategory) {
return Arrays.stream(values())
.filter(ec -> ec.errorCategory == errorCategory)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.mifos.connector.common.exception;

public enum PaymentHubErrorCategory {
Interop, System, BusinessRule
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.mifos.connector.common.exception;

import lombok.Getter;

@Getter
public class PaymentHubException extends Exception {

private String errorCode;
private String errorDescription;

private PaymentHubException(PhExceptionBuilder builder) {
this.errorCode = builder.errorCode;
this.errorDescription = builder.errorDescription;
}

public void throwException() throws PaymentHubException {
throw this;
}

/**
* Builder class for PaymentHubException
*
* Ways to create an exception object
*
* 1. Set error code and description (Can be used for creating custom errors)
* new PaymentHubException.PhExceptionBuilder()
* .withErrorCode(ec)
* .withErrorDescription(ed)
* .build()
*
* 2. Set [PaymentHubErrors] enum (Can be used for creating predefined errors)
* new PaymentHubException.PhExceptionBuilder()
* .withErrorInformation(PaymentHubErrors.PayerFSPIdNotFound)
* .build();
*
* 3. Set error code (can be used when error code belong to existing list of errors)
* new PaymentHubException.PhExceptionBuilder()
* .withErrorCode("PayerFSPIdNotFound")
* .build();
*
*/
public static class PhExceptionBuilder {
private String errorCode;
private String errorDescription;

public PhExceptionBuilder withErrorCode(String errorCode) {
this.errorCode = errorCode;
return this;
}

public PhExceptionBuilder withErrorDescription(String errorInformation) {
this.errorDescription = errorInformation;
return this;
}

public PhExceptionBuilder withErrorInformation(PaymentHubError errorInformation) {
this.errorCode = errorInformation.getErrorCode();
this.errorDescription = errorInformation.getErrorDescription();
return this;
}

public PaymentHubException build() {
validate();
return new PaymentHubException(this);
}

// builds and throws an PaymentHubException
public void throwException() throws PaymentHubException {
build().throwException();
}

// validates the fields and fetches default values for error code
private void validate() {
if (errorCode == null) {
throw new IllegalArgumentException("Error code is required");
}
if (errorDescription == null) {
PaymentHubError paymentHubErrors = PaymentHubError.fromCode(this.errorCode);
this.errorCode = paymentHubErrors.getErrorCode();
this.errorDescription = paymentHubErrors.getErrorDescription();
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.mifos.connector.common.exception.mapper;

import org.mifos.connector.common.exception.PaymentHubError;

import java.util.HashMap;
import java.util.Map;

/**
* Default implementation of mapper. This class can be used in ams or
* payment schema connector for creating there respective mappings of
* external error codes.
*/
public abstract class ErrorMapper implements Mapper {

private Map<String, PaymentHubError> errorMap = new HashMap<>();

public ErrorMapper() {
configure();
}

@Override
public void add(String externalErrorCode, PaymentHubError internalError) {
errorMap.put(externalErrorCode, internalError);
}

@Override
public void add(String externalErrorCode, String internalError) {
errorMap.put(externalErrorCode, PaymentHubError.fromCode(internalError));
}

@Override
public PaymentHubError getInternalError(String externalErrorCode) {
return errorMap.get(externalErrorCode);
}

@Override
public String getExternalError(String internalErrorCode) {
PaymentHubError paymentHubErrors = PaymentHubError.fromCode(internalErrorCode);
PaymentHubError filterResult = errorMap.values()
.stream()
.filter(paymentHubErrors::equals)
.findFirst().orElseThrow(() ->
new RuntimeException("Can not get external error code for internal error code: " +
internalErrorCode));
return filterResult.getErrorCode();
}

@Override
public String getExternalError(PaymentHubError internalErrorCode) {
PaymentHubError filterResult = errorMap.values()
.stream()
.filter(internalErrorCode::equals)
.findFirst().orElseThrow(() ->
new RuntimeException("Can not get external error code for internal error code: " +
internalErrorCode));
return filterResult.getErrorCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.mifos.connector.common.exception.mapper;

import org.mifos.connector.common.exception.PaymentHubError;

/**
* Interface for creating external error code mapper
*/
public interface Mapper {

// adds the mapping of externalErrorCode - [PaymentHubErrors]
void add(String externalErrorCode, PaymentHubError internalError);

// adds the mapping of externalErrorCode - [PaymentHubErrors] (fetches enum using the string passed)
void add(String externalErrorCode, String internalError);

// getting the internal error code using the external error code
PaymentHubError getInternalError(String externalErrorCode);

// getting the external error code using the internal error code
String getExternalError(String internalErrorCode);

// getting the external error code using the internal error code enum
String getExternalError(PaymentHubError internalErrorCode);

// method to configure all the mappings
void configure();
}

0 comments on commit 02771cf

Please sign in to comment.