From e055a7f4b22ab2df998812dbf79767248b538c2c Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Fri, 22 Mar 2024 09:39:47 +0530 Subject: [PATCH 01/23] Improvement to Idempotency validation with extending capability --- .../idempotency/IdempotencyConstants.java | 25 +- .../IdempotencyValidationException.java | 35 +++ .../IdempotencyValidationUtils.java | 134 +++++++++++ .../idempotency/IdempotencyValidator.java | 227 ++++++++++-------- .../IdempotencyValidatorTests.java | 103 +++++--- 5 files changed, 376 insertions(+), 148 deletions(-) create mode 100644 open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationException.java create mode 100644 open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java index d0b8f988..1998b775 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java @@ -1,12 +1,20 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at * - * This software is the property of WSO2 LLC. and its suppliers, if any. - * Dissemination of any information or reproduction of any material contained - * herein in any form is strictly forbidden, unless permitted by WSO2 expressly. - * You may not alter or remove any copyright or other notice from copies of this content. + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - package com.wso2.openbanking.accelerator.consent.extensions.common.idempotency; /** @@ -14,6 +22,8 @@ */ public class IdempotencyConstants { + public static final String CONTENT_TYPE_TAG = "content-type"; + public static final String X_IDEMPOTENCY_KEY = "x-idempotency-key"; public static final String IDEMPOTENCY_IS_ENABLED = "Consent.Idempotency.Enabled"; public static final String IDEMPOTENCY_ALLOWED_TIME = "Consent.Idempotency.AllowedTimeDuration"; public static final String ERROR_PAYLOAD_NOT_SIMILAR = "Payloads are not similar. Hence this is not a valid" + @@ -25,4 +35,5 @@ public class IdempotencyConstants { public static final String ERROR_NO_CONSENT_DETAILS = "No consent details found for the consent ID, Hence this" + " is not a valid idempotent request"; public static final String JSON_COMPARING_ERROR = "Error occurred while comparing JSON payloads"; + public static final String CONSENT_RETRIEVAL_ERROR = "Error while retrieving detailed consent data"; } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationException.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationException.java new file mode 100644 index 00000000..821287ca --- /dev/null +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationException.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.wso2.openbanking.accelerator.consent.extensions.common.idempotency; + +import com.wso2.openbanking.accelerator.common.exception.OpenBankingException; + +/** + * Used for handling exceptions in Idempotency Validation. + */ +public class IdempotencyValidationException extends OpenBankingException { + + public IdempotencyValidationException(String message) { + super(message); + } + + public IdempotencyValidationException(String message, Throwable e) { + super(message, e); + } +} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java new file mode 100644 index 00000000..7ef7881f --- /dev/null +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.wso2.openbanking.accelerator.consent.extensions.common.idempotency; + +import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; +import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; +import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; +import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; +import com.wso2.openbanking.accelerator.consent.mgt.service.ConsentCoreService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.Map; + +/** + * Class to hold idempotency validation utils. + */ +public class IdempotencyValidationUtils { + + private static final Log log = LogFactory.getLog(IdempotencyValidationUtils.class); + private static final Map configs = OpenBankingConfigParser.getInstance().getConfiguration(); + private static final ConsentCoreService consentCoreService = ConsentExtensionsDataHolder.getInstance() + .getConsentCoreService(); + + /** + * Method to check whether the required parameters for idempotency validation are present. + * + * @param consentManageData Consent Manage Data + * @return Whether the required parameters are present + */ + static boolean isMandatoryParamsPresent(ConsentManageData consentManageData, String idempotencyKeyName) { + String idempotencyKeyValue = consentManageData.getHeaders().get(idempotencyKeyName); + return idempotencyKeyValue != null && consentManageData.getPayload() != null && + consentManageData.getClientId() != null; + } + + /** + * Method to check whether Idempotency handling is required. + * + * @return True if idempotency is required, else False. + */ + static boolean isIdempotencyEnabledFromConfig() { + + String isIdempotencyEnabled = (String) configs.get(IdempotencyConstants.IDEMPOTENCY_IS_ENABLED); + return Boolean.parseBoolean(isIdempotencyEnabled); + } + + /** + * Method to retrieve the consent ids that have the idempotency key name and value as attribute. + * + * @param idempotencyKeyName Idempotency Key Name + * @param idempotencyKeyValue Idempotency Key Value + * @return List of consent ids + */ + static ArrayList getConsentIdsFromIdempotencyKey(String idempotencyKeyName, + String idempotencyKeyValue) { + try { + return consentCoreService.getConsentIdByConsentAttributeNameAndValue( + idempotencyKeyName, idempotencyKeyValue); + } catch (ConsentManagementException e) { + log.debug("No consent ids found for the idempotency key value"); + return new ArrayList<>(); + } + } + + /** + * Method to compare the client ID sent in the request and client id retrieved from the database. + * + * @param requestClientID Client ID sent in the request + * @param dbClientId client ID retrieved from the database + * @return Whether JSON client Ids are equal + */ + static boolean isClientIdsMatching(String requestClientID, String dbClientId) { + return requestClientID.equals(dbClientId); + } + + /** + * Method to check whether difference between two dates is less than the configured time. + * + * @param createdTime Created Time of the request + * @return Whether the request is received within allowed time + */ + static boolean isRequestReceivedWithinAllowedTime(long createdTime) { + + if (createdTime == 0L) { + return false; + } + String allowedTimeDuration = (String) configs.get(IdempotencyConstants.IDEMPOTENCY_ALLOWED_TIME); + if (allowedTimeDuration != null) { + OffsetDateTime createdDate = OffsetDateTime.parse(convertToISO8601DateTimeFormat(createdTime)); + OffsetDateTime currDate = OffsetDateTime.now(createdDate.getOffset()); + + long diffInHours = Duration.between(createdDate, currDate).toMinutes(); + return diffInHours <= Long.parseLong(allowedTimeDuration); + } else { + log.error("Idempotency Allowed duration is null"); + return false; + } + } + + /** + * Convert long date values to ISO 8601 format. + * @param dateValue Date value + * @return ISO 8601 formatted date + */ + private static String convertToISO8601DateTimeFormat(long dateValue) { + + DateFormat simple = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); + Date simpleDateVal = new Date(dateValue * 1000); + return simple.format(simpleDateVal); + } +} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java index 170cc54b..a4a0b23e 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java @@ -18,24 +18,19 @@ package com.wso2.openbanking.accelerator.consent.extensions.common.idempotency; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; +import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; import com.wso2.openbanking.accelerator.consent.mgt.service.ConsentCoreService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.time.Duration; -import java.time.OffsetDateTime; import java.util.ArrayList; -import java.util.Date; -import java.util.Map; /** * Class to handle idempotency related operations. @@ -43,35 +38,38 @@ public class IdempotencyValidator { private static final Log log = LogFactory.getLog(IdempotencyValidator.class); - private static final Map configs = OpenBankingConfigParser.getInstance().getConfiguration(); private static final ConsentCoreService consentCoreService = ConsentExtensionsDataHolder.getInstance() .getConsentCoreService(); /** * Method to check whether the request is idempotent. - * This method will first check whether idempotency validation is enabled. Then it will check whether the - * idempotency key exists in the database and whether the request is received within the allowed time. + * This method will first check whether idempotency validation is enabled. After that it will validate whether + * required parameters for validation is present. + * For validation, need to check whether the idempotency key values is present as a consent attribute, if present + * the consent will be retrieved. Finally following conditions will be validated. + * - Whether the client id sent in the request and client id retrieved from the database are equal + * - Whether the difference between two dates is less than the configured time + * - Whether payloads are equal * - * @param idempotencyKeyName Idempotency Key Name - * @param idempotencyKeyValue Idempotency Key Value - * @param request Request Payload + * @param consentManageData Consent Manage Data * @return IdempotencyValidationResult */ - public static IdempotencyValidationResult validateIdempotency(String idempotencyKeyName, String idempotencyKeyValue, - String request, String clientId) - throws ConsentManagementException { - - if (Boolean.parseBoolean((configs.get(IdempotencyConstants.IDEMPOTENCY_IS_ENABLED)).toString())) { - // If idempotency key name, value or request is empty then cannot proceed with idempotency validation - if (idempotencyKeyName == null || idempotencyKeyValue == null || request.isEmpty() || clientId == null) { - log.debug("Idempotency Key , NameValue, Client ID or Request is empty. Hence cannot proceed with " + + public static IdempotencyValidationResult validateIdempotency(ConsentManageData consentManageData) + throws IdempotencyValidationException { + + if (IdempotencyValidationUtils.isIdempotencyEnabledFromConfig()) { + // If idempotency key value, client id or request is empty then cannot proceed with idempotency validation + if (!IdempotencyValidationUtils.isMandatoryParamsPresent(consentManageData, getIdempotencyHeaderName())) { + log.error("Idempotency Key Value, Client ID or Request is empty. Hence cannot proceed with " + "idempotency validation"); return new IdempotencyValidationResult(false, false, null, null); } try { + String idempotencyKeyName = getIdempotencyAttributeName(consentManageData.getRequestPath()); + String idempotencyKeyValue = consentManageData.getHeaders().get(getIdempotencyHeaderName()); // Retrieve consent ids that have the idempotency key name and value as attribute - ArrayList consentIds = getConsentIdsFromIdempotencyKey(idempotencyKeyName, - idempotencyKeyValue); + ArrayList consentIds = IdempotencyValidationUtils + .getConsentIdsFromIdempotencyKey(idempotencyKeyName, idempotencyKeyValue); // Check whether the consent id list is not empty. If idempotency key exists in the database then // the consent Id list will be not empty. if (!consentIds.isEmpty()) { @@ -80,123 +78,146 @@ public static IdempotencyValidationResult validateIdempotency(String idempotency for (String consentId : consentIds) { DetailedConsentResource consentRequest = consentCoreService.getDetailedConsent(consentId); if (consentRequest != null) { - // Compare the client ID sent in the request and client id retrieved from the database - // to validate whether the request is received from the same client - if (isClientIdsMatching(clientId, consentRequest.getClientID())) { - // Check whether difference between two dates is less than the configured time - if (isRequestReceivedWithinAllowedTime(consentRequest.getCreatedTime())) { - // Compare whether JSON payloads are equal - if (isJSONPayloadSimilar(consentRequest.getReceipt(), request)) { - log.debug("Payloads are similar and request received within allowed time." + - " Hence this is a valid idempotent request"); - return new IdempotencyValidationResult(true, true, - consentRequest, consentId); - } else { - log.debug(IdempotencyConstants.ERROR_PAYLOAD_NOT_SIMILAR); - throw new ConsentManagementException(IdempotencyConstants - .ERROR_PAYLOAD_NOT_SIMILAR); - } - } else { - log.debug(IdempotencyConstants.ERROR_AFTER_ALLOWED_TIME); - throw new ConsentManagementException(IdempotencyConstants - .ERROR_AFTER_ALLOWED_TIME); - } - } else { - log.debug(IdempotencyConstants.ERROR_MISMATCHING_CLIENT_ID); - throw new ConsentManagementException(IdempotencyConstants.ERROR_MISMATCHING_CLIENT_ID); - } + return validateIdempotencyConditions(consentManageData, consentRequest); } else { - log.debug(IdempotencyConstants.ERROR_NO_CONSENT_DETAILS); - throw new ConsentManagementException(IdempotencyConstants.ERROR_NO_CONSENT_DETAILS); + log.error(IdempotencyConstants.ERROR_NO_CONSENT_DETAILS); + throw new IdempotencyValidationException(IdempotencyConstants.ERROR_NO_CONSENT_DETAILS); } } } } catch (IOException e) { log.error(IdempotencyConstants.JSON_COMPARING_ERROR, e); - throw new ConsentManagementException(IdempotencyConstants.JSON_COMPARING_ERROR); + throw new IdempotencyValidationException(IdempotencyConstants.JSON_COMPARING_ERROR); + } catch (ConsentManagementException e) { + log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); + return new IdempotencyValidationResult(true, false, null, null); } } return new IdempotencyValidationResult(false, false, null, null); } /** - * Method to retrieve the consent ids that have the idempotency key name and value as attribute. + * Method to check whether the idempotency conditions are met. + * This method will validate the following conditions. + * - Whether the client id sent in the request and client id retrieved from the database are equal + * - Whether the difference between two dates is less than the configured time + * - Whether payloads are equal * - * @param idempotencyKeyName Idempotency Key Name - * @param idempotencyKeyValue Idempotency Key Value - * @return List of consent ids + * @param consentManageData Consent Manage Data + * @param consentRequest Detailed Consent Resource + * @return IdempotencyValidationResult */ - private static ArrayList getConsentIdsFromIdempotencyKey(String idempotencyKeyName, - String idempotencyKeyValue) { - try { - return consentCoreService.getConsentIdByConsentAttributeNameAndValue( - idempotencyKeyName, idempotencyKeyValue); - } catch (ConsentManagementException e) { - log.debug("No consent ids found for the idempotency key value"); - return new ArrayList<>(); + private static IdempotencyValidationResult validateIdempotencyConditions(ConsentManageData consentManageData, + DetailedConsentResource consentRequest) + throws IdempotencyValidationException, IOException { + // Compare the client ID sent in the request and client id retrieved from the database + // to validate whether the request is received from the same client + if (IdempotencyValidationUtils.isClientIdsMatching(consentManageData.getClientId(), + consentRequest.getClientID())) { + // Check whether difference between two dates is less than the configured time + if (IdempotencyValidationUtils.isRequestReceivedWithinAllowedTime(getCreatedTimeOfPreviousRequest( + consentManageData.getRequestPath(), consentRequest.getConsentID()))) { + // Compare whether JSON payloads are equal + if (isPayloadSimilar(consentManageData, getPayloadOfPreviousRequest( + consentManageData.getRequestPath(), consentRequest.getConsentID()))) { + log.debug("Payloads are similar and request received within allowed" + + " time. Hence this is a valid idempotent request"); + return new IdempotencyValidationResult(true, true, + consentRequest, consentRequest.getConsentID()); + } else { + log.error(IdempotencyConstants.ERROR_PAYLOAD_NOT_SIMILAR); + throw new IdempotencyValidationException(IdempotencyConstants + .ERROR_PAYLOAD_NOT_SIMILAR); + } + } else { + log.error(IdempotencyConstants.ERROR_AFTER_ALLOWED_TIME); + throw new IdempotencyValidationException(IdempotencyConstants + .ERROR_AFTER_ALLOWED_TIME); + } + } else { + log.error(IdempotencyConstants.ERROR_MISMATCHING_CLIENT_ID); + throw new IdempotencyValidationException(IdempotencyConstants.ERROR_MISMATCHING_CLIENT_ID); } } /** - * Method to compare the client ID sent in the request and client id retrieved from the database. + * Method to get the Idempotency Attribute Name store in consent Attributes. * - * @param requestClientID Client ID sent in the request - * @param dbClientId client ID retrieved from the database - * @return Whether JSON client Ids are equal + * @param resourcePath Resource Path + * @return idempotency Attribute Name. */ - private static boolean isClientIdsMatching(String requestClientID, String dbClientId) { - - return requestClientID.equals(dbClientId); + public static String getIdempotencyAttributeName(String resourcePath) { + return "IdempotencyKey"; } /** - * Method to compare whether JSON payloads are equal. + * Method to get the Idempotency Header Name according to the request. * - * @param jsonString1 JSON payload retrieved from database - * @param jsonString2 JSON payload received from current request - * @return Whether JSON payloads are equal - * @throws IOException If an error occurs while comparing JSON payloads + * @return idempotency Header Name. */ - private static boolean isJSONPayloadSimilar(String jsonString1, String jsonString2) throws IOException { - - JsonNode expectedNode = new ObjectMapper().readTree(jsonString1); - JsonNode actualNode = new ObjectMapper().readTree(jsonString2); - return expectedNode.equals(actualNode); + public static String getIdempotencyHeaderName() { + return "x-idempotency-key"; } /** - * Method to check whether difference between two dates is less than the configured time. + * Method to get created time from the Detailed Consent Resource. * - * @param createdTime Created Time of the request - * @return Whether the request is received within allowed time + * @param resourcePath Resource Path + * @param consentId ConsentId + * @return Created Time. */ - protected static boolean isRequestReceivedWithinAllowedTime(long createdTime) { - - if (createdTime == 0L) { - return false; + public static long getCreatedTimeOfPreviousRequest(String resourcePath, String consentId) { + DetailedConsentResource consentRequest = null; + try { + consentRequest = consentCoreService.getDetailedConsent(consentId); + } catch (ConsentManagementException e) { + log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); + return 0L; } - String allowedTimeDuration = (String) configs.get(IdempotencyConstants.IDEMPOTENCY_ALLOWED_TIME); - if (allowedTimeDuration != null) { - OffsetDateTime createdDate = OffsetDateTime.parse(convertToISO8601(createdTime)); - OffsetDateTime currDate = OffsetDateTime.now(createdDate.getOffset()); - long diffInHours = Duration.between(createdDate, currDate).toMinutes(); - return diffInHours <= Long.parseLong(allowedTimeDuration); - } else { - log.error("Idempotency Allowed duration is null"); - return false; + return consentRequest.getCreatedTime(); + } + + /** + * Method to get payload from previous request. + * + * @param resourcePath Resource Path + * @param consentId ConsentId + * @return Map containing the payload. + */ + public static String getPayloadOfPreviousRequest(String resourcePath, String consentId) { + DetailedConsentResource consentRequest = null; + try { + consentRequest = consentCoreService.getDetailedConsent(consentId); + } catch (ConsentManagementException e) { + log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); + return null; } + return consentRequest.getReceipt(); } /** - * Convert long date values to ISO 8601 format. - * @param dateValue Date value - * @return ISO 8601 formatted date + * Method to compare whether payloads are equal. + * + * @param consentManageData Consent Manage Data Object + * @param consentReceipt Payload received from database + * @return Whether payloads are equal */ - public static String convertToISO8601(long dateValue) { + public static boolean isPayloadSimilar(ConsentManageData consentManageData, String consentReceipt) { - DateFormat simple = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); - Date simpleDateVal = new Date(dateValue * 1000); - return simple.format(simpleDateVal); + if (consentManageData.getPayload() == null || consentReceipt == null) { + return false; + } + + JsonNode expectedNode = null; + JsonNode actualNode = null; + try { + expectedNode = new ObjectMapper().readTree(consentManageData.getPayload().toString()); + actualNode = new ObjectMapper().readTree(consentReceipt); + } catch (JsonProcessingException e) { + log.error(IdempotencyConstants.JSON_COMPARING_ERROR, e); + return false; + } + return expectedNode.equals(actualNode); } } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java index 1aefc909..386e8032 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java @@ -21,8 +21,10 @@ import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; +import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; import com.wso2.openbanking.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; +import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; @@ -44,10 +46,13 @@ @PowerMockIgnore("jdk.internal.reflect.*") public class IdempotencyValidatorTests { + @Mock + private ConsentManageData consentManageData; private ConsentCoreServiceImpl consentCoreServiceImpl; private ArrayList consentIdList; private String consentId; private Map configs; + private Map headers; private static final String IDEMPOTENCY_IS_ENABLED = "Consent.Idempotency.Enabled"; private static final String IDEMPOTENCY_ALLOWED_TIME = "Consent.Idempotency.AllowedTimeDuration"; private static final String CLIENT_ID = "testClientId"; @@ -113,6 +118,11 @@ public void beforeTest() { configs.put(IDEMPOTENCY_IS_ENABLED, "true"); configs.put(IDEMPOTENCY_ALLOWED_TIME, "1"); + headers = new HashMap<>(); + headers.put(IdempotencyConstants.X_IDEMPOTENCY_KEY, "123456"); + headers.put(IdempotencyConstants.CONTENT_TYPE_TAG, "application/json"); + + consentManageData = Mockito.mock(ConsentManageData.class); consentCoreServiceImpl = Mockito.mock(ConsentCoreServiceImpl.class); OpenBankingConfigParser openBankingConfigParserMock = PowerMockito.mock(OpenBankingConfigParser.class); Mockito.doReturn(configs).when(openBankingConfigParserMock).getConfiguration(); @@ -132,15 +142,17 @@ public void beforeTest() { } @Test - public void testValidateIdempotency() throws ConsentManagementException { + public void testValidateIdempotency() throws ConsentManagementException, IdempotencyValidationException { OffsetDateTime offsetDateTime = OffsetDateTime.now(); Mockito.doReturn(consentIdList).when(consentCoreServiceImpl) .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); Mockito.doReturn(getConsent(offsetDateTime.toEpochSecond())).when(consentCoreServiceImpl) .getDetailedConsent(Mockito.anyString()); - IdempotencyValidationResult result = IdempotencyValidator.validateIdempotency("IdempotencyKey", - "123456", PAYLOAD, CLIENT_ID); + Mockito.doReturn(headers).when(consentManageData).getHeaders(); + Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); + Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); + IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); Assert.assertTrue(result.isIdempotent()); Assert.assertTrue(result.isValid()); @@ -149,73 +161,83 @@ public void testValidateIdempotency() throws ConsentManagementException { } @Test - public void testValidateIdempotencyWithoutIdempotencyKeyName() throws ConsentManagementException { - IdempotencyValidationResult result = IdempotencyValidator - .validateIdempotency(null, "", "", CLIENT_ID); + public void testValidateIdempotencyWithoutIdempotencyKeyValue() throws IdempotencyValidationException { - Assert.assertFalse(result.isIdempotent()); - } - - @Test - public void testValidateIdempotencyWithoutIdempotencyKeyValue() throws ConsentManagementException { - IdempotencyValidationResult result = IdempotencyValidator - .validateIdempotency("IdempotencyKey", null, "", CLIENT_ID); + Mockito.doReturn(new HashMap<>()).when(consentManageData).getHeaders(); + Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); + Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); + IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); Assert.assertFalse(result.isIdempotent()); } @Test - public void testValidateIdempotencyWithoutRequest() throws ConsentManagementException { - IdempotencyValidationResult result = IdempotencyValidator - .validateIdempotency("IdempotencyKey", "123456", "", CLIENT_ID); + public void testValidateIdempotencyWithoutRequest() throws IdempotencyValidationException { + Mockito.doReturn(headers).when(consentManageData).getHeaders(); + Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); + Mockito.doReturn("").when(consentManageData).getPayload(); + IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); Assert.assertFalse(result.isIdempotent()); } @Test - public void testValidateIdempotencyRetrievingAttributesWithException() throws ConsentManagementException { + public void testValidateIdempotencyRetrievingAttributesWithException() + throws ConsentManagementException, IdempotencyValidationException { Mockito.doThrow(ConsentManagementException.class).when(consentCoreServiceImpl) .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); - IdempotencyValidationResult result = IdempotencyValidator.validateIdempotency("IdempotencyKey", - "123456", "test", CLIENT_ID); + Mockito.doReturn(headers).when(consentManageData).getHeaders(); + Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); + Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); + IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); Assert.assertFalse(result.isIdempotent()); } @Test - public void testValidateIdempotencyWithoutAttribute() throws ConsentManagementException { + public void testValidateIdempotencyWithoutAttribute() + throws ConsentManagementException, IdempotencyValidationException { Mockito.doReturn(new ArrayList<>()).when(consentCoreServiceImpl) .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); - IdempotencyValidationResult result = IdempotencyValidator.validateIdempotency("IdempotencyKey", - "123456", "test", CLIENT_ID); + Mockito.doReturn(headers).when(consentManageData).getHeaders(); + Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); + Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); + IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); Assert.assertFalse(result.isIdempotent()); } - @Test(expectedExceptions = ConsentManagementException.class) - public void testValidateIdempotencyWithNullConsentRequest() throws ConsentManagementException { + @Test(expectedExceptions = IdempotencyValidationException.class) + public void testValidateIdempotencyWithNullConsentRequest() + throws ConsentManagementException, IdempotencyValidationException { Mockito.doReturn(consentIdList).when(consentCoreServiceImpl) .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); + Mockito.doReturn(headers).when(consentManageData).getHeaders(); + Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); + Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); Mockito.doReturn(null).when(consentCoreServiceImpl).getDetailedConsent(Mockito.anyString()); - IdempotencyValidator.validateIdempotency("IdempotencyKey", - "123456", "test", CLIENT_ID); + new IdempotencyValidator().validateIdempotency(consentManageData); } - @Test(expectedExceptions = ConsentManagementException.class) - public void testValidateIdempotencyWithNonMatchingClientId() throws ConsentManagementException { + @Test(expectedExceptions = IdempotencyValidationException.class) + public void testValidateIdempotencyWithNonMatchingClientId() + throws ConsentManagementException, IdempotencyValidationException { Mockito.doReturn(consentIdList).when(consentCoreServiceImpl) .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); + Mockito.doReturn(headers).when(consentManageData).getHeaders(); + Mockito.doReturn("sampleClientID").when(consentManageData).getClientId(); + Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); Mockito.doReturn(null).when(consentCoreServiceImpl).getDetailedConsent(Mockito.anyString()); - IdempotencyValidator.validateIdempotency("IdempotencyKey", - "123456", "test", "sampleClientID"); + new IdempotencyValidator().validateIdempotency(consentManageData); } - @Test(expectedExceptions = ConsentManagementException.class) - public void testValidateIdempotencyAfterAllowedTime() throws ConsentManagementException { + @Test(expectedExceptions = IdempotencyValidationException.class) + public void testValidateIdempotencyAfterAllowedTime() + throws ConsentManagementException, IdempotencyValidationException { OffsetDateTime offsetDateTime = OffsetDateTime.now().minusHours(2); @@ -223,12 +245,15 @@ public void testValidateIdempotencyAfterAllowedTime() throws ConsentManagementEx .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); Mockito.doReturn(getConsent(offsetDateTime.toEpochSecond())).when(consentCoreServiceImpl) .getDetailedConsent(Mockito.anyString()); - IdempotencyValidator.validateIdempotency("IdempotencyKey", - "123456", PAYLOAD, CLIENT_ID); + Mockito.doReturn(headers).when(consentManageData).getHeaders(); + Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); + Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); + new IdempotencyValidator().validateIdempotency(consentManageData); } - @Test(expectedExceptions = ConsentManagementException.class) - public void testValidateIdempotencyWithNonMatchingPayload() throws ConsentManagementException { + @Test(expectedExceptions = IdempotencyValidationException.class) + public void testValidateIdempotencyWithNonMatchingPayload() + throws ConsentManagementException, IdempotencyValidationException { OffsetDateTime offsetDateTime = OffsetDateTime.now(); @@ -236,8 +261,10 @@ public void testValidateIdempotencyWithNonMatchingPayload() throws ConsentManage .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); Mockito.doReturn(getConsent(offsetDateTime.toEpochSecond())).when(consentCoreServiceImpl) .getDetailedConsent(Mockito.anyString()); - IdempotencyValidator.validateIdempotency("IdempotencyKey", "123456", - DIFFERENT_PAYLOAD, CLIENT_ID); + Mockito.doReturn(headers).when(consentManageData).getHeaders(); + Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); + Mockito.doReturn(DIFFERENT_PAYLOAD).when(consentManageData).getPayload(); + new IdempotencyValidator().validateIdempotency(consentManageData); } From 00385c84bfae1a355926d99f1fc540504f6660d4 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Fri, 22 Mar 2024 10:15:47 +0530 Subject: [PATCH 02/23] Improvement to Idempotency validation with extending capability --- .../idempotency/IdempotencyValidator.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java index a4a0b23e..216296e6 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java @@ -54,12 +54,13 @@ public class IdempotencyValidator { * @param consentManageData Consent Manage Data * @return IdempotencyValidationResult */ - public static IdempotencyValidationResult validateIdempotency(ConsentManageData consentManageData) + public IdempotencyValidationResult validateIdempotency(ConsentManageData consentManageData) throws IdempotencyValidationException { if (IdempotencyValidationUtils.isIdempotencyEnabledFromConfig()) { // If idempotency key value, client id or request is empty then cannot proceed with idempotency validation - if (!IdempotencyValidationUtils.isMandatoryParamsPresent(consentManageData, getIdempotencyHeaderName())) { + if (!IdempotencyValidationUtils.isMandatoryParamsPresent(consentManageData, + getIdempotencyHeaderName())) { log.error("Idempotency Key Value, Client ID or Request is empty. Hence cannot proceed with " + "idempotency validation"); return new IdempotencyValidationResult(false, false, null, null); @@ -107,8 +108,8 @@ public static IdempotencyValidationResult validateIdempotency(ConsentManageData * @param consentRequest Detailed Consent Resource * @return IdempotencyValidationResult */ - private static IdempotencyValidationResult validateIdempotencyConditions(ConsentManageData consentManageData, - DetailedConsentResource consentRequest) + private IdempotencyValidationResult validateIdempotencyConditions(ConsentManageData consentManageData, + DetailedConsentResource consentRequest) throws IdempotencyValidationException, IOException { // Compare the client ID sent in the request and client id retrieved from the database // to validate whether the request is received from the same client @@ -146,7 +147,7 @@ private static IdempotencyValidationResult validateIdempotencyConditions(Consent * @param resourcePath Resource Path * @return idempotency Attribute Name. */ - public static String getIdempotencyAttributeName(String resourcePath) { + public String getIdempotencyAttributeName(String resourcePath) { return "IdempotencyKey"; } @@ -155,7 +156,7 @@ public static String getIdempotencyAttributeName(String resourcePath) { * * @return idempotency Header Name. */ - public static String getIdempotencyHeaderName() { + public String getIdempotencyHeaderName() { return "x-idempotency-key"; } @@ -166,7 +167,7 @@ public static String getIdempotencyHeaderName() { * @param consentId ConsentId * @return Created Time. */ - public static long getCreatedTimeOfPreviousRequest(String resourcePath, String consentId) { + public long getCreatedTimeOfPreviousRequest(String resourcePath, String consentId) { DetailedConsentResource consentRequest = null; try { consentRequest = consentCoreService.getDetailedConsent(consentId); @@ -185,7 +186,7 @@ public static long getCreatedTimeOfPreviousRequest(String resourcePath, String c * @param consentId ConsentId * @return Map containing the payload. */ - public static String getPayloadOfPreviousRequest(String resourcePath, String consentId) { + public String getPayloadOfPreviousRequest(String resourcePath, String consentId) { DetailedConsentResource consentRequest = null; try { consentRequest = consentCoreService.getDetailedConsent(consentId); @@ -203,7 +204,7 @@ public static String getPayloadOfPreviousRequest(String resourcePath, String con * @param consentReceipt Payload received from database * @return Whether payloads are equal */ - public static boolean isPayloadSimilar(ConsentManageData consentManageData, String consentReceipt) { + public boolean isPayloadSimilar(ConsentManageData consentManageData, String consentReceipt) { if (consentManageData.getPayload() == null || consentReceipt == null) { return false; From eb45d2a7394dfab852cf796ea00fe03b7476df0e Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Mon, 25 Mar 2024 10:26:09 +0530 Subject: [PATCH 03/23] Improvement to Idempotency validation with extending capability --- .../config/OpenBankingConfigParser.java | 19 +++++++++++++++++++ .../common/constant/OpenBankingConstants.java | 2 ++ .../idempotency/IdempotencyConstants.java | 2 -- .../IdempotencyValidationUtils.java | 8 +++----- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java index c3df4d3b..2ad025f9 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java @@ -1457,4 +1457,23 @@ public String getFederatedIDPName() { ((String) getConfigElementFromKey(OpenBankingConstants.PSU_FEDERATED_IDP_NAME)).trim(); } + /** + * Method to get the value Idempotency enable configuration. + * @return + */ + public boolean isIdempotencyValidationEnabled() { + return getConfigElementFromKey(OpenBankingConstants.IDEMPOTENCY_IS_ENABLED) != null && + Boolean.parseBoolean(((String) + getConfigElementFromKey(OpenBankingConstants.IDEMPOTENCY_IS_ENABLED)).trim()); + } + + /** + * Method to get the value Idempotency allowed time configuration. + * @return + */ + public String getIdempotencyAllowedTime() { + return getConfigElementFromKey(OpenBankingConstants.IDEMPOTENCY_ALLOWED_TIME) == null ? "1440" : + (String) getConfigElementFromKey(OpenBankingConstants.IDEMPOTENCY_ALLOWED_TIME); + } + } diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java index 6831a70c..6f4a6e3f 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java @@ -255,4 +255,6 @@ public class OpenBankingConstants { public static final String QUERY = "query"; public static final String IS_PSU_FEDERATED = "PSUFederatedAuthentication.Enabled"; public static final String PSU_FEDERATED_IDP_NAME = "PSUFederatedAuthentication.IDPName"; + public static final String IDEMPOTENCY_IS_ENABLED = "Consent.Idempotency.Enabled"; + public static final String IDEMPOTENCY_ALLOWED_TIME = "Consent.Idempotency.AllowedTimeDuration"; } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java index 1998b775..9c78392d 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java @@ -24,8 +24,6 @@ public class IdempotencyConstants { public static final String CONTENT_TYPE_TAG = "content-type"; public static final String X_IDEMPOTENCY_KEY = "x-idempotency-key"; - public static final String IDEMPOTENCY_IS_ENABLED = "Consent.Idempotency.Enabled"; - public static final String IDEMPOTENCY_ALLOWED_TIME = "Consent.Idempotency.AllowedTimeDuration"; public static final String ERROR_PAYLOAD_NOT_SIMILAR = "Payloads are not similar. Hence this is not a valid" + " idempotent request"; public static final String ERROR_AFTER_ALLOWED_TIME = "Request received after the allowed time., Hence this is" + diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java index 7ef7881f..8bfe649f 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java @@ -40,7 +40,7 @@ public class IdempotencyValidationUtils { private static final Log log = LogFactory.getLog(IdempotencyValidationUtils.class); - private static final Map configs = OpenBankingConfigParser.getInstance().getConfiguration(); + private static final OpenBankingConfigParser parser = OpenBankingConfigParser.getInstance(); private static final ConsentCoreService consentCoreService = ConsentExtensionsDataHolder.getInstance() .getConsentCoreService(); @@ -62,9 +62,7 @@ static boolean isMandatoryParamsPresent(ConsentManageData consentManageData, Str * @return True if idempotency is required, else False. */ static boolean isIdempotencyEnabledFromConfig() { - - String isIdempotencyEnabled = (String) configs.get(IdempotencyConstants.IDEMPOTENCY_IS_ENABLED); - return Boolean.parseBoolean(isIdempotencyEnabled); + return parser.isIdempotencyValidationEnabled(); } /** @@ -107,7 +105,7 @@ static boolean isRequestReceivedWithinAllowedTime(long createdTime) { if (createdTime == 0L) { return false; } - String allowedTimeDuration = (String) configs.get(IdempotencyConstants.IDEMPOTENCY_ALLOWED_TIME); + String allowedTimeDuration = parser.getIdempotencyAllowedTime(); if (allowedTimeDuration != null) { OffsetDateTime createdDate = OffsetDateTime.parse(convertToISO8601DateTimeFormat(createdTime)); OffsetDateTime currDate = OffsetDateTime.now(createdDate.getOffset()); From e1205691228fc4b76853a0eab7dd99dac2516661 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Mon, 25 Mar 2024 15:28:36 +0530 Subject: [PATCH 04/23] Fixed review comments --- .../idempotency/IdempotencyConstants.java | 3 +- .../IdempotencyValidationUtils.java | 49 ++++----- .../idempotency/IdempotencyValidator.java | 102 +++++++++++------- .../IdempotencyValidatorTests.java | 4 +- 4 files changed, 86 insertions(+), 72 deletions(-) diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java index 9c78392d..af0a8fa2 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java @@ -24,13 +24,14 @@ public class IdempotencyConstants { public static final String CONTENT_TYPE_TAG = "content-type"; public static final String X_IDEMPOTENCY_KEY = "x-idempotency-key"; + public static final String IDEMPOTENCY_KEY_NAME = "IdempotencyKey"; public static final String ERROR_PAYLOAD_NOT_SIMILAR = "Payloads are not similar. Hence this is not a valid" + " idempotent request"; public static final String ERROR_AFTER_ALLOWED_TIME = "Request received after the allowed time., Hence this is" + " not a valid idempotent request"; public static final String ERROR_MISMATCHING_CLIENT_ID = "Client ID sent in the request does not match with the" + " client ID in the retrieved consent. Hence this is not a valid idempotent request"; - public static final String ERROR_NO_CONSENT_DETAILS = "No consent details found for the consent ID, Hence this" + + public static final String ERROR_NO_CONSENT_DETAILS = "No consent details found for the consent ID %s, Hence this" + " is not a valid idempotent request"; public static final String JSON_COMPARING_ERROR = "Error occurred while comparing JSON payloads"; public static final String CONSENT_RETRIEVAL_ERROR = "Error while retrieving detailed consent data"; diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java index 8bfe649f..955078f3 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java @@ -21,8 +21,8 @@ import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; -import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; import com.wso2.openbanking.accelerator.consent.mgt.service.ConsentCoreService; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -32,7 +32,6 @@ import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Date; -import java.util.Map; /** * Class to hold idempotency validation utils. @@ -44,18 +43,6 @@ public class IdempotencyValidationUtils { private static final ConsentCoreService consentCoreService = ConsentExtensionsDataHolder.getInstance() .getConsentCoreService(); - /** - * Method to check whether the required parameters for idempotency validation are present. - * - * @param consentManageData Consent Manage Data - * @return Whether the required parameters are present - */ - static boolean isMandatoryParamsPresent(ConsentManageData consentManageData, String idempotencyKeyName) { - String idempotencyKeyValue = consentManageData.getHeaders().get(idempotencyKeyName); - return idempotencyKeyValue != null && consentManageData.getPayload() != null && - consentManageData.getClientId() != null; - } - /** * Method to check whether Idempotency handling is required. * @@ -70,7 +57,7 @@ static boolean isIdempotencyEnabledFromConfig() { * * @param idempotencyKeyName Idempotency Key Name * @param idempotencyKeyValue Idempotency Key Value - * @return List of consent ids + * @return List of consent ids if available, else an empty list will be returned */ static ArrayList getConsentIdsFromIdempotencyKey(String idempotencyKeyName, String idempotencyKeyValue) { @@ -87,46 +74,50 @@ static ArrayList getConsentIdsFromIdempotencyKey(String idempotencyKeyNa * Method to compare the client ID sent in the request and client id retrieved from the database. * * @param requestClientID Client ID sent in the request - * @param dbClientId client ID retrieved from the database - * @return Whether JSON client Ids are equal + * @param dbClientID client ID retrieved from the database + * @return true if the client ID sent in the request and client id retrieved from the database are equal */ - static boolean isClientIdsMatching(String requestClientID, String dbClientId) { - return requestClientID.equals(dbClientId); + static boolean isClientIDEqual(String requestClientID, String dbClientID) { + if (requestClientID == null) { + return false; + } + return requestClientID.equals(dbClientID); } /** * Method to check whether difference between two dates is less than the configured time. * * @param createdTime Created Time of the request - * @return Whether the request is received within allowed time + * @return true if the request is received within allowed time */ static boolean isRequestReceivedWithinAllowedTime(long createdTime) { if (createdTime == 0L) { + log.debug("Created time is of the previous request is not correctly set. Hence returning false"); return false; } String allowedTimeDuration = parser.getIdempotencyAllowedTime(); - if (allowedTimeDuration != null) { - OffsetDateTime createdDate = OffsetDateTime.parse(convertToISO8601DateTimeFormat(createdTime)); + if (StringUtils.isNotBlank(allowedTimeDuration)) { + OffsetDateTime createdDate = OffsetDateTime.parse(toISO8601DateTime(createdTime)); OffsetDateTime currDate = OffsetDateTime.now(createdDate.getOffset()); - long diffInHours = Duration.between(createdDate, currDate).toMinutes(); - return diffInHours <= Long.parseLong(allowedTimeDuration); + long diffInMinutes = Duration.between(createdDate, currDate).toMinutes(); + return diffInMinutes <= Long.parseLong(allowedTimeDuration); } else { - log.error("Idempotency Allowed duration is null"); + log.error("Idempotency Allowed duration is configured in the system. Hence returning false"); return false; } } /** - * Convert long date values to ISO 8601 format. - * @param dateValue Date value + * Convert long date values to ISO 8601 format. ISO 8601 format - "yyyy-MM-dd'T'HH:mm:ssXXX" + * @param epochDate Date value in epoch format * @return ISO 8601 formatted date */ - private static String convertToISO8601DateTimeFormat(long dateValue) { + private static String toISO8601DateTime(long epochDate) { DateFormat simple = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); - Date simpleDateVal = new Date(dateValue * 1000); + Date simpleDateVal = new Date(epochDate * 1000); return simple.format(simpleDateVal); } } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java index 216296e6..7ec9bf63 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java @@ -26,6 +26,7 @@ import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; import com.wso2.openbanking.accelerator.consent.mgt.service.ConsentCoreService; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -57,42 +58,54 @@ public class IdempotencyValidator { public IdempotencyValidationResult validateIdempotency(ConsentManageData consentManageData) throws IdempotencyValidationException { - if (IdempotencyValidationUtils.isIdempotencyEnabledFromConfig()) { - // If idempotency key value, client id or request is empty then cannot proceed with idempotency validation - if (!IdempotencyValidationUtils.isMandatoryParamsPresent(consentManageData, - getIdempotencyHeaderName())) { - log.error("Idempotency Key Value, Client ID or Request is empty. Hence cannot proceed with " + - "idempotency validation"); - return new IdempotencyValidationResult(false, false, null, null); - } - try { - String idempotencyKeyName = getIdempotencyAttributeName(consentManageData.getRequestPath()); - String idempotencyKeyValue = consentManageData.getHeaders().get(getIdempotencyHeaderName()); - // Retrieve consent ids that have the idempotency key name and value as attribute - ArrayList consentIds = IdempotencyValidationUtils - .getConsentIdsFromIdempotencyKey(idempotencyKeyName, idempotencyKeyValue); - // Check whether the consent id list is not empty. If idempotency key exists in the database then - // the consent Id list will be not empty. - if (!consentIds.isEmpty()) { - log.debug(String.format("Idempotency Key %s exists in the database. Hence this is an idempotent" + - " request", idempotencyKeyValue)); - for (String consentId : consentIds) { - DetailedConsentResource consentRequest = consentCoreService.getDetailedConsent(consentId); - if (consentRequest != null) { - return validateIdempotencyConditions(consentManageData, consentRequest); - } else { - log.error(IdempotencyConstants.ERROR_NO_CONSENT_DETAILS); - throw new IdempotencyValidationException(IdempotencyConstants.ERROR_NO_CONSENT_DETAILS); - } + if (!IdempotencyValidationUtils.isIdempotencyEnabledFromConfig()) { + return new IdempotencyValidationResult(false, false, null, null); + } + // If request is empty then cannot proceed with idempotency validation + if (consentManageData.getPayload() == null) { + log.error("Request payload is empty. Hence cannot proceed with idempotency validation"); + return new IdempotencyValidationResult(false, false, null, null); + } + // If client id is empty then cannot proceed with idempotency validation + if (StringUtils.isBlank(consentManageData.getClientId())) { + log.error("Client ID is empty. Hence cannot proceed with idempotency validation"); + return new IdempotencyValidationResult(false, false, null, null); + } + String idempotencyKeyValue = consentManageData.getHeaders().get(getIdempotencyHeaderName()); + // If idempotency key value is empty then cannot proceed with idempotency validation + if (StringUtils.isBlank(idempotencyKeyValue)) { + log.error("Idempotency Key Valueis empty. Hence cannot proceed with idempotency validation"); + return new IdempotencyValidationResult(false, false, null, null); + } + try { + String idempotencyKeyName = getIdempotencyAttributeName(consentManageData.getRequestPath()); + // Retrieve consent ids that have the idempotency key name and value as attribute + ArrayList consentIds = IdempotencyValidationUtils + .getConsentIdsFromIdempotencyKey(idempotencyKeyName, idempotencyKeyValue); + // Check whether the consent id list is not empty. If idempotency key exists in the database then + // the consent Id list will be not empty. + if (!consentIds.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug(String.format("Idempotency Key %s exists in the database. Hence this is an" + + " idempotent request", idempotencyKeyValue)); + } + for (String consentId : consentIds) { + DetailedConsentResource consentRequest = consentCoreService.getDetailedConsent(consentId); + if (consentRequest != null) { + return validateIdempotencyConditions(consentManageData, consentRequest); + } else { + String errorMsg = String.format(IdempotencyConstants.ERROR_NO_CONSENT_DETAILS, consentId); + log.error(errorMsg); + throw new IdempotencyValidationException(errorMsg); } } - } catch (IOException e) { - log.error(IdempotencyConstants.JSON_COMPARING_ERROR, e); - throw new IdempotencyValidationException(IdempotencyConstants.JSON_COMPARING_ERROR); - } catch (ConsentManagementException e) { - log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); - return new IdempotencyValidationResult(true, false, null, null); } + } catch (IOException e) { + log.error(IdempotencyConstants.JSON_COMPARING_ERROR, e); + throw new IdempotencyValidationException(IdempotencyConstants.JSON_COMPARING_ERROR); + } catch (ConsentManagementException e) { + log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); + return new IdempotencyValidationResult(true, false, null, null); } return new IdempotencyValidationResult(false, false, null, null); } @@ -113,8 +126,7 @@ private IdempotencyValidationResult validateIdempotencyConditions(ConsentManageD throws IdempotencyValidationException, IOException { // Compare the client ID sent in the request and client id retrieved from the database // to validate whether the request is received from the same client - if (IdempotencyValidationUtils.isClientIdsMatching(consentManageData.getClientId(), - consentRequest.getClientID())) { + if (IdempotencyValidationUtils.isClientIDEqual(consentRequest.getClientID(), consentManageData.getClientId())) { // Check whether difference between two dates is less than the configured time if (IdempotencyValidationUtils.isRequestReceivedWithinAllowedTime(getCreatedTimeOfPreviousRequest( consentManageData.getRequestPath(), consentRequest.getConsentID()))) { @@ -148,7 +160,7 @@ private IdempotencyValidationResult validateIdempotencyConditions(ConsentManageD * @return idempotency Attribute Name. */ public String getIdempotencyAttributeName(String resourcePath) { - return "IdempotencyKey"; + return IdempotencyConstants.IDEMPOTENCY_KEY_NAME; } /** @@ -157,7 +169,7 @@ public String getIdempotencyAttributeName(String resourcePath) { * @return idempotency Header Name. */ public String getIdempotencyHeaderName() { - return "x-idempotency-key"; + return IdempotencyConstants.X_IDEMPOTENCY_KEY; } /** @@ -175,7 +187,9 @@ public long getCreatedTimeOfPreviousRequest(String resourcePath, String consentI log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); return 0L; } - + if (consentRequest == null) { + return 0L; + } return consentRequest.getCreatedTime(); } @@ -194,6 +208,9 @@ public String getPayloadOfPreviousRequest(String resourcePath, String consentId) log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); return null; } + if (consentRequest == null) { + return null; + } return consentRequest.getReceipt(); } @@ -213,8 +230,13 @@ public boolean isPayloadSimilar(ConsentManageData consentManageData, String cons JsonNode expectedNode = null; JsonNode actualNode = null; try { - expectedNode = new ObjectMapper().readTree(consentManageData.getPayload().toString()); - actualNode = new ObjectMapper().readTree(consentReceipt); + ObjectMapper mapper = new ObjectMapper(); + expectedNode = mapper.readTree(consentManageData.getPayload().toString()); + actualNode = mapper.readTree(consentReceipt); + if (log.isDebugEnabled()) { + log.debug(String.format("Expected payload for idempotent request is: %s. But actual payload " + + "received is %s", expectedNode.toString(), actualNode.toString())); + } } catch (JsonProcessingException e) { log.error(IdempotencyConstants.JSON_COMPARING_ERROR, e); return false; diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java index 386e8032..3d0c122f 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java @@ -115,8 +115,6 @@ public class IdempotencyValidatorTests { @BeforeClass public void beforeTest() { configs = new HashMap<>(); - configs.put(IDEMPOTENCY_IS_ENABLED, "true"); - configs.put(IDEMPOTENCY_ALLOWED_TIME, "1"); headers = new HashMap<>(); headers.put(IdempotencyConstants.X_IDEMPOTENCY_KEY, "123456"); @@ -126,6 +124,8 @@ public void beforeTest() { consentCoreServiceImpl = Mockito.mock(ConsentCoreServiceImpl.class); OpenBankingConfigParser openBankingConfigParserMock = PowerMockito.mock(OpenBankingConfigParser.class); Mockito.doReturn(configs).when(openBankingConfigParserMock).getConfiguration(); + Mockito.doReturn(true).when(openBankingConfigParserMock).isIdempotencyValidationEnabled(); + Mockito.doReturn("1").when(openBankingConfigParserMock).getIdempotencyAllowedTime(); ConsentExtensionsDataHolder consentExtensionsDataHolderMock = PowerMockito .mock(ConsentExtensionsDataHolder.class); From 6b7b503501568a245aa7f71c45404f10cdebef17 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Mon, 25 Mar 2024 15:33:24 +0530 Subject: [PATCH 05/23] Fixed review comments --- .../idempotency/IdempotencyValidationResult.java | 7 +++++++ .../common/idempotency/IdempotencyValidator.java | 12 ++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationResult.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationResult.java index 32745967..a67f6694 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationResult.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationResult.java @@ -41,6 +41,13 @@ public IdempotencyValidationResult(boolean isIdempotent, boolean isValid, Detail this.consentId = consentId; } + public IdempotencyValidationResult(boolean isIdempotent, boolean isValid) { + this.isIdempotent = isIdempotent; + this.isValid = isValid; + this.consent = null; + this.consentId = null; + } + public boolean isIdempotent() { return isIdempotent; } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java index 7ec9bf63..1fc28751 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java @@ -59,23 +59,23 @@ public IdempotencyValidationResult validateIdempotency(ConsentManageData consent throws IdempotencyValidationException { if (!IdempotencyValidationUtils.isIdempotencyEnabledFromConfig()) { - return new IdempotencyValidationResult(false, false, null, null); + return new IdempotencyValidationResult(false, false); } // If request is empty then cannot proceed with idempotency validation if (consentManageData.getPayload() == null) { log.error("Request payload is empty. Hence cannot proceed with idempotency validation"); - return new IdempotencyValidationResult(false, false, null, null); + return new IdempotencyValidationResult(false, false); } // If client id is empty then cannot proceed with idempotency validation if (StringUtils.isBlank(consentManageData.getClientId())) { log.error("Client ID is empty. Hence cannot proceed with idempotency validation"); - return new IdempotencyValidationResult(false, false, null, null); + return new IdempotencyValidationResult(false, false); } String idempotencyKeyValue = consentManageData.getHeaders().get(getIdempotencyHeaderName()); // If idempotency key value is empty then cannot proceed with idempotency validation if (StringUtils.isBlank(idempotencyKeyValue)) { log.error("Idempotency Key Valueis empty. Hence cannot proceed with idempotency validation"); - return new IdempotencyValidationResult(false, false, null, null); + return new IdempotencyValidationResult(false, false); } try { String idempotencyKeyName = getIdempotencyAttributeName(consentManageData.getRequestPath()); @@ -105,9 +105,9 @@ public IdempotencyValidationResult validateIdempotency(ConsentManageData consent throw new IdempotencyValidationException(IdempotencyConstants.JSON_COMPARING_ERROR); } catch (ConsentManagementException e) { log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); - return new IdempotencyValidationResult(true, false, null, null); + return new IdempotencyValidationResult(true, false); } - return new IdempotencyValidationResult(false, false, null, null); + return new IdempotencyValidationResult(false, false); } /** From 473c6a6abe9cc7caaa346c896d00d9146ed1922c Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Mon, 25 Mar 2024 17:14:31 +0530 Subject: [PATCH 06/23] Fixed review comments --- .../common/idempotency/IdempotencyValidationUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java index 955078f3..d39d841c 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java @@ -104,7 +104,7 @@ static boolean isRequestReceivedWithinAllowedTime(long createdTime) { long diffInMinutes = Duration.between(createdDate, currDate).toMinutes(); return diffInMinutes <= Long.parseLong(allowedTimeDuration); } else { - log.error("Idempotency Allowed duration is configured in the system. Hence returning false"); + log.error("Idempotency allowed duration is not configured in the system. Hence returning false"); return false; } } From a4a8132d3869d130f077a8f845cfabf5effd49f4 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Mon, 25 Mar 2024 17:19:59 +0530 Subject: [PATCH 07/23] Fixed review comments --- .../common/idempotency/IdempotencyConstants.java | 1 + .../idempotency/IdempotencyValidationUtils.java | 14 ++------------ .../common/idempotency/IdempotencyValidator.java | 3 ++- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java index af0a8fa2..c3d3993f 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java @@ -25,6 +25,7 @@ public class IdempotencyConstants { public static final String CONTENT_TYPE_TAG = "content-type"; public static final String X_IDEMPOTENCY_KEY = "x-idempotency-key"; public static final String IDEMPOTENCY_KEY_NAME = "IdempotencyKey"; + public static final String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX"; public static final String ERROR_PAYLOAD_NOT_SIMILAR = "Payloads are not similar. Hence this is not a valid" + " idempotent request"; public static final String ERROR_AFTER_ALLOWED_TIME = "Request received after the allowed time., Hence this is" + diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java index d39d841c..ce1119b1 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidationUtils.java @@ -39,19 +39,9 @@ public class IdempotencyValidationUtils { private static final Log log = LogFactory.getLog(IdempotencyValidationUtils.class); - private static final OpenBankingConfigParser parser = OpenBankingConfigParser.getInstance(); private static final ConsentCoreService consentCoreService = ConsentExtensionsDataHolder.getInstance() .getConsentCoreService(); - /** - * Method to check whether Idempotency handling is required. - * - * @return True if idempotency is required, else False. - */ - static boolean isIdempotencyEnabledFromConfig() { - return parser.isIdempotencyValidationEnabled(); - } - /** * Method to retrieve the consent ids that have the idempotency key name and value as attribute. * @@ -96,7 +86,7 @@ static boolean isRequestReceivedWithinAllowedTime(long createdTime) { log.debug("Created time is of the previous request is not correctly set. Hence returning false"); return false; } - String allowedTimeDuration = parser.getIdempotencyAllowedTime(); + String allowedTimeDuration = OpenBankingConfigParser.getInstance().getIdempotencyAllowedTime(); if (StringUtils.isNotBlank(allowedTimeDuration)) { OffsetDateTime createdDate = OffsetDateTime.parse(toISO8601DateTime(createdTime)); OffsetDateTime currDate = OffsetDateTime.now(createdDate.getOffset()); @@ -116,7 +106,7 @@ static boolean isRequestReceivedWithinAllowedTime(long createdTime) { */ private static String toISO8601DateTime(long epochDate) { - DateFormat simple = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); + DateFormat simple = new SimpleDateFormat(IdempotencyConstants.ISO_FORMAT); Date simpleDateVal = new Date(epochDate * 1000); return simple.format(simpleDateVal); } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java index 1fc28751..19b1d193 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; @@ -58,7 +59,7 @@ public class IdempotencyValidator { public IdempotencyValidationResult validateIdempotency(ConsentManageData consentManageData) throws IdempotencyValidationException { - if (!IdempotencyValidationUtils.isIdempotencyEnabledFromConfig()) { + if (!OpenBankingConfigParser.getInstance().isIdempotencyValidationEnabled()) { return new IdempotencyValidationResult(false, false); } // If request is empty then cannot proceed with idempotency validation From 0a8b1342640b1d8202f92a1e4f58063ea4fbdb93 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Tue, 26 Mar 2024 09:25:59 +0530 Subject: [PATCH 08/23] Fixed review comments --- .../IdempotencyValidatorTests.java | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java index 3d0c122f..7a0de2c3 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java @@ -20,6 +20,7 @@ import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; +import com.wso2.openbanking.accelerator.common.util.CarbonUtils; import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; @@ -31,8 +32,10 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.testng.Assert; import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.lang.reflect.Field; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.HashMap; @@ -53,8 +56,6 @@ public class IdempotencyValidatorTests { private String consentId; private Map configs; private Map headers; - private static final String IDEMPOTENCY_IS_ENABLED = "Consent.Idempotency.Enabled"; - private static final String IDEMPOTENCY_ALLOWED_TIME = "Consent.Idempotency.AllowedTimeDuration"; private static final String CLIENT_ID = "testClientId"; private static final String PAYLOAD = "{\n" + @@ -113,15 +114,29 @@ public class IdempotencyValidatorTests { @BeforeClass - public void beforeTest() { + public void beforeTest() throws ReflectiveOperationException { configs = new HashMap<>(); + //to execute util class initialization + new CarbonUtils(); + System.setProperty("some.property", "property.value"); + System.setProperty("carbon.home", "."); + injectEnvironmentVariable("CARBON_HOME", "."); + headers = new HashMap<>(); headers.put(IdempotencyConstants.X_IDEMPOTENCY_KEY, "123456"); headers.put(IdempotencyConstants.CONTENT_TYPE_TAG, "application/json"); consentManageData = Mockito.mock(ConsentManageData.class); consentCoreServiceImpl = Mockito.mock(ConsentCoreServiceImpl.class); + + consentId = UUID.randomUUID().toString(); + consentIdList = new ArrayList<>(); + consentIdList.add(consentId); + } + + @BeforeMethod + public void beforeMethod() { OpenBankingConfigParser openBankingConfigParserMock = PowerMockito.mock(OpenBankingConfigParser.class); Mockito.doReturn(configs).when(openBankingConfigParserMock).getConfiguration(); Mockito.doReturn(true).when(openBankingConfigParserMock).isIdempotencyValidationEnabled(); @@ -135,10 +150,6 @@ public void beforeTest() { PowerMockito.mockStatic(ConsentExtensionsDataHolder.class); PowerMockito.when(ConsentExtensionsDataHolder.getInstance()).thenReturn(consentExtensionsDataHolderMock); PowerMockito.when(consentExtensionsDataHolderMock.getConsentCoreService()).thenReturn(consentCoreServiceImpl); - - consentId = UUID.randomUUID().toString(); - consentIdList = new ArrayList<>(); - consentIdList.add(consentId); } @Test @@ -276,4 +287,35 @@ private DetailedConsentResource getConsent(long createdTime) { consent.setCreatedTime(createdTime); return consent; } + + public static void injectEnvironmentVariable(String key, String value) + throws ReflectiveOperationException { + + Class processEnvironment = Class.forName("java.lang.ProcessEnvironment"); + + Field unmodifiableMapField = getAccessibleField(processEnvironment, "theUnmodifiableEnvironment"); + Object unmodifiableMap = unmodifiableMapField.get(null); + injectIntoUnmodifiableMap(key, value, unmodifiableMap); + + Field mapField = getAccessibleField(processEnvironment, "theEnvironment"); + Map map = (Map) mapField.get(null); + map.put(key, value); + } + + private static Field getAccessibleField(Class clazz, String fieldName) + throws NoSuchFieldException { + + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } + + private static void injectIntoUnmodifiableMap(String key, String value, Object map) + throws ReflectiveOperationException { + + Class unmodifiableMap = Class.forName("java.util.Collections$UnmodifiableMap"); + Field field = getAccessibleField(unmodifiableMap, "m"); + Object obj = field.get(map); + ((Map) obj).put(key, value); + } } From 910e96883d97acde9d281ea21bb2f6bdfd65ec44 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Tue, 26 Mar 2024 10:01:19 +0530 Subject: [PATCH 09/23] Fixed review comments --- .../IdempotencyValidatorTests.java | 44 ++----------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java index 7a0de2c3..aa6f09ae 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java @@ -20,7 +20,6 @@ import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; -import com.wso2.openbanking.accelerator.common.util.CarbonUtils; import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; @@ -30,12 +29,12 @@ import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import java.lang.reflect.Field; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.HashMap; @@ -47,7 +46,7 @@ */ @PrepareForTest({OpenBankingConfigParser.class, ConsentExtensionsDataHolder.class}) @PowerMockIgnore("jdk.internal.reflect.*") -public class IdempotencyValidatorTests { +public class IdempotencyValidatorTests extends PowerMockTestCase { @Mock private ConsentManageData consentManageData; @@ -114,15 +113,9 @@ public class IdempotencyValidatorTests { @BeforeClass - public void beforeTest() throws ReflectiveOperationException { + public void beforeTest() { configs = new HashMap<>(); - //to execute util class initialization - new CarbonUtils(); - System.setProperty("some.property", "property.value"); - System.setProperty("carbon.home", "."); - injectEnvironmentVariable("CARBON_HOME", "."); - headers = new HashMap<>(); headers.put(IdempotencyConstants.X_IDEMPOTENCY_KEY, "123456"); headers.put(IdempotencyConstants.CONTENT_TYPE_TAG, "application/json"); @@ -287,35 +280,4 @@ private DetailedConsentResource getConsent(long createdTime) { consent.setCreatedTime(createdTime); return consent; } - - public static void injectEnvironmentVariable(String key, String value) - throws ReflectiveOperationException { - - Class processEnvironment = Class.forName("java.lang.ProcessEnvironment"); - - Field unmodifiableMapField = getAccessibleField(processEnvironment, "theUnmodifiableEnvironment"); - Object unmodifiableMap = unmodifiableMapField.get(null); - injectIntoUnmodifiableMap(key, value, unmodifiableMap); - - Field mapField = getAccessibleField(processEnvironment, "theEnvironment"); - Map map = (Map) mapField.get(null); - map.put(key, value); - } - - private static Field getAccessibleField(Class clazz, String fieldName) - throws NoSuchFieldException { - - Field field = clazz.getDeclaredField(fieldName); - field.setAccessible(true); - return field; - } - - private static void injectIntoUnmodifiableMap(String key, String value, Object map) - throws ReflectiveOperationException { - - Class unmodifiableMap = Class.forName("java.util.Collections$UnmodifiableMap"); - Field field = getAccessibleField(unmodifiableMap, "m"); - Object obj = field.get(map); - ((Map) obj).put(key, value); - } } From 872101f765a514f5dc10049a05a6c13dba06a353 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Tue, 26 Mar 2024 17:12:03 +0530 Subject: [PATCH 10/23] Changing parent pom version to support jenkins releases --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4aaab44..993bcd09 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ org.wso2 wso2 - 2 + 1.3 From bd0da2be4e8b0e505bbd4c72a5c175a40dc5a010 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Tue, 26 Mar 2024 17:18:44 +0530 Subject: [PATCH 11/23] Changing parent pom version to support jenkins releases --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 993bcd09..0cf90812 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ org.wso2 wso2 - 1.3 + 5.3 From 1053789706cbcce969e8139213d61ee7329098d4 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Thu, 24 Oct 2024 11:19:01 +0530 Subject: [PATCH 12/23] Adding Event notification implementation --- .../components/lib/quartz-2.3.2.jar | Bin 0 -> 693050 bytes .../repository/conf/financial-services.xml.j2 | 98 ++++++++++++ .../config/FinancialServicesConfigParser.java | 150 ++++++++++++++++++ .../constant/FinancialServicesConstants.java | 25 +++ .../common/util/HTTPClientUtils.java | 29 ++++ .../accelerator/common/util/JWTUtils.java | 54 +++++++ financial-services-accelerator/pom.xml | 1 + pom.xml | 16 ++ 8 files changed, 373 insertions(+) create mode 100644 financial-services-accelerator/accelerators/fs-is/carbon-home/repository/components/lib/quartz-2.3.2.jar diff --git a/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/components/lib/quartz-2.3.2.jar b/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/components/lib/quartz-2.3.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..8b7388287d95c578067924846d71c99c1fd389c7 GIT binary patch literal 693050 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 z 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 z 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%0DwyD~b5y3tp|h#%L3cna2f( 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

Qc+Rwao|fzqUX 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�HD|4ZDGMl7q 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

E3#%`hXg`>xDk%_jgb1A+O`M`KvZJDc}q1 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[P454Dg>RJXlPH+^&qbq54qBjaw1)(P{V9Ce89 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 g$dUv*b4(J@yez5oCK literal 0 HcmV?d00001 diff --git a/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/resources/conf/templates/repository/conf/financial-services.xml.j2 b/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/resources/conf/templates/repository/conf/financial-services.xml.j2 index a8761461..b43c194b 100644 --- a/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/resources/conf/templates/repository/conf/financial-services.xml.j2 +++ b/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/resources/conf/templates/repository/conf/financial-services.xml.j2 @@ -210,4 +210,102 @@ 1000 {% endif %} + + + {% if financial_services.event.notifications.event_notification_generator is defined %} + {{financial_services.event.notifications.event_notification_generator}} + {% else %} + com.wso2.openbanking.accelerator.event.notifications.service.service.DefaultEventNotificationGenerator + {% endif %} + {% if financial_services.event.notifications.token_issuer is defined %} + {{financial_services.event.notifications.token_issuer}} + {% else %} + www.wso2.com + {% endif %} + {% if financial_services.event.notifications.number_of_sets_to_return %} + {{financial_services.event.notifications.number_of_sets_to_return}} + {% else %} + 5 + {% endif %} + + {% if financial_services.event.notifications.event_creation_handler is defined %} + {{financial_services.event.notifications.event_creation_handler}} + {% else %} + com.wso2.openbanking.accelerator.event.notifications.service.handler.DefaultEventCreationServiceHandler + {% endif %} + {% if financial_services.event.notifications.event_polling_handler is defined %} + {{financial_services.event.notifications.event_polling_handler}} + {% else %} + com.wso2.openbanking.accelerator.event.notifications.service.handler.DefaultEventPollingServiceHandler + {% endif %} + {% if financial_services.event.notifications.event_subscription_handler is defined %} + {{financial_services.event.notifications.event_subscription_handler}} + {% else %} + com.wso2.openbanking.accelerator.event.notifications.service.handler.DefaultEventSubscriptionServiceHandler + {% endif %} + + {% if financial_services.event.notifications.set_sub_claim_included is defined%} + {{financial_services.event.notifications.set_sub_claim_included}} + {% else %} + true + {% endif %} + {% if financial_services.event.notifications.set_txn_claim_included is defined %} + {{financial_services.event.notifications.set_txn_claim_included}} + {% else %} + true + {% endif %} + {% if financial_services.event.notifications.set_toe_cliam_included is defined %} + {{financial_services.event.notifications.set_toe_cliam_included}} + {% else %} + true + {% endif %} + + + {% if financial_services.event.notifications.realtime.enable is defined %} + {{financial_services.event.notifications.realtime.enable}} + {% else %} + false + {% endif %} + {% if financial_services.event.notifications.realtime.periodic_cron_expression is defined %} + {{financial_services.event.notifications.realtime.periodic_cron_expression}} + {% else %} + 0 0/1 0 ? * * * + {% endif %} + {% if financial_services.event.notifications.realtime.request_timeout is defined %} + {{financial_services.event.notifications.realtime.request_timeout}} + {% else %} + 60 + {% endif %} + {% if financial_services.event.notifications.realtime.maximum_retry_count is defined %} + {{financial_services.event.notifications.realtime.maximum_retry_count}} + {% else %} + 5 + {% endif %} + {% if financial_services.event.notifications.realtime.initial_retry_waiting_time is defined %} + {{financial_services.event.notifications.realtime.initial_retry_waiting_time}} + {% else %} + 60 + {% endif %} + {% if financial_services.event.notifications.realtime.retry_function is defined %} + {{financial_services.event.notifications.realtime.retry_function}} + {% else %} + EX + {% endif %} + {% if financial_services.event.notifications.realtime.circuit_breaker_open_timeout is defined %} + {{financial_services.event.notifications.realtime.circuit_breaker_open_timeout}} + {% else %} + 600 + {% endif %} + {% if financial_services.event.notifications.realtime.thread_pool_size is defined %} + {{financial_services.event.notifications.realtime.thread_pool_size}} + {% else %} + 20 + {% endif %} + {% if financial_services.event.notifications.realtime.event_notification_request_generator is defined %} + {{financial_services.event.notifications.realtime.event_notification_request_generator}} + {% else %} + com.wso2.openbanking.accelerator.event.notifications.service.realtime.service.DefaultRealtimeEventNotificationRequestGenerator + {% endif %} + + diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/config/FinancialServicesConfigParser.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/config/FinancialServicesConfigParser.java index 0f3504f2..74960e6a 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/config/FinancialServicesConfigParser.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/config/FinancialServicesConfigParser.java @@ -601,4 +601,154 @@ public String getConsentValidationConfig() { return source.map(String::trim).orElse(""); } + //Event notifications configurations. + public String getEventNotificationTokenIssuer() { + + Optional source = getConfigurationFromKeyAsString(FinancialServicesConstants.TOKEN_ISSUER); + return source.map(String::trim).orElse("www.wso2.com"); + } + + public int getNumberOfSetsToReturn() { + + Optional config = getConfigurationFromKeyAsString(FinancialServicesConstants.MAX_SETS_TO_RETURN); + return config.map(Integer::parseInt).orElse(5); + } + + public boolean isSubClaimIncluded() { + + Optional config = getConfigurationFromKeyAsString(FinancialServicesConstants.IS_SUB_CLAIM_INCLUDED); + return config.map(Boolean::parseBoolean).orElse(false); + } + + public boolean isToeClaimIncluded() { + + Optional config = getConfigurationFromKeyAsString(FinancialServicesConstants.IS_TOE_CLAIM_INCLUDED); + return config.map(Boolean::parseBoolean).orElse(false); + } + + public boolean isTxnClaimIncluded() { + + Optional config = getConfigurationFromKeyAsString(FinancialServicesConstants.IS_TXN_CLAIM_INCLUDED); + return config.map(Boolean::parseBoolean).orElse(false); + } + + /** + * Method to determine real-time event notification feature is enabled or not from the configurations. + * + * @return boolean value indicating the state + */ + public boolean isRealtimeEventNotificationEnabled() { + + Optional config = getConfigurationFromKeyAsString( + FinancialServicesConstants.REALTIME_EVENT_NOTIFICATION_ENABLED); + return config.map(Boolean::parseBoolean).orElse(false); + } + + /** + * Method to get periodic Cron expression config for realtime event notifications scheduler. + * + * @return String Cron expression to trigger the Cron job for real-time event notification + */ + public String getRealtimeEventNotificationSchedulerCronExpression() { + + Optional source = getConfigurationFromKeyAsString(FinancialServicesConstants.PERIODIC_CRON_EXPRESSION); + return source.map(String::trim).orElse("0 0/1 0 ? * * *"); + } + + /** + * Method to get TIMEOUT_IN_SECONDS config for realtime event notifications. + * + * @return integer timeout for the HTTP Client's POST requests + */ + public int getRealtimeEventNotificationTimeoutInSeconds() { + + Optional config = getConfigurationFromKeyAsString(FinancialServicesConstants.TIMEOUT_IN_SECONDS); + return config.map(Integer::parseInt).orElse(60); + } + + /** + * Method to get MAX_RETRIES config for realtime event notifications. + * + * @return integer maximum number of retries to the retry policy in real-time notification sender + */ + public int getRealtimeEventNotificationMaxRetries() { + + Optional config = getConfigurationFromKeyAsString(FinancialServicesConstants.MAX_RETRIES); + return config.map(Integer::parseInt).orElse(5); + } + + /** + * Method to get INITIAL_BACKOFF_TIME_IN_SECONDS config for realtime event notifications. + * + * @return integer start waiting time for the retry policy before the first retry + */ + public int getRealtimeEventNotificationInitialBackoffTimeInSeconds() { + + Optional config = getConfigurationFromKeyAsString( + FinancialServicesConstants.INITIAL_BACKOFF_TIME_IN_SECONDS); + return config.map(Integer::parseInt).orElse(60); + } + + /** + * Method to get BACKOFF_FUNCTION config for realtime event notifications. + * Function name should be "EX", "CONSTANT" or "LINEAR". + * + * @return string indicating the retry function + */ + public String getRealtimeEventNotificationBackoffFunction() { + + Optional source = getConfigurationFromKeyAsString(FinancialServicesConstants.BACKOFF_FUNCTION); + return source.map(String::trim).orElse("EX"); + } + + /** + * Method to get CIRCUIT_BREAKER_OPEN_TIMEOUT_IN_SECONDS config for realtime event notifications. + * + * @return integer timeout to break the retrying process and make that notification as ERR + */ + public int getRealtimeEventNotificationCircuitBreakerOpenTimeoutInSeconds() { + + Optional config = getConfigurationFromKeyAsString( + FinancialServicesConstants.CIRCUIT_BREAKER_OPEN_TIMEOUT_IN_SECONDS); + return config.map(Integer::parseInt).orElse(600); + } + + /** + * Method to get EVENT_NOTIFICATION_THREAD_POOL_SIZE config for realtime event notifications. + * + * @return integer fix size to set the Thread Pool size in the real-time event notification sender + */ + public int getEventNotificationThreadpoolSize() { + + Optional config = getConfigurationFromKeyAsString( + FinancialServicesConstants.EVENT_NOTIFICATION_THREAD_POOL_SIZE); + return config.map(Integer::parseInt).orElse(20); + } + + /** + * Method to get EVENT_NOTIFICATION_GENERATOR config for event notifications. + * + * @return String class name of the event notification generator to generate the event notification payload + */ + public String getEventNotificationGenerator() { + + Optional source = getConfigurationFromKeyAsString( + FinancialServicesConstants.EVENT_NOTIFICATION_GENERATOR); + return source.map(String::trim).orElse("com.wso2.openbanking.accelerator.event.notifications." + + "service.service.DefaultEventNotificationGenerator"); + } + + /** + * Method to get REALTIME_EVENT_NOTIFICATION_REQUEST_GENERATOR config for realtime event notifications. + * + * @return String class path of the realtime event notification payload generator + */ + public String getRealtimeEventNotificationRequestGenerator() { + + Optional source = getConfigurationFromKeyAsString( + FinancialServicesConstants.REALTIME_EVENT_NOTIFICATION_REQUEST_GENERATOR); + return source.map(String::trim).orElse("com.wso2.openbanking.accelerator.event.notifications.service." + + "realtime.service.DefaultRealtimeEventNotificationRequestGenerator"); + } + } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/constant/FinancialServicesConstants.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/constant/FinancialServicesConstants.java index ccc7707d..75b12d2e 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/constant/FinancialServicesConstants.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/constant/FinancialServicesConstants.java @@ -86,5 +86,30 @@ public class FinancialServicesConstants { public static final String REQUEST_ROUTER = "Gateway.RequestRouter"; public static final String GATEWAY_CACHE_EXPIRY = "Gateway.Cache.GatewayCache.CacheAccessExpiry"; public static final String GATEWAY_CACHE_MODIFIED_EXPIRY = "Gateway.Cache.GatewayCache.CacheModifiedExpiry"; + //Event Notifications Constants + public static final String EVENT_NOTIFICATION_GENERATOR = "EventNotifications.NotificationGeneration." + + "NotificationGenerator"; + public static final String TOKEN_ISSUER = "EventNotifications.NotificationGeneration.TokenIssuer"; + public static final String MAX_SETS_TO_RETURN = "EventNotifications.NotificationGeneration.NumberOfSetsToReturn"; + public static final String SIGNING_ALIAS = "EventNotifications.SigningAlias"; + public static final String IS_SUB_CLAIM_INCLUDED = "EventNotifications.PollingResponseParams.IsSubClaimAvailable"; + public static final String IS_TXN_CLAIM_INCLUDED = "EventNotifications.PollingResponseParams.IsTxnClaimAvailable"; + public static final String IS_TOE_CLAIM_INCLUDED = "EventNotifications.PollingResponseParams.IsToeClaimAvailable"; + public static final String EVENT_CREATION_HANDLER = "EventNotifications.EventCreationHandler"; + public static final String EVENT_POLLING_HANDLER = "EventNotifications.EventPollingHandler"; + public static final String EVENT_SUBSCRIPTION_HANDLER = "EventNotifications.EventSubscriptionHandler"; + public static final String REALTIME_EVENT_NOTIFICATION_ENABLED = "EventNotifications.Realtime.Enable"; + public static final String PERIODIC_CRON_EXPRESSION = "EventNotifications.Realtime.PeriodicCronExpression"; + public static final String TIMEOUT_IN_SECONDS = "EventNotifications.Realtime.TimeoutInSeconds"; + public static final String MAX_RETRIES = "EventNotifications.Realtime.MaxRetries"; + public static final String INITIAL_BACKOFF_TIME_IN_SECONDS = + "EventNotifications.Realtime.InitialBackoffTimeInSeconds"; + public static final String BACKOFF_FUNCTION = "EventNotifications.Realtime.BackoffFunction"; + public static final String CIRCUIT_BREAKER_OPEN_TIMEOUT_IN_SECONDS = + "EventNotifications.Realtime.CircuitBreakerOpenTimeoutInSeconds"; + public static final String EVENT_NOTIFICATION_THREAD_POOL_SIZE = + "EventNotifications.Realtime.EventNotificationThreadPoolSize"; + public static final String REALTIME_EVENT_NOTIFICATION_REQUEST_GENERATOR = + "EventNotifications.Realtime.RequestGenerator"; } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/HTTPClientUtils.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/HTTPClientUtils.java index e42ef2cc..633ea10c 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/HTTPClientUtils.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/HTTPClientUtils.java @@ -88,6 +88,35 @@ public static CloseableHttpClient getHttpsClient() throws FinancialServicesExcep return HttpClients.custom().setConnectionManager(connectionManager).build(); } + /** + * Get closeable https client to send realtime event notifications. + * + * @return Closeable https client + * @throws FinancialServicesException FinancialServicesException exception + */ + @Generated(message = "Ignoring since method contains no logics") + public static CloseableHttpClient getRealtimeEventNotificationHttpsClient() throws FinancialServicesException { + + SSLConnectionSocketFactory sslsf = createSSLConnectionSocketFactory(); + + Registry socketFactoryRegistry = RegistryBuilder.create() + .register(HTTP_PROTOCOL, new PlainConnectionSocketFactory()) + .register(HTTPS_PROTOCOL, sslsf) + .build(); + + final PoolingHttpClientConnectionManager connectionManager = (socketFactoryRegistry != null) ? + new PoolingHttpClientConnectionManager(socketFactoryRegistry) : + new PoolingHttpClientConnectionManager(); + + // configuring default maximum connections + connectionManager.setMaxTotal(FinancialServicesConfigParser.getInstance() + .getRealtimeEventNotificationMaxRetries() + 1); + connectionManager.setDefaultMaxPerRoute(FinancialServicesConfigParser.getInstance() + .getRealtimeEventNotificationMaxRetries() + 1); + + return HttpClients.custom().setConnectionManager(connectionManager).build(); + } + /** * create a SSL Connection Socket Factory. * diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/JWTUtils.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/JWTUtils.java index 3949f857..6c37a447 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/JWTUtils.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/JWTUtils.java @@ -20,8 +20,11 @@ import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSObject; +import com.nimbusds.jose.JWSSigner; import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.jwk.source.RemoteJWKSet; import com.nimbusds.jose.proc.BadJOSEException; @@ -31,6 +34,7 @@ import com.nimbusds.jose.proc.SimpleSecurityContext; import com.nimbusds.jose.util.DefaultResourceRetriever; import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTParser; import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.proc.ConfigurableJWTProcessor; @@ -39,10 +43,12 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.core.util.KeyStoreManager; import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; import org.wso2.financial.services.accelerator.common.constant.FinancialServicesConstants; import org.wso2.financial.services.accelerator.common.exception.ConsentManagementException; import org.wso2.financial.services.accelerator.common.exception.FinancialServicesException; +import org.wso2.financial.services.accelerator.common.exception.FinancialServicesRuntimeException; import org.wso2.financial.services.accelerator.common.internal.FinancialServicesCommonDataHolder; import java.io.FileInputStream; @@ -50,6 +56,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.security.Key; import java.security.KeyFactory; import java.security.KeyStore; import java.security.KeyStoreException; @@ -57,6 +64,7 @@ import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; @@ -353,4 +361,50 @@ public static KeyStore getTrustStore() throws ConsentManagementException { } return FinancialServicesCommonDataHolder.getInstance().getTrustStore(); } + + /** + * Sign a string body using the carbon default key pair. + * Skipped in unit tests since @KeystoreManager cannot be mocked + * + * @param body the body that needs to be signed as a string + * @return string value of the signed JWT + * @throws Exception error if the tenant is invalid + */ + public static String signJWTWithDefaultKey(String body) throws Exception { + KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(-1234); + Key privateKey = keyStoreManager.getDefaultPrivateKey(); + return generateJWT(body, privateKey); + } + + /** + * Util method to generate JWT using a payload and a private key. RS256 is the + * algorithm used + * + * @param payload The payload body to be signed + * @param privateKey The private key for the JWT to be signed with + * @return String signed JWT + */ + public static String generateJWT(String payload, Key privateKey) { + + if (privateKey == null || payload == null) { + log.debug("Null value passed for payload or key. Cannot generate JWT"); + throw new FinancialServicesRuntimeException("Payload and key cannot be null"); + } + + if (!(privateKey instanceof RSAPrivateKey)) { + throw new FinancialServicesRuntimeException("Private key should be an instance of RSAPrivateKey"); + } + + JWSSigner signer = new RSASSASigner((RSAPrivateKey) privateKey); + JWSHeader.Builder headerBuilder = new JWSHeader.Builder(JWSAlgorithm.RS256); + + SignedJWT signedJWT = null; + try { + signedJWT = new SignedJWT(headerBuilder.build(), JWTClaimsSet.parse(payload)); + signedJWT.sign(signer); + } catch (ParseException | JOSEException e) { + throw new FinancialServicesRuntimeException("Error occurred while signing JWT"); + } + return signedJWT.serialize(); + } } diff --git a/financial-services-accelerator/pom.xml b/financial-services-accelerator/pom.xml index c2b8ba0d..dc847105 100644 --- a/financial-services-accelerator/pom.xml +++ b/financial-services-accelerator/pom.xml @@ -39,6 +39,7 @@ components/org.wso2.financial.services.accelerator.consent.mgt.extensions components/org.wso2.financial.services.accelerator.identity.extensions components/org.wso2.financial.services.accelerator.gateway + components/org.wso2.financial.services.accelerator.event.notifications.service internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint internal-webapps/org.wso2.financial.services.accelerator.authentication.endpoint diff --git a/pom.xml b/pom.xml index 879d236b..8eb777e2 100644 --- a/pom.xml +++ b/pom.xml @@ -383,6 +383,11 @@ jackson-databind ${jackson.databinding.version} + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.databinding.version} + org.springframework spring-web @@ -443,6 +448,11 @@ swagger-parser ${swagger.parser.version} + + org.quartz-scheduler + quartz + ${quartz.version} + org.wso2.carbon @@ -506,6 +516,11 @@ org.wso2.financial.services.accelerator.consent.mgt.extensions ${project.version} + + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.identity.extensions + ${project.version} + org.testng @@ -593,6 +608,7 @@ 9.0.11 2.0.1.Final 2.0.24 + 2.3.2 7.0.75 [7.0.75, 8.0.0) From b74b5f074b6aa03269e2d968ddfb97c50c817792 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Sun, 27 Oct 2024 23:10:21 +0530 Subject: [PATCH 13/23] Adding Event creation, polling and subscription implementation --- .../event-notifications/mssql.sql | 16 +- .../event-notifications/mysql.sql | 16 +- .../event-notifications/oracle.sql | 26 +- .../event-notifications/postgresql.sql | 16 +- .../pom.xml | 214 +++++++++ .../constants/EventNotificationConstants.java | 118 +++++ .../service/dao/EventNotificationDAO.java | 127 ++++++ .../service/dao/EventNotificationDAOImpl.java | 415 ++++++++++++++++++ .../service/dao/EventSubscriptionDAO.java | 120 +++++ .../service/dao/EventSubscriptionDAOImpl.java | 310 +++++++++++++ .../PostgreSqlEventNotificationDAOImpl.java | 218 +++++++++ .../PostgreSqlEventSubscriptionDAOImpl.java | 198 +++++++++ .../dto/EventNotificationErrorDTO.java | 53 +++ .../service/dto/EventPollingDTO.java | 75 ++++ .../service/dto/EventSubscriptionDTO.java | 54 +++ .../service/dto/NotificationCreationDTO.java | 59 +++ .../FSEventNotificationException.java | 35 ++ .../DefaultEventCreationServiceHandler.java | 105 +++++ .../DefaultEventPollingServiceHandler.java | 181 ++++++++ ...efaultEventSubscriptionServiceHandler.java | 345 +++++++++++++++ .../handler/EventCreationServiceHandler.java | 38 ++ .../handler/EventPollingServiceHandler.java | 48 ++ .../EventSubscriptionServiceHandler.java | 94 ++++ .../internal/EventNotificationComponent.java | 100 +++++ .../internal/EventNotificationDataHolder.java | 73 +++ .../model/AggregatedPollingResponse.java | 64 +++ .../service/model/EventCreationResponse.java | 55 +++ .../service/model/EventPolling.java | 86 ++++ .../service/model/EventPollingResponse.java | 55 +++ .../service/model/EventSubscription.java | 99 +++++ .../model/EventSubscriptionResponse.java | 54 +++ .../service/model/Notification.java | 71 +++ .../service/model/NotificationError.java | 52 +++ .../service/model/NotificationEvent.java | 64 +++ .../service/model/NotificationResponse.java | 126 ++++++ .../EventNotificationStoreInitializer.java | 107 +++++ .../EventNotificationSqlStatements.java | 70 +++ .../EventSubscriptionSqlStatements.java | 71 +++ .../MSSQLEventNotificationSqlStatements.java | 32 ++ .../DefaultEventNotificationGenerator.java | 85 ++++ .../service/service/EventCreationService.java | 117 +++++ .../service/EventNotificationGenerator.java | 48 ++ .../service/service/EventPollingService.java | 161 +++++++ .../service/EventSubscriptionService.java | 260 +++++++++++ .../util/EventNotificationServiceUtil.java | 167 +++++++ .../src/main/resources/findbugs-exclude.xml | 42 ++ .../src/main/resources/findbugs-include.xml | 22 + 47 files changed, 4925 insertions(+), 37 deletions(-) create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/constants/EventNotificationConstants.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventNotificationDAO.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventNotificationDAOImpl.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAO.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAOImpl.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/PostgreSqlEventNotificationDAOImpl.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/PostgreSqlEventSubscriptionDAOImpl.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventNotificationErrorDTO.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventPollingDTO.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventSubscriptionDTO.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/NotificationCreationDTO.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/exception/FSEventNotificationException.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventCreationServiceHandler.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventPollingServiceHandler.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventSubscriptionServiceHandler.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationComponent.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationDataHolder.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/AggregatedPollingResponse.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventCreationResponse.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPolling.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPollingResponse.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscription.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscriptionResponse.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/Notification.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationError.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationEvent.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationResponse.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/persistence/EventNotificationStoreInitializer.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/EventNotificationSqlStatements.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/EventSubscriptionSqlStatements.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/MSSQLEventNotificationSqlStatements.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/DefaultEventNotificationGenerator.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventCreationService.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventNotificationGenerator.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventPollingService.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventSubscriptionService.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-exclude.xml create mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-include.xml diff --git a/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/mssql.sql b/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/mssql.sql index d0c43ab8..58965e86 100644 --- a/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/mssql.sql +++ b/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/mssql.sql @@ -21,7 +21,7 @@ -- Since the database systems does not support adding default unix time to the database columns, the default data -- storing is handled within the database querieS. -CREATE TABLE OB_NOTIFICATION ( +CREATE TABLE FS_NOTIFICATION ( NOTIFICATION_ID varchar(36) NOT NULL, CLIENT_ID varchar(255) NOT NULL, RESOURCE_ID varchar(255) NOT NULL, @@ -30,24 +30,24 @@ CREATE TABLE OB_NOTIFICATION ( PRIMARY KEY (NOTIFICATION_ID) ); -CREATE TABLE OB_NOTIFICATION_EVENT ( +CREATE TABLE FS_NOTIFICATION_EVENT ( EVENT_ID int NOT NULL IDENTITY, NOTIFICATION_ID varchar(36) NOT NULL, EVENT_TYPE varchar(200) NOT NULL, EVENT_INFO varchar(1000) NOT NULL, PRIMARY KEY (EVENT_ID), - CONSTRAINT FK_NotificationEvent FOREIGN KEY (NOTIFICATION_ID) REFERENCES OB_NOTIFICATION(NOTIFICATION_ID) + CONSTRAINT FK_NotificationEvent FOREIGN KEY (NOTIFICATION_ID) REFERENCES FS_NOTIFICATION(NOTIFICATION_ID) ); -CREATE TABLE OB_NOTIFICATION_ERROR ( +CREATE TABLE FS_NOTIFICATION_ERROR ( NOTIFICATION_ID varchar(36) NOT NULL, ERROR_CODE varchar(255) NOT NULL, DESCRIPTION varchar(255) NOT NULL, PRIMARY KEY (NOTIFICATION_ID), - CONSTRAINT FK_NotificationError FOREIGN KEY (NOTIFICATION_ID) REFERENCES OB_NOTIFICATION(NOTIFICATION_ID) + CONSTRAINT FK_NotificationError FOREIGN KEY (NOTIFICATION_ID) REFERENCES FS_NOTIFICATION(NOTIFICATION_ID) ); -CREATE TABLE OB_NOTIFICATION_SUBSCRIPTION ( +CREATE TABLE FS_NOTIFICATION_SUBSCRIPTION ( SUBSCRIPTION_ID varchar(36) NOT NULL, CLIENT_ID varchar(255) NOT NULL, REQUEST JSON NOT NULL, @@ -58,9 +58,9 @@ CREATE TABLE OB_NOTIFICATION_SUBSCRIPTION ( PRIMARY KEY (SUBSCRIPTION_ID) ); -CREATE TABLE OB_NOTIFICATION_SUBSCRIBED_EVENTS ( +CREATE TABLE FS_NOTIFICATION_SUBSCRIBED_EVENTS ( SUBSCRIPTION_ID varchar(36) NOT NULL, EVENT_TYPE varchar(255) NOT NULL, PRIMARY KEY (SUBSCRIPTION_ID, EVENT_TYPE), - CONSTRAINT FK_NotificationSubEvents FOREIGN KEY (SUBSCRIPTION_ID) REFERENCES OB_NOTIFICATION_SUBSCRIPTION(SUBSCRIPTION_ID) + CONSTRAINT FK_NotificationSubEvents FOREIGN KEY (SUBSCRIPTION_ID) REFERENCES FS_NOTIFICATION_SUBSCRIPTION(SUBSCRIPTION_ID) ); diff --git a/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/mysql.sql b/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/mysql.sql index 30201693..4cce33d9 100644 --- a/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/mysql.sql +++ b/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/mysql.sql @@ -23,7 +23,7 @@ -- For event notifications feature run the following queries against the openbank_openbankingdb-- -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION ( NOTIFICATION_ID varchar(36) NOT NULL, CLIENT_ID varchar(255) NOT NULL, RESOURCE_ID varchar(255) NOT NULL, @@ -33,26 +33,26 @@ CREATE TABLE IF NOT EXISTS OB_NOTIFICATION ( ) ENGINE=InnoDB; -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_EVENT ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION_EVENT ( EVENT_ID int(11) NOT NULL AUTO_INCREMENT, NOTIFICATION_ID varchar(36) NOT NULL, EVENT_TYPE varchar(200) NOT NULL, EVENT_INFO varchar(1000) NOT NULL, PRIMARY KEY (EVENT_ID), - CONSTRAINT FK_NotificationEvent FOREIGN KEY (NOTIFICATION_ID) REFERENCES OB_NOTIFICATION(NOTIFICATION_ID) + CONSTRAINT FK_NotificationEvent FOREIGN KEY (NOTIFICATION_ID) REFERENCES FS_NOTIFICATION(NOTIFICATION_ID) ) ENGINE=InnoDB; -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_ERROR ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION_ERROR ( NOTIFICATION_ID varchar(36) NOT NULL, ERROR_CODE varchar(255) NOT NULL, DESCRIPTION varchar(255) NOT NULL, PRIMARY KEY (NOTIFICATION_ID), - CONSTRAINT FK_NotificationError FOREIGN KEY (NOTIFICATION_ID) REFERENCES OB_NOTIFICATION(NOTIFICATION_ID) + CONSTRAINT FK_NotificationError FOREIGN KEY (NOTIFICATION_ID) REFERENCES FS_NOTIFICATION(NOTIFICATION_ID) ) ENGINE=InnoDB; -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_SUBSCRIPTION ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION_SUBSCRIPTION ( SUBSCRIPTION_ID varchar(36) NOT NULL, CLIENT_ID varchar(255) NOT NULL, REQUEST JSON NOT NULL, @@ -64,10 +64,10 @@ CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_SUBSCRIPTION ( ) ENGINE=InnoDB; -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_SUBSCRIBED_EVENTS ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION_SUBSCRIBED_EVENTS ( SUBSCRIPTION_ID varchar(36) NOT NULL, EVENT_TYPE varchar(255) NOT NULL, PRIMARY KEY (SUBSCRIPTION_ID, EVENT_TYPE), - CONSTRAINT FK_NotificationSubEvents FOREIGN KEY (SUBSCRIPTION_ID) REFERENCES OB_NOTIFICATION_SUBSCRIPTION(SUBSCRIPTION_ID) + CONSTRAINT FK_NotificationSubEvents FOREIGN KEY (SUBSCRIPTION_ID) REFERENCES FS_NOTIFICATION_SUBSCRIPTION(SUBSCRIPTION_ID) ) ENGINE=InnoDB; diff --git a/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/oracle.sql b/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/oracle.sql index 26a374e4..0d5341ea 100644 --- a/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/oracle.sql +++ b/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/oracle.sql @@ -19,9 +19,9 @@ -- All the data related to time are stored in unix time stamp and therefore, the data types for the time related data -- are represented in BIGINT. -- Since the database systems does not support adding default unix time to the database columns, the default data --- storing is handled within the database querieS. +-- storing is handled within the database queries. -CREATE TABLE OB_NOTIFICATION ( +CREATE TABLE FS_NOTIFICATION ( NOTIFICATION_ID varchar2(36) NOT NULL, CLIENT_ID varchar2(255) NOT NULL, RESOURCE_ID varchar2(255) NOT NULL, @@ -30,34 +30,34 @@ CREATE TABLE OB_NOTIFICATION ( PRIMARY KEY (NOTIFICATION_ID) ); -CREATE TABLE OB_NOTIFICATION_EVENT ( +CREATE TABLE FS_NOTIFICATION_EVENT ( EVENT_ID number(10) NOT NULL, NOTIFICATION_ID varchar2(36) NOT NULL, EVENT_TYPE varchar2(200) NOT NULL, EVENT_INFO varchar2(1000) NOT NULL, PRIMARY KEY (EVENT_ID), - CONSTRAINT FK_NotificationEvent FOREIGN KEY (NOTIFICATION_ID) REFERENCES OB_NOTIFICATION(NOTIFICATION_ID) + CONSTRAINT FK_NotificationEvent FOREIGN KEY (NOTIFICATION_ID) REFERENCES FS_NOTIFICATION(NOTIFICATION_ID) ); -- Generate ID using sequence and trigger -CREATE SEQUENCE OB_NOTIFICATION_EVENT_seq START WITH 1 INCREMENT BY 1; +CREATE SEQUENCE FS_NOTIFICATION_EVENT_seq START WITH 1 INCREMENT BY 1; -CREATE OR REPLACE TRIGGER OB_NOTIFICATION_EVENT_seq_tr - BEFORE INSERT ON OB_NOTIFICATION_EVENT FOR EACH ROW +CREATE OR REPLACE TRIGGER FS_NOTIFICATION_EVENT_seq_tr + BEFORE INSERT ON FS_NOTIFICATION_EVENT FOR EACH ROW WHEN (NEW.EVENT_ID IS NULL) BEGIN - SELECT OB_NOTIFICATION_EVENT_seq.NEXTVAL INTO :NEW.EVENT_ID FROM DUAL; + SELECT FS_NOTIFICATION_EVENT_seq.NEXTVAL INTO :NEW.EVENT_ID FROM DUAL; END; -CREATE TABLE OB_NOTIFICATION_ERROR ( +CREATE TABLE FS_NOTIFICATION_ERROR ( NOTIFICATION_ID varchar2(36) NOT NULL, ERROR_CODE varchar2(255) NOT NULL, DESCRIPTION varchar2(255) NOT NULL, PRIMARY KEY (NOTIFICATION_ID), - CONSTRAINT FK_NotificationError FOREIGN KEY (NOTIFICATION_ID) REFERENCES OB_NOTIFICATION(NOTIFICATION_ID) + CONSTRAINT FK_NotificationError FOREIGN KEY (NOTIFICATION_ID) REFERENCES FS_NOTIFICATION(NOTIFICATION_ID) ) -CREATE TABLE OB_NOTIFICATION_SUBSCRIPTION ( +CREATE TABLE FS_NOTIFICATION_SUBSCRIPTION ( SUBSCRIPTION_ID varchar(36) NOT NULL, CLIENT_ID varchar(255) NOT NULL, REQUEST JSON NOT NULL, @@ -68,9 +68,9 @@ CREATE TABLE OB_NOTIFICATION_SUBSCRIPTION ( PRIMARY KEY (SUBSCRIPTION_ID) ); -CREATE TABLE OB_NOTIFICATION_SUBSCRIBED_EVENTS ( +CREATE TABLE FS_NOTIFICATION_SUBSCRIBED_EVENTS ( SUBSCRIPTION_ID varchar(36) NOT NULL, EVENT_TYPE varchar(255) NOT NULL, PRIMARY KEY (SUBSCRIPTION_ID, EVENT_TYPE), - CONSTRAINT FK_NotificationSubEvents FOREIGN KEY (SUBSCRIPTION_ID) REFERENCES OB_NOTIFICATION_SUBSCRIPTION(SUBSCRIPTION_ID) + CONSTRAINT FK_NotificationSubEvents FOREIGN KEY (SUBSCRIPTION_ID) REFERENCES FS_NOTIFICATION_SUBSCRIPTION(SUBSCRIPTION_ID) ); diff --git a/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/postgresql.sql b/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/postgresql.sql index 86d3d7c3..629230ff 100644 --- a/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/postgresql.sql +++ b/financial-services-accelerator/accelerators/fs-is/carbon-home/dbscripts/financial-services/event-notifications/postgresql.sql @@ -23,7 +23,7 @@ -- For event notifications feature run the following queries against the openbank_openbankingdb-- -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION ( NOTIFICATION_ID varchar(36) NOT NULL, CLIENT_ID varchar(255) NOT NULL, RESOURCE_ID varchar(255) NOT NULL, @@ -32,23 +32,23 @@ CREATE TABLE IF NOT EXISTS OB_NOTIFICATION ( PRIMARY KEY (NOTIFICATION_ID) ); -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_EVENT ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION_EVENT ( EVENT_ID SERIAL PRIMARY KEY, NOTIFICATION_ID varchar(36) NOT NULL, EVENT_TYPE varchar(200) NOT NULL, EVENT_INFO varchar(1000) NOT NULL, - CONSTRAINT FK_NotificationEvent FOREIGN KEY (NOTIFICATION_ID) REFERENCES OB_NOTIFICATION(NOTIFICATION_ID) + CONSTRAINT FK_NotificationEvent FOREIGN KEY (NOTIFICATION_ID) REFERENCES FS_NOTIFICATION(NOTIFICATION_ID) ); -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_ERROR ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION_ERROR ( NOTIFICATION_ID varchar(36) NOT NULL, ERROR_CODE varchar(255) NOT NULL, DESCRIPTION varchar(255) NOT NULL, PRIMARY KEY (NOTIFICATION_ID), - CONSTRAINT FK_NotificationError FOREIGN KEY (NOTIFICATION_ID) REFERENCES OB_NOTIFICATION(NOTIFICATION_ID) + CONSTRAINT FK_NotificationError FOREIGN KEY (NOTIFICATION_ID) REFERENCES FS_NOTIFICATION(NOTIFICATION_ID) ); -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_SUBSCRIPTION ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION_SUBSCRIPTION ( SUBSCRIPTION_ID varchar(36) NOT NULL, CLIENT_ID varchar(255) NOT NULL, REQUEST JSON NOT NULL, @@ -59,9 +59,9 @@ CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_SUBSCRIPTION ( PRIMARY KEY (SUBSCRIPTION_ID) ); -CREATE TABLE IF NOT EXISTS OB_NOTIFICATION_SUBSCRIBED_EVENTS ( +CREATE TABLE IF NOT EXISTS FS_NOTIFICATION_SUBSCRIBED_EVENTS ( SUBSCRIPTION_ID varchar(36) NOT NULL, EVENT_TYPE varchar(255) NOT NULL, PRIMARY KEY (SUBSCRIPTION_ID, EVENT_TYPE), - CONSTRAINT FK_NotificationSubEvents FOREIGN KEY (SUBSCRIPTION_ID) REFERENCES OB_NOTIFICATION_SUBSCRIPTION(SUBSCRIPTION_ID) + CONSTRAINT FK_NotificationSubEvents FOREIGN KEY (SUBSCRIPTION_ID) REFERENCES FS_NOTIFICATION_SUBSCRIPTION(SUBSCRIPTION_ID) ); diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml new file mode 100644 index 00000000..d1826979 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml @@ -0,0 +1,214 @@ + + + 4.0.0 + + + financial-services-accelerator + org.wso2.financial.services.accelerator + 4.0.0-SNAPSHOT + ../../pom.xml + + + org.wso2.financial.services.accelerator.event.notifications.service + bundle + WSO2 Financial Services - Event Notification Service Module + + + + org.apache.commons + commons-lang3 + provided + + + org.json.wso2 + json + + + com.fasterxml.jackson.core + jackson-annotations + + + org.quartz-scheduler + quartz + + + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.common + + + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.consent.mgt.service + + + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.consent.mgt.dao + + + org.testng + testng + test + + + org.mockito + mockito-core + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + src/test/resources/testng.xml + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + + **/*Constants.class + **/*Component.class + **/*DataHolder.class + *SqlStatements.* + **/exceptions/* + **/StoreInitializer/* + + + + + default-prepare-agent + + prepare-agent + + + + default-prepare-agent-integration + + prepare-agent-integration + + + + default-report + + report + + + + default-report-integration + + report-integration + + + + default-check + + check + + + + + BUNDLE + + + INSTRUCTION + COVEREDRATIO + 0.8 + + + + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + Max + Low + true + true + ${project.build.directory}/spotbugs + ${project.basedir}/src/main/resources/findbugs-exclude.xml + ${project.basedir}/src/main/resources/findbugs-include.xml + + + com.h3xstream.findsecbugs + findsecbugs-plugin + ${com.h3xstream.findsecbugs.version} + + + + + + analyze-compile + compile + + check + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + + ${project.artifactId} + + + org.wso2.financial.services.accelerator.event.notifications.service.internal + + + org.osgi.framework;version="${osgi.framework.imp.pkg.version.range}", + org.osgi.service.component;version="${osgi.service.component.imp.pkg.version.range}", + com.wso2.openbanking.accelerator.common.*;version="${project.version}", + org.apache.commons.lang3;version="${commons-lang3.version}" + com.wso2.openbanking.accelerator.consent.mgt.service.*;version="${project.version}", + + + !org.wso2.financial.services.accelerator.event.notifications.service.internal, + org.wso2.financial.services.accelerator.event.notifications.service.*;version="${project.version}", + + + javax.ws.rs-api;scope=compile;inline=false, + + * + <_dsannotations>* + + + + + + diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/constants/EventNotificationConstants.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/constants/EventNotificationConstants.java new file mode 100644 index 00000000..94b1190c --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/constants/EventNotificationConstants.java @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.constants; + +/** + * Event Notification Constants. + */ +public class EventNotificationConstants { + + //Service level constants + public static final String X_WSO2_CLIENT_ID = "x-wso2-client_id"; + + //Event Notification Status + public static final String ACK = "ACK"; + public static final String ERROR = "ERR"; + public static final String OPEN = "OPEN"; + + //Response Status + public static final String NOT_FOUND = "NOTFOUND"; + public static final String OK = "OK"; + public static final String CREATED = "CREATED"; + public static final String BAD_REQUEST = "BADREQUEST"; + public static final String NO_CONTENT = "NO_CONTENT"; + public static final String INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR"; + //Database columns + public static final String NOTIFICATION_ID = "NOTIFICATION_ID"; + public static final String CLIENT_ID = "CLIENT_ID"; + public static final String RESOURCE_ID = "RESOURCE_ID"; + public static final String STATUS = "STATUS"; + public static final String UPDATED_TIMESTAMP = "UPDATED_TIMESTAMP"; + public static final String EVENT_INFO = "EVENT_INFO"; + public static final String EVENT_TYPE = "EVENT_TYPE"; + public static final String SUBSCRIPTION_ID = "SUBSCRIPTION_ID"; + public static final String CALLBACK_URL = "CALLBACK_URL"; + public static final String TIME_STAMP = "TIMESTAMP"; + public static final String SPEC_VERSION = "SPEC_VERSION"; + public static final String REQUEST = "REQUEST"; + + //Error Constants + public static final String INVALID_REQUEST = "invalid_request"; + public static final String EVENT_NOTIFICATION_CREATION_ERROR = "Error occurred while saving event " + + "notifications in the database"; + public static final String MISSING_REQ_PAYLOAD = "No request payload found"; + public static final String MISSING_HEADER_PARAM_CLIENT_ID = "Missing header x-wso2-client_id"; + public static final String MISSING_HEADER_PARAM_RESOURCE_ID = "Missing header x-wso2-resource_id"; + public static final String ERROR_IN_EVENT_POLLING_REQUEST = "Error in event polling request"; + public static final String INVALID_CHARS_IN_HEADER_ERROR = "Invalid characters found in the request headers"; + + //Polling request params + public static final String SET_ERRORS = "setErrs"; + public static final String MAX_EVENTS = "maxEvents"; + public static final String DESCRIPTION = "description"; + public static final String RETURN_IMMEDIATELY = "returnImmediately"; + + //Polling response params + public static final String SETS = "sets"; + public static final String MORE_AVAILABLE = "moreAvailable"; + public static final String NOTIFICATIONS_ID = "notificationsID"; + + // Event Subscription Request Params + public static final String SUBSCRIPTION_ID_PARAM = "subscriptionId"; + public static final String CALLBACK_URL_PARAM = "callbackUrl"; + public static final String VERSION_PARAM = "version"; + public static final String EVENT_TYPE_PARAM = "eventTypes"; + public static final String DATA_PARAM = "data"; + + public static final String DB_ERROR_UPDATING = "Database error while updating notification with ID : " + + "'%s' in the database. "; + public static final String DB_ERROR_NOTIFICATION_RETRIEVE = "Error occurred while retrieving" + + " notifications for client ID : '%s'."; + public static final String DB_FAILED_ERROR_NOTIFICATION_STORING = "Failed to store error notification with ID : "; + public static final String DB_ERROR_STORING_ERROR_NOTIFICATION = "Error occurred while closing the " + + "event-notification database connection"; + public static final String DB_ERROR_EVENTS_RETRIEVE = "Error occurred while retrieving events for" + + " notifications ID : '%s'."; + public static final String PARSE_ERROR_NOTIFICATION_ID = "Error occurred while parsing events for" + + " notifications ID : '%s'."; + public static final String DB_CONN_ESTABLISHED = "Database connection is established to get notification " + + "for client ID : '%s' in the database. "; + public static final String RETRIEVED_NOTIFICATION_CLIENT = "Retrieved notification for client ID: '%s'. "; + + public static final String RETRIEVED_EVENTS_NOTIFICATION = "Retrieved events for notification ID: '%s'. "; + public static final String NO_NOTIFICATIONS_FOUND_CLIENT = "No notifications found for client ID - '%s'"; + public static final String NO_EVENTS_NOTIFICATION_ID = "No events found for notification ID - '%s'"; + public static final String INVALID_CLIENT_ID = "Invalid mandatory parameter x-wso2-client-id."; + public static final String DATABASE_CONNECTION_CLOSE_LOG_MSG = "Closing database connection"; + + public static final String ERROR_STORING_EVENT_SUBSCRIPTION = "Error occurred while storing event " + + "subscription in the database. "; + public static final String ERROR_UPDATING_EVENT_SUBSCRIPTION = "Error occurred while updating event " + + "subscription in the database. "; + public static final String ERROR_RETRIEVING_EVENT_SUBSCRIPTION = "Error occurred while retrieving event " + + "subscription in the database. "; + public static final String ERROR_RETRIEVING_EVENT_SUBSCRIPTIONS = "Error occurred while retrieving event " + + "subscriptions in the database."; + public static final String ERROR_DELETING_EVENT_SUBSCRIPTION = "Error occurred while deleting event " + + "subscription in the database. "; + public static final String EVENT_SUBSCRIPTION_NOT_FOUND = "Event subscription not found."; + public static final String EVENT_SUBSCRIPTIONS_NOT_FOUND = "Event subscriptions not found for the given client id."; + public static final String ERROR_HANDLING_EVENT_SUBSCRIPTION = "Error occurred while handling the event " + + "subscription request"; +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventNotificationDAO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventNotificationDAO.java new file mode 100644 index 00000000..d195a96d --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventNotificationDAO.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dao; + +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.Notification; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationError; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationEvent; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; + +/** + * Event Publisher DAO interface. + */ +public interface EventNotificationDAO { + + /** + * This method is used to persist event notifications in the database. + * + * @param connection Database connection + * @param notification Notification details + * @param eventsList List of notification events + * @return NotificationID of the saved notification. + * @throws FSEventNotificationException Exception when persisting event notification data + */ + String persistEventNotification(Connection connection, Notification notification, + ArrayList eventsList) + throws FSEventNotificationException; + + /** + * This method is to update the notification status by ID, allowed values are. + * OPEN,ACK and ERR + * + * @param connection Database connection + * @param notificationId Notification ID to update + * @param notificationStatus Notification status to update + * @throws FSEventNotificationException Exception when updating notification status by ID + */ + void updateNotificationStatusById(Connection connection, String notificationId, String notificationStatus) + throws FSEventNotificationException; + + /** + * This method is to store event notifications error details in the FS_NOTIFICATION table. + * + * @param connection Database connection + * @param notificationError Notification error details + * @throws FSEventNotificationException Exception when storing event notifications error details + */ + void storeErrorNotification(Connection connection, NotificationError notificationError) + throws FSEventNotificationException; + + /** + * This method is to retrieve given number of notifications in the FS_NOTIFICATION table by client and status. + * + * @param connection Database connection + * @param clientId Client ID to retrieve notifications + * @param status Notification status to retrieve + * @param max Maximum number of notifications to retrieve + * @return List of notifications by client and status + * @throws FSEventNotificationException Exception when retrieving notifications by client ID and status + */ + List getNotificationsByClientIdAndStatus(Connection connection, String clientId, String + status, int max) throws FSEventNotificationException; + + /** + * This method is to retrieve notifications by NotificationID. + * + * @param connection Database connection + * @param notificationId Notification ID to retrieve + * @return List of notifications by notification ID + * @throws FSEventNotificationException Exception when retrieving notifications by notification ID + */ + List getEventsByNotificationID(Connection connection, String notificationId) + throws FSEventNotificationException; + + /** + * This method is to retrieve notifications in the FS_NOTIFICATION table by status. + * + * @param connection Database connection + * @param status Notification status to retrieve + * @return List of notifications by status + * @throws FSEventNotificationException Exception when retrieving notifications by status + */ + List getNotificationsByStatus(Connection connection, String status) + throws FSEventNotificationException; + + /** + * This method is to retrieve notificationsCount by ClientId and Status. + * + * @param connection Database connection + * @param clientId Client ID to retrieve notifications + * @param eventStatus Notification status to retrieve + * @return Notification count by client ID and status + * @throws FSEventNotificationException Exception when retrieving notification count by client ID and status + */ + int getNotificationCountByClientIdAndStatus(Connection connection, String clientId, String eventStatus) + throws FSEventNotificationException; + + /** + * This method is to retrieve the notification status. + * + * @param connection Database connection + * @param notificationId Notification ID to retrieve + * @return Notification status by notification ID + * @throws FSEventNotificationException Exception when retrieving notification status + */ + boolean getNotificationStatus(Connection connection, String notificationId) throws FSEventNotificationException; + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventNotificationDAOImpl.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventNotificationDAOImpl.java new file mode 100644 index 00000000..f379632d --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventNotificationDAOImpl.java @@ -0,0 +1,415 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.Notification; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationError; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationEvent; +import org.wso2.financial.services.accelerator.event.notifications.service.queries.EventNotificationSqlStatements; +import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Persisting event notifications to database. + */ +public class EventNotificationDAOImpl implements EventNotificationDAO { + + private static Log log = LogFactory.getLog(EventNotificationDAOImpl.class); + protected EventNotificationSqlStatements sqlStatements; + + public EventNotificationDAOImpl(EventNotificationSqlStatements eventNotificationSqlStatements) { + this.sqlStatements = eventNotificationSqlStatements; + } + + @Override + public String persistEventNotification(Connection connection, Notification notification, + ArrayList eventsList) + throws FSEventNotificationException { + + int result; + int[] noOfRows; + String persistEventNotification = sqlStatements.getStoreNotification(); + String persistEvents = sqlStatements.getStoreNotificationEvents(); + + try (PreparedStatement persistEventNotificationStmnt = + connection.prepareStatement(persistEventNotification); + PreparedStatement persistEventsStmt = connection.prepareStatement(persistEvents)) { + + log.debug("Setting parameters to prepared statement to add event notification "); + + persistEventNotificationStmnt.setString(1, notification.getNotificationId()); + persistEventNotificationStmnt.setString(2, notification.getClientId()); + persistEventNotificationStmnt.setString(3, notification.getResourceId()); + persistEventNotificationStmnt.setString(4, notification.getStatus()); + + // with result, we can determine whether the insertion was successful or not + result = persistEventNotificationStmnt.executeUpdate(); + + // to insert notification events + for (NotificationEvent event : eventsList) { + persistEventsStmt.setString(1, notification.getNotificationId()); + persistEventsStmt.setString(2, event.getEventType()); + persistEventsStmt.setString(3, event.getEventInformation().toString()); + persistEventsStmt.addBatch(); + } + noOfRows = persistEventsStmt.executeBatch(); + } catch (SQLException e) { + log.error(EventNotificationConstants.EVENT_NOTIFICATION_CREATION_ERROR, e); + throw new FSEventNotificationException(EventNotificationConstants. + EVENT_NOTIFICATION_CREATION_ERROR, e); + } + // Confirm that the data are updated successfully + if (result > 0 && noOfRows.length != 0) { + log.info("Created the event notification successfully"); + return notification.getNotificationId(); + } else { + throw new FSEventNotificationException("Failed to create the event notification."); + } + } + + @Override + public void updateNotificationStatusById(Connection connection, String notificationId, String notificationStatus) + throws FSEventNotificationException { + + String sql = sqlStatements.updateNotificationStatusQueryById(); + + try (PreparedStatement updateNotificationStatusById = connection.prepareStatement(sql)) { + Timestamp currentTimeStamp = new Timestamp(new Date().getTime()); + updateNotificationStatusById.setString(1, notificationStatus); + updateNotificationStatusById.setTimestamp(2, currentTimeStamp); + updateNotificationStatusById.setString(3, notificationId); + + int affectedRows = updateNotificationStatusById.executeUpdate(); + + if (affectedRows != 0) { + connection.commit(); + if (log.isDebugEnabled()) { + log.debug(String.format("Updated notification with Notification ID '%s'", + notificationId.replaceAll("[\r\n]", ""))); + } + } else { + if (log.isDebugEnabled()) { + log.debug(String.format("Failed updating notification with ID : '%s'", + notificationId.replaceAll("[\r\n]", ""))); + } + throw new FSEventNotificationException(String.format(EventNotificationConstants.DB_ERROR_UPDATING, + notificationId)); + } + } catch (SQLException e) { + log.error(String.format(EventNotificationConstants.DB_ERROR_UPDATING, + notificationId.replaceAll("[\r\n]", "")), e); + throw new FSEventNotificationException(String.format(EventNotificationConstants.DB_ERROR_UPDATING, + notificationId)); + } + } + + @Override + public void storeErrorNotification(Connection connection, NotificationError notificationError) + throws FSEventNotificationException { + + String storeErrorNotificationQuery = sqlStatements.storeErrorNotificationQuery(); + + try (PreparedStatement storeErrorNotificationPreparedStatement = + connection.prepareStatement(storeErrorNotificationQuery)) { + + storeErrorNotificationPreparedStatement.setString(1, notificationError. + getNotificationId()); + storeErrorNotificationPreparedStatement.setString(2, notificationError. + getErrorCode()); + storeErrorNotificationPreparedStatement.setString(3, notificationError. + getErrorDescription()); + + int affectedRows = storeErrorNotificationPreparedStatement.executeUpdate(); + if (affectedRows == 1) { + if (log.isDebugEnabled()) { + log.debug(String.format("Successfully stored error notification with ID:'%s'.", + notificationError.getNotificationId().replaceAll("[\r\n]", ""))); + } + } else { + if (log.isDebugEnabled()) { + log.debug(String.format("Failed store error notification with ID:'%s'.", + notificationError.getNotificationId().replaceAll("[\r\n]", ""))); + } + throw new FSEventNotificationException(EventNotificationConstants. + DB_FAILED_ERROR_NOTIFICATION_STORING + notificationError.getNotificationId()); + } + + } catch (SQLException e) { + throw new FSEventNotificationException(EventNotificationConstants. + DB_ERROR_STORING_ERROR_NOTIFICATION, e); + } + } + + @Override + public List getNotificationsByClientIdAndStatus(Connection connection, String clientId, + String status, int max) + throws FSEventNotificationException { + + List notificationList = new ArrayList<>(); + String sql = sqlStatements.getMaxNotificationsQuery(); + + try (PreparedStatement getNotificationsPreparedStatement = connection.prepareStatement(sql)) { + getNotificationsPreparedStatement.setString(1, clientId); + getNotificationsPreparedStatement.setString(2, status); + getNotificationsPreparedStatement.setInt(3, max); + + try (ResultSet notificationResultSet = getNotificationsPreparedStatement.executeQuery()) { + if (notificationResultSet.next()) { + + //bring pointer back to the top of the result set if not on the top + if (!notificationResultSet.isBeforeFirst()) { + notificationResultSet.beforeFirst(); + } + + //read event notifications from the result set + while (notificationResultSet.next()) { + Notification notification = new Notification(); + + notification.setNotificationId(notificationResultSet.getString + (EventNotificationConstants.NOTIFICATION_ID)); + notification.setClientId(notificationResultSet.getString + (EventNotificationConstants.CLIENT_ID)); + notification.setResourceId(notificationResultSet.getString + (EventNotificationConstants.RESOURCE_ID)); + notification.setStatus(notificationResultSet.getString + (EventNotificationConstants.STATUS)); + notification.setUpdatedTimeStamp((notificationResultSet.getTimestamp( + (EventNotificationConstants.UPDATED_TIMESTAMP)).getTime())); + + notificationList.add(notification); + } + notificationResultSet.close(); + getNotificationsPreparedStatement.close(); + + if (log.isDebugEnabled()) { + log.debug(String.format(EventNotificationConstants.RETRIEVED_NOTIFICATION_CLIENT, + clientId.replaceAll("[\r\n]", ""))); + } + } else { + if (log.isDebugEnabled()) { + log.debug(String.format(EventNotificationConstants.NO_NOTIFICATIONS_FOUND_CLIENT, + clientId.replaceAll("[\r\n]", ""))); + } + } + } + } catch (SQLException e) { + throw new FSEventNotificationException(String.format + (EventNotificationConstants.DB_ERROR_NOTIFICATION_RETRIEVE, clientId), e); + } + + return notificationList; + } + + @Override + public List getEventsByNotificationID(Connection connection, String notificationId) + throws FSEventNotificationException { + + List eventList = new ArrayList<>(); + String sql = sqlStatements.getEventsByNotificationIdQuery(); + + try (PreparedStatement getEventsPreparedStatement = connection.prepareStatement(sql)) { + + getEventsPreparedStatement.setString(1, notificationId); + + try (ResultSet eventsResultSet = getEventsPreparedStatement.executeQuery()) { + if (eventsResultSet.next()) { + + //bring pointer back to the top of the result set if not on the top + if (!eventsResultSet.isBeforeFirst()) { + eventsResultSet.beforeFirst(); + } + + //read event notifications from the result set + while (eventsResultSet.next()) { + NotificationEvent event = new NotificationEvent(); + event.setNotificationId(eventsResultSet.getString + (EventNotificationConstants.NOTIFICATION_ID)); + event.setEventType(eventsResultSet.getString + (EventNotificationConstants.EVENT_TYPE)); + event.setEventInformation(EventNotificationServiceUtil. + getEventJSONFromString(eventsResultSet.getString + (EventNotificationConstants.EVENT_INFO))); + eventList.add(event); + } + eventsResultSet.close(); + getEventsPreparedStatement.close(); + + if (log.isDebugEnabled()) { + log.debug(String.format(EventNotificationConstants.RETRIEVED_EVENTS_NOTIFICATION, + notificationId.replaceAll("[\r\n]", ""))); + } + } else { + if (log.isDebugEnabled()) { + log.debug(String.format(EventNotificationConstants.NO_EVENTS_NOTIFICATION_ID, + notificationId.replaceAll("[\r\n]", ""))); + } + } + } catch (SQLException e) { + log.error(String.format(EventNotificationConstants.PARSE_ERROR_NOTIFICATION_ID, + notificationId.replaceAll("[\r\n]", "")), e); + throw new FSEventNotificationException(String.format ( + EventNotificationConstants.PARSE_ERROR_NOTIFICATION_ID, notificationId), e); + } + } catch (SQLException e) { + log.error(String.format(EventNotificationConstants.DB_ERROR_EVENTS_RETRIEVE, + notificationId.replaceAll("[\r\n]", "")), e); + throw new FSEventNotificationException(String.format + (EventNotificationConstants.DB_ERROR_EVENTS_RETRIEVE, notificationId), e); + } + + + return eventList; + } + + @Override + public List getNotificationsByStatus(Connection connection, String status) + throws FSEventNotificationException { + + List notificationList = new ArrayList<>(); + String sql = sqlStatements.getNotificationsByState(); + try (PreparedStatement getNotificationsPreparedStatement = connection.prepareStatement(sql)) { + getNotificationsPreparedStatement.setString(1, status); + + try (ResultSet notificationResultSet = getNotificationsPreparedStatement.executeQuery()) { + if (notificationResultSet.next()) { + //bring pointer back to the top of the result set if not on the top + if (!notificationResultSet.isBeforeFirst()) { + notificationResultSet.beforeFirst(); + } + //read event notifications from the result set + while (notificationResultSet.next()) { + Notification notification = new Notification(); + notification.setNotificationId(notificationResultSet.getString + (EventNotificationConstants.NOTIFICATION_ID)); + notification.setClientId(notificationResultSet.getString + (EventNotificationConstants.CLIENT_ID)); + notification.setResourceId(notificationResultSet.getString + (EventNotificationConstants.RESOURCE_ID)); + notification.setStatus(notificationResultSet.getString + (EventNotificationConstants.STATUS)); + notification.setUpdatedTimeStamp((notificationResultSet.getTimestamp( + (EventNotificationConstants.UPDATED_TIMESTAMP)).getTime())); + notificationList.add(notification); + } + notificationResultSet.close(); + getNotificationsPreparedStatement.close(); + if (log.isDebugEnabled()) { + log.debug( + EventNotificationConstants.RETRIEVED_NOTIFICATION_CLIENT); + } + } else { + if (log.isDebugEnabled()) { + log.debug(EventNotificationConstants.NO_NOTIFICATIONS_FOUND_CLIENT); + } + } + } + } catch (SQLException e) { + throw new FSEventNotificationException(EventNotificationConstants.DB_ERROR_NOTIFICATION_RETRIEVE, e); + } + + return notificationList; + } + + @Override + public int getNotificationCountByClientIdAndStatus(Connection connection, String clientId, String eventStatus) + throws FSEventNotificationException { + + String sql = sqlStatements.getNotificationsCountQuery(); + + try (PreparedStatement getNotificationCount = connection.prepareStatement(sql)) { + + getNotificationCount.setString(1, clientId); + getNotificationCount.setString(2, eventStatus); + + try (ResultSet notificationCountResultSet = getNotificationCount.executeQuery()) { + if (notificationCountResultSet.next()) { + + int count = notificationCountResultSet.getInt("NOTIFICATION_COUNT"); + notificationCountResultSet.close(); + getNotificationCount.close(); + + if (log.isDebugEnabled()) { + log.debug(String.format("Retrieved notification count for client ID: '%s'. ", + clientId.replaceAll("[\r\n]", ""))); + } + + return count; + } else { + if (log.isDebugEnabled()) { + log.debug(String.format( + EventNotificationConstants.NO_NOTIFICATIONS_FOUND_CLIENT, + clientId.replaceAll("[\r\n]", ""))); + } + + return 0; + } + } + } catch (SQLException e) { + throw new FSEventNotificationException(String.format + (EventNotificationConstants.DB_ERROR_NOTIFICATION_RETRIEVE, clientId), e); + } + } + + @Override + public boolean getNotificationStatus(Connection connection, String notificationId) + throws FSEventNotificationException { + + boolean isOpenStatus = false; + + String sql = sqlStatements.getNotificationByNotificationId(); + try (PreparedStatement getNotificationStatus = connection.prepareStatement(sql)) { + getNotificationStatus.setString(1, notificationId); + + try (ResultSet notificationResultSet = getNotificationStatus.executeQuery()) { + if (notificationResultSet.next()) { + + if (EventNotificationConstants.OPEN.equals(notificationResultSet. + getString("STATUS"))) { + isOpenStatus = true; + } + + return isOpenStatus; + } else { + if (log.isDebugEnabled()) { + log.debug(String.format("No notifications found for notification ID - '%s'", + notificationId.replaceAll("[\r\n]", ""))); + } + } + } + } catch (SQLException e) { + throw new FSEventNotificationException(String.format + ("Error occurred while retrieving status for the notifications ID : '%s'.", + notificationId), e); + } + + return false; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAO.java new file mode 100644 index 00000000..a22ad7ed --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAO.java @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dao; + +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscription; + +import java.sql.Connection; +import java.util.List; + +/** + * Event Notification Subscription DAO interface. + */ +public interface EventSubscriptionDAO { + + /** + * This method is used to store event notification subscription in the database. + * + * @param connection Database connection. + * @param eventSubscription EventSubscription object. + * @return EventSubscription object. + * @throws FSEventNotificationException Exception when storing event subscription + */ + EventSubscription storeEventSubscription(Connection connection, EventSubscription eventSubscription) + throws FSEventNotificationException; + + /** + * This method is used to store subscribed event types in the database. + * + * @param connection Database connection. + * @param subscriptionId Subscription ID. + * @param eventTypes Event types to be stored. + * @return List of strings with subscribed event types. + * @throws FSEventNotificationException Exception when storing subscribed event types + */ + List storeSubscribedEventTypes(Connection connection, String subscriptionId, List eventTypes) + throws FSEventNotificationException; + + /** + * This method is used to retrieve an event subscription by a subscription ID. + * + * @param connection Database connection. + * @param subscriptionId Subscription ID. + * @return EventSubscription model. + * @throws FSEventNotificationException Exception when retrieving event subscription by subscription ID + */ + EventSubscription getEventSubscriptionBySubscriptionId(Connection connection, String subscriptionId) + throws FSEventNotificationException; + + /** + * This method is used to retrieve all event subscriptions a client. + * + * @param connection Database connection. + * @param clientId Client ID. + * @return List of EventSubscription models. + * @throws FSEventNotificationException Exception when retrieving event subscriptions by client ID + */ + List getEventSubscriptionsByClientId(Connection connection, String clientId) + throws FSEventNotificationException; + + /** + * This method is used to retrieve all event subscriptions by event type. + * + * @param connection Database connection. + * @param eventType Event type that need to be subscribed by the retrieving subscriptions. + * @return List of EventSubscription models. + * @throws FSEventNotificationException Exception when retrieving event subscriptions by event type + */ + List getEventSubscriptionsByEventType(Connection connection, String eventType) + throws FSEventNotificationException; + + /** + * This method is used to update an event subscription. + * + * @param connection Database connection. + * @param eventSubscription eventSubscription object. + * @return true if update was successful. + * @throws FSEventNotificationException Exception when updating event subscription + */ + Boolean updateEventSubscription(Connection connection, EventSubscription eventSubscription) + throws FSEventNotificationException; + + /** + * This method is used to delete an event subscription. + * + * @param connection Database connection. + * @param subscriptionId Subscription ID. + * @return true if deletion was successful. + * @throws FSEventNotificationException Exception when deleting event subscription + */ + Boolean deleteEventSubscription(Connection connection, String subscriptionId) throws FSEventNotificationException; + + /** + * This method is used to delete subscribed event types of a subscription. + * + * @param connection Database connection. + * @param subscriptionId subscription ID. + * @return true if deletion was successful. + * @throws FSEventNotificationException Exception when deleting subscribed event types + */ + Boolean deleteSubscribedEventTypes(Connection connection, String subscriptionId) + throws FSEventNotificationException; + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAOImpl.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAOImpl.java new file mode 100644 index 00000000..a66db38a --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAOImpl.java @@ -0,0 +1,310 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscription; +import org.wso2.financial.services.accelerator.event.notifications.service.queries.EventSubscriptionSqlStatements; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static java.sql.Statement.EXECUTE_FAILED; + +/** + * Default EventSubscriptionDAO Impl. + */ +public class EventSubscriptionDAOImpl implements EventSubscriptionDAO { + private static Log log = LogFactory.getLog(EventSubscriptionDAOImpl.class); + + protected EventSubscriptionSqlStatements sqlStatements; + + public EventSubscriptionDAOImpl(EventSubscriptionSqlStatements sqlStatements) { + this.sqlStatements = sqlStatements; + } + + public EventSubscription storeEventSubscription(Connection connection, EventSubscription eventSubscription) + throws FSEventNotificationException { + + int storeSubscriptionAffectedRows; + + UUID subscriptionId = UUID.randomUUID(); + long unixTime = Instant.now().getEpochSecond(); + eventSubscription.setSubscriptionId(subscriptionId.toString()); + eventSubscription.setTimeStamp(unixTime); + eventSubscription.setStatus(EventNotificationConstants.CREATED); + + final String sql = sqlStatements.storeEventSubscriptionQuery(); + try (PreparedStatement storeEventSubscriptionStatement = connection.prepareStatement(sql)) { + storeEventSubscriptionStatement.setString(1, eventSubscription.getSubscriptionId()); + storeEventSubscriptionStatement.setString(2, eventSubscription.getClientId()); + storeEventSubscriptionStatement.setString(3, eventSubscription.getCallbackUrl()); + storeEventSubscriptionStatement.setLong(4, eventSubscription.getTimeStamp()); + storeEventSubscriptionStatement.setString(5, eventSubscription.getSpecVersion()); + storeEventSubscriptionStatement.setString(6, eventSubscription.getStatus()); + storeEventSubscriptionStatement.setString(7, eventSubscription.getRequestData()); + storeSubscriptionAffectedRows = storeEventSubscriptionStatement.executeUpdate(); + if (storeSubscriptionAffectedRows == 0) { + log.error("Failed to store the event notification subscription."); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_STORING_EVENT_SUBSCRIPTION); + } + } catch (SQLException e) { + log.error("SQL exception when storing the event types of the subscription", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_STORING_EVENT_SUBSCRIPTION); + } + return eventSubscription; + } + + @Override + public List storeSubscribedEventTypes(Connection connection, String subscriptionId, List eventTypes) + throws FSEventNotificationException { + + final String sql = sqlStatements.storeSubscribedEventTypesQuery(); + try (PreparedStatement storeSubscribedEventTypesStatement = connection.prepareStatement(sql)) { + for (String eventType : eventTypes) { + storeSubscribedEventTypesStatement.setString(1, subscriptionId); + storeSubscribedEventTypesStatement.setString(2, eventType); + storeSubscribedEventTypesStatement.addBatch(); + } + int[] storeSubscribedEventTypesAffectedRows = storeSubscribedEventTypesStatement.executeBatch(); + for (int affectedRows : storeSubscribedEventTypesAffectedRows) { + if (affectedRows == 0 || affectedRows == EXECUTE_FAILED) { + log.error("Failed to store the subscribed event types."); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_STORING_EVENT_SUBSCRIPTION); + } + } + } catch (SQLException e) { + log.error("SQL exception when storing the subscribed event types.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_STORING_EVENT_SUBSCRIPTION); + } + log.debug("Stored the subscribed event types successfully."); + return eventTypes; + + } + + @Override + public EventSubscription getEventSubscriptionBySubscriptionId(Connection connection, String subscriptionId) + throws FSEventNotificationException { + EventSubscription retrievedSubscription = new EventSubscription(); + List eventTypes = new ArrayList<>(); + + final String sql = sqlStatements.getEventSubscriptionBySubscriptionIdQuery(); + try (PreparedStatement getEventSubscriptionBySubscriptionIdStatement = connection.prepareStatement(sql)) { + getEventSubscriptionBySubscriptionIdStatement.setString(1, subscriptionId); + try (ResultSet resultSet = getEventSubscriptionBySubscriptionIdStatement.executeQuery()) { + if (resultSet.next()) { + mapResultSetToEventSubscription(retrievedSubscription, resultSet); + resultSet.beforeFirst(); // Reset the cursor position to the beginning of the result set. + while (resultSet.next()) { + String eventType = resultSet.getString(EventNotificationConstants.EVENT_TYPE); + if (eventType != null) { + eventTypes.add(eventType); + } + } + if (!eventTypes.isEmpty()) { + retrievedSubscription.setEventTypes(eventTypes); + } + } else { + log.error("No event notification subscription found for the given subscription id."); + throw new FSEventNotificationException( + EventNotificationConstants.EVENT_SUBSCRIPTION_NOT_FOUND); + } + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscription.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTION); + } + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscription.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTION); + } + return retrievedSubscription; + } + + @Override + public List getEventSubscriptionsByClientId(Connection connection, String clientId) + throws FSEventNotificationException { + List retrievedSubscriptions = new ArrayList<>(); + + final String sql = sqlStatements.getEventSubscriptionsByClientIdQuery(); + try (PreparedStatement getEventSubscriptionsByClientIdStatement = connection.prepareStatement(sql)) { + getEventSubscriptionsByClientIdStatement.setString(1, clientId); + try (ResultSet resultSet = getEventSubscriptionsByClientIdStatement.executeQuery()) { + if (resultSet.isBeforeFirst()) { + while (resultSet.next()) { + EventSubscription eventSubscription = new EventSubscription(); + List eventTypes = new ArrayList<>(); + mapResultSetToEventSubscription(eventSubscription, resultSet); + resultSet.previous(); + while (resultSet.next()) { + if (eventSubscription.getSubscriptionId().equals(resultSet. + getString(EventNotificationConstants.SUBSCRIPTION_ID))) { + if (resultSet.getString(EventNotificationConstants.EVENT_TYPE) != null) { + eventTypes.add(resultSet.getString(EventNotificationConstants.EVENT_TYPE)); + } + } else { + resultSet.previous(); + break; + } + } + if (!eventTypes.isEmpty()) { + eventSubscription.setEventTypes(eventTypes); + } + retrievedSubscriptions.add(eventSubscription); + } + log.debug("Retrieved the event notification subscriptions successfully."); + } + return retrievedSubscriptions; + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscriptions.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTION); + } + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscriptions.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTIONS); + } + } + + @Override + public List getEventSubscriptionsByEventType(Connection connection, String eventType) + throws FSEventNotificationException { + List retrievedSubscriptions = new ArrayList<>(); + + final String sql = sqlStatements.getEventSubscriptionsByEventTypeQuery(); + try (PreparedStatement getEventSubscriptionsByClientIdAndEventTypeStatement = + connection.prepareStatement(sql)) { + getEventSubscriptionsByClientIdAndEventTypeStatement.setString(1, eventType); + try (ResultSet resultSet = getEventSubscriptionsByClientIdAndEventTypeStatement.executeQuery()) { + if (resultSet.isBeforeFirst()) { + while (resultSet.next()) { + EventSubscription eventSubscription = new EventSubscription(); + List eventTypes = new ArrayList<>(); + mapResultSetToEventSubscription(eventSubscription, resultSet); + resultSet.previous(); + while (resultSet.next()) { + if (eventSubscription.getSubscriptionId().equals(resultSet. + getString(EventNotificationConstants.SUBSCRIPTION_ID))) { + if (resultSet.getString(EventNotificationConstants.EVENT_TYPE) != null) { + eventTypes.add(resultSet.getString(EventNotificationConstants.EVENT_TYPE)); + } + } else { + resultSet.previous(); + break; + } + } + if (!eventTypes.isEmpty()) { + eventSubscription.setEventTypes(eventTypes); + } + retrievedSubscriptions.add(eventSubscription); + } + log.debug("Retrieved the event notification subscriptions successfully."); + } + return retrievedSubscriptions; + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscriptions.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTION); + } + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscriptions.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTIONS); + } + } + + @Override + public Boolean updateEventSubscription(Connection connection, EventSubscription eventSubscription) + throws FSEventNotificationException { + boolean isUpdated = false; + final String sql = sqlStatements.updateEventSubscriptionQuery(); + try (PreparedStatement updateEventSubscriptionStatement = connection.prepareStatement(sql)) { + updateEventSubscriptionStatement.setString(1, eventSubscription.getCallbackUrl()); + updateEventSubscriptionStatement.setLong(2, Instant.now().getEpochSecond()); + updateEventSubscriptionStatement.setString(3, eventSubscription.getRequestData()); + updateEventSubscriptionStatement.setString(4, eventSubscription.getSubscriptionId()); + int affectedRows = updateEventSubscriptionStatement.executeUpdate(); + if (affectedRows > 0) { + log.debug("Event notification subscription is successfully updated."); + isUpdated = true; + } + } catch (SQLException e) { + log.error("SQL exception when updating event notification subscription", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_UPDATING_EVENT_SUBSCRIPTION); + } + return isUpdated; + } + + @Override + public Boolean deleteEventSubscription(Connection connection, String subscriptionId) + throws FSEventNotificationException { + + final String sql = sqlStatements.updateEventSubscriptionStatusQuery(); + try (PreparedStatement deleteEventSubscriptionStatement = connection.prepareStatement(sql)) { + deleteEventSubscriptionStatement.setString(1, "DELETED"); + deleteEventSubscriptionStatement.setString(2, subscriptionId); + int affectedRows = deleteEventSubscriptionStatement.executeUpdate(); + if (affectedRows == 0) { + log.debug("Failed deleting event notification subscription."); + return false; + } + log.debug("Event notification subscription is successfully deleted from the database."); + return true; + } catch (SQLException e) { + log.error("SQL exception when deleting event notification subscription data.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_DELETING_EVENT_SUBSCRIPTION); + } + } + + @Override + public Boolean deleteSubscribedEventTypes(Connection connection, String subscriptionId) + throws FSEventNotificationException { + boolean isDeleted = false; + int affectedRowsCount; + final String deleteEventTypesQuery = sqlStatements.deleteSubscribedEventTypesQuery(); + try (PreparedStatement deleteEventTypesStatement = connection.prepareStatement(deleteEventTypesQuery)) { + deleteEventTypesStatement.setString(1, subscriptionId); + affectedRowsCount = deleteEventTypesStatement.executeUpdate(); + if (affectedRowsCount > 0) { + log.debug("Successfully deleted the subscribed event types"); + isDeleted = true; + } + } catch (SQLException e) { + log.error("SQL exception when deleting subscribed event types. ", e); + throw new FSEventNotificationException( + "Error occurred while deleting the event notification subscription."); + } + return isDeleted; + } + + private void mapResultSetToEventSubscription(EventSubscription response, ResultSet resultSet) throws SQLException { + response.setSubscriptionId(resultSet.getString(EventNotificationConstants.SUBSCRIPTION_ID)); + response.setClientId(resultSet.getString(EventNotificationConstants.CLIENT_ID)); + response.setCallbackUrl(resultSet.getString(EventNotificationConstants.CALLBACK_URL)); + response.setTimeStamp(resultSet.getLong(EventNotificationConstants.TIME_STAMP)); + response.setSpecVersion(resultSet.getString(EventNotificationConstants.SPEC_VERSION)); + response.setStatus(resultSet.getString(EventNotificationConstants.STATUS)); + response.setRequestData(resultSet.getString(EventNotificationConstants.REQUEST)); + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/PostgreSqlEventNotificationDAOImpl.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/PostgreSqlEventNotificationDAOImpl.java new file mode 100644 index 00000000..8715f1c0 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/PostgreSqlEventNotificationDAOImpl.java @@ -0,0 +1,218 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.financial.services.accelerator.common.util.Generated; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.Notification; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationEvent; +import org.wso2.financial.services.accelerator.event.notifications.service.queries.EventNotificationSqlStatements; +import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * PostgreSql event polling dao class. + */ +@Generated(message = "Postgres Implementation") +public class PostgreSqlEventNotificationDAOImpl extends EventNotificationDAOImpl { + + private static Log log = LogFactory.getLog(PostgreSqlEventNotificationDAOImpl.class); + + public PostgreSqlEventNotificationDAOImpl(EventNotificationSqlStatements eventNotificationSqlStatements) { + super(eventNotificationSqlStatements); + } + + @Override + public List getNotificationsByClientIdAndStatus(Connection connection, String clientId, + String status, int max) + throws FSEventNotificationException { + + List notificationList = new ArrayList<>(); + String sql = sqlStatements.getMaxNotificationsQuery(); + + try (PreparedStatement getNotificationsPreparedStatement = connection.prepareStatement(sql, + ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + getNotificationsPreparedStatement.setString(1, clientId); + getNotificationsPreparedStatement.setString(2, status); + getNotificationsPreparedStatement.setInt(3, max); + + try (ResultSet notificationResultSet = getNotificationsPreparedStatement.executeQuery()) { + if (notificationResultSet.next()) { + + //bring pointer back to the top of the result set if not on the top + if (!notificationResultSet.isBeforeFirst()) { + notificationResultSet.beforeFirst(); + } + + //read event notifications from the result set + while (notificationResultSet.next()) { + Notification notification = new Notification(); + + notification.setNotificationId(notificationResultSet.getString + (EventNotificationConstants.NOTIFICATION_ID)); + notification.setClientId(notificationResultSet.getString + (EventNotificationConstants.CLIENT_ID)); + notification.setResourceId(notificationResultSet.getString + (EventNotificationConstants.RESOURCE_ID)); + notification.setStatus(notificationResultSet.getString + (EventNotificationConstants.STATUS)); + notification.setUpdatedTimeStamp((notificationResultSet.getTimestamp( + (EventNotificationConstants.UPDATED_TIMESTAMP)).getTime())); + + notificationList.add(notification); + } + notificationResultSet.close(); + getNotificationsPreparedStatement.close(); + + if (log.isDebugEnabled()) { + log.debug(String.format(EventNotificationConstants.RETRIEVED_NOTIFICATION_CLIENT, + clientId.replaceAll("[\r\n]", ""))); + } + + } else { + if (log.isDebugEnabled()) { + log.debug(String.format(EventNotificationConstants.NO_NOTIFICATIONS_FOUND_CLIENT, + clientId.replaceAll("[\r\n]", ""))); + } + } + } + } catch (SQLException e) { + throw new FSEventNotificationException(String.format + (EventNotificationConstants.DB_ERROR_NOTIFICATION_RETRIEVE, + clientId), e); + } + return notificationList; + } + + @Override + public List getEventsByNotificationID(Connection connection, String notificationId) + throws FSEventNotificationException { + + List eventList = new ArrayList<>(); + String sql = sqlStatements.getEventsByNotificationIdQuery(); + + try (PreparedStatement getEventsPreparedStatement = connection.prepareStatement(sql, + ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + + getEventsPreparedStatement.setString(1, notificationId); + + try (ResultSet eventsResultSet = getEventsPreparedStatement.executeQuery()) { + if (eventsResultSet.next()) { + + //bring pointer back to the top of the result set if not on the top + if (!eventsResultSet.isBeforeFirst()) { + eventsResultSet.beforeFirst(); + } + + //read event notifications from the result set + while (eventsResultSet.next()) { + NotificationEvent event = new NotificationEvent(); + event.setNotificationId(eventsResultSet.getString + (EventNotificationConstants.NOTIFICATION_ID)); + event.setEventType(eventsResultSet.getString + (EventNotificationConstants.EVENT_TYPE)); + event.setEventInformation(EventNotificationServiceUtil. + getEventJSONFromString(eventsResultSet.getString + (EventNotificationConstants.EVENT_INFO))); + eventList.add(event); + } + eventsResultSet.close(); + getEventsPreparedStatement.close(); + + if (log.isDebugEnabled()) { + log.debug(String.format(EventNotificationConstants.RETRIEVED_EVENTS_NOTIFICATION, + notificationId.replaceAll("[\r\n]", ""))); + } + } else { + if (log.isDebugEnabled()) { + log.debug(String.format(EventNotificationConstants.NO_EVENTS_NOTIFICATION_ID, + notificationId.replaceAll("[\r\n]", ""))); + } + } + } + } catch (SQLException e) { + log.error(String.format(EventNotificationConstants.DB_ERROR_EVENTS_RETRIEVE, + notificationId.replaceAll("[\r\n]", "")), e); + throw new FSEventNotificationException(String.format + (EventNotificationConstants.DB_ERROR_EVENTS_RETRIEVE, notificationId), e); + } + + return eventList; + } + + @Override + public List getNotificationsByStatus(Connection connection, String status) + throws FSEventNotificationException { + + List notificationList = new ArrayList<>(); + String sql = sqlStatements.getMaxNotificationsQuery(); + + try (PreparedStatement getNotificationsPreparedStatement = connection.prepareStatement(sql, + ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + getNotificationsPreparedStatement.setString(1, status); + try (ResultSet notificationResultSet = getNotificationsPreparedStatement.executeQuery()) { + if (notificationResultSet.next()) { + //bring pointer back to the top of the result set if not on the top + if (!notificationResultSet.isBeforeFirst()) { + notificationResultSet.beforeFirst(); + } + //read event notifications from the result set + while (notificationResultSet.next()) { + Notification notification = new Notification(); + notification.setNotificationId(notificationResultSet.getString + (EventNotificationConstants.NOTIFICATION_ID)); + notification.setClientId(notificationResultSet.getString + (EventNotificationConstants.CLIENT_ID)); + notification.setResourceId(notificationResultSet.getString + (EventNotificationConstants.RESOURCE_ID)); + notification.setStatus(notificationResultSet.getString + (EventNotificationConstants.STATUS)); + notification.setUpdatedTimeStamp((notificationResultSet.getTimestamp( + (EventNotificationConstants.UPDATED_TIMESTAMP)).getTime())); + notificationList.add(notification); + } + notificationResultSet.close(); + getNotificationsPreparedStatement.close(); + if (log.isDebugEnabled()) { + log.debug( + EventNotificationConstants.RETRIEVED_NOTIFICATION_CLIENT); + } + } else { + if (log.isDebugEnabled()) { + log.debug( + EventNotificationConstants.NO_NOTIFICATIONS_FOUND_CLIENT); + } + } + } + } catch (SQLException e) { + throw new FSEventNotificationException( + EventNotificationConstants.DB_ERROR_NOTIFICATION_RETRIEVE, e); + } + return notificationList; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/PostgreSqlEventSubscriptionDAOImpl.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/PostgreSqlEventSubscriptionDAOImpl.java new file mode 100644 index 00000000..25d1396d --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/PostgreSqlEventSubscriptionDAOImpl.java @@ -0,0 +1,198 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscription; +import org.wso2.financial.services.accelerator.event.notifications.service.queries.EventSubscriptionSqlStatements; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Postgres SQL EventSubscriptionDAO Impl. + */ +public class PostgreSqlEventSubscriptionDAOImpl extends EventSubscriptionDAOImpl { + + private static final Log log = LogFactory.getLog(PostgreSqlEventSubscriptionDAOImpl.class); + + public PostgreSqlEventSubscriptionDAOImpl(EventSubscriptionSqlStatements sqlStatements) { + super(sqlStatements); + } + + @Override + public EventSubscription storeEventSubscription(Connection connection, EventSubscription eventSubscription) + throws FSEventNotificationException { + + int storeSubscriptionAffectedRows; + + UUID subscriptionId = UUID.randomUUID(); + long unixTime = Instant.now().getEpochSecond(); + eventSubscription.setSubscriptionId(subscriptionId.toString()); + eventSubscription.setTimeStamp(unixTime); + eventSubscription.setStatus(EventNotificationConstants.CREATED); + + final String sql = sqlStatements.storeEventSubscriptionQuery(); + try (PreparedStatement storeEventSubscriptionStatement = connection.prepareStatement(sql)) { + storeEventSubscriptionStatement.setString(1, eventSubscription.getSubscriptionId()); + storeEventSubscriptionStatement.setString(2, eventSubscription.getClientId()); + storeEventSubscriptionStatement.setString(3, eventSubscription.getCallbackUrl()); + storeEventSubscriptionStatement.setLong(4, eventSubscription.getTimeStamp()); + storeEventSubscriptionStatement.setString(5, eventSubscription.getSpecVersion()); + storeEventSubscriptionStatement.setString(6, eventSubscription.getStatus()); + storeEventSubscriptionStatement.setObject(7, eventSubscription.getRequestData(), + java.sql.Types.OTHER); + storeSubscriptionAffectedRows = storeEventSubscriptionStatement.executeUpdate(); + if (storeSubscriptionAffectedRows == 0) { + log.error("Failed to store the event notification subscription."); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_STORING_EVENT_SUBSCRIPTION); + } + } catch (SQLException e) { + log.error("SQL exception when storing the event types of the subscription", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_STORING_EVENT_SUBSCRIPTION); + } + return eventSubscription; + } + + @Override + public List getEventSubscriptionsByClientId(Connection connection, String clientId) + throws FSEventNotificationException { + List retrievedSubscriptions = new ArrayList<>(); + + final String sql = sqlStatements.getEventSubscriptionsByClientIdQuery(); + try (PreparedStatement getEventSubscriptionsByClientIdStatement = connection.prepareStatement(sql, + ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + getEventSubscriptionsByClientIdStatement.setString(1, clientId); + try (ResultSet resultSet = getEventSubscriptionsByClientIdStatement.executeQuery()) { + if (resultSet.isBeforeFirst()) { + while (resultSet.next()) { + EventSubscription eventSubscription = new EventSubscription(); + List eventTypes = new ArrayList<>(); + mapResultSetToEventSubscription(eventSubscription, resultSet); + resultSet.previous(); + while (resultSet.next()) { + if (eventSubscription.getSubscriptionId().equals(resultSet. + getString(EventNotificationConstants.SUBSCRIPTION_ID))) { + if (resultSet.getString(EventNotificationConstants.EVENT_TYPE) != null) { + eventTypes.add(resultSet.getString(EventNotificationConstants.EVENT_TYPE)); + } + } else { + resultSet.previous(); + break; + } + } + if (!eventTypes.isEmpty()) { + eventSubscription.setEventTypes(eventTypes); + } + retrievedSubscriptions.add(eventSubscription); + } + log.debug("Retrieved the event notification subscriptions successfully."); + } + return retrievedSubscriptions; + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscriptions.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTION); + } + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscriptions.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTIONS); + } + } + + @Override + public EventSubscription getEventSubscriptionBySubscriptionId(Connection connection, String subscriptionId) + throws FSEventNotificationException { + EventSubscription retrievedSubscription = new EventSubscription(); + List eventTypes = new ArrayList<>(); + + final String sql = sqlStatements.getEventSubscriptionBySubscriptionIdQuery(); + try (PreparedStatement getEventSubscriptionBySubscriptionIdStatement = connection.prepareStatement(sql, + ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + getEventSubscriptionBySubscriptionIdStatement.setString(1, subscriptionId); + try (ResultSet resultSet = getEventSubscriptionBySubscriptionIdStatement.executeQuery()) { + if (resultSet.next()) { + mapResultSetToEventSubscription(retrievedSubscription, resultSet); + resultSet.beforeFirst(); // Reset the cursor position to the beginning of the result set. + while (resultSet.next()) { + String eventType = resultSet.getString(EventNotificationConstants.EVENT_TYPE); + if (eventType != null) { + eventTypes.add(eventType); + } + } + if (!eventTypes.isEmpty()) { + retrievedSubscription.setEventTypes(eventTypes); + } + } else { + log.error("No event notification subscription found for the given subscription id."); + throw new FSEventNotificationException( + EventNotificationConstants.EVENT_SUBSCRIPTION_NOT_FOUND); + } + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscription.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTION); + } + } catch (SQLException e) { + log.error("SQL exception when retrieving the event notification subscription.", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_RETRIEVING_EVENT_SUBSCRIPTION); + } + return retrievedSubscription; + } + + @Override + public Boolean updateEventSubscription(Connection connection, EventSubscription eventSubscription) + throws FSEventNotificationException { + boolean isUpdated = false; + final String sql = sqlStatements.updateEventSubscriptionQuery(); + try (PreparedStatement updateEventSubscriptionStatement = connection.prepareStatement(sql)) { + updateEventSubscriptionStatement.setString(1, eventSubscription.getCallbackUrl()); + updateEventSubscriptionStatement.setLong(2, Instant.now().getEpochSecond()); + updateEventSubscriptionStatement.setObject(3, eventSubscription.getRequestData(), + java.sql.Types.OTHER); + updateEventSubscriptionStatement.setString(4, eventSubscription.getSubscriptionId()); + int affectedRows = updateEventSubscriptionStatement.executeUpdate(); + if (affectedRows > 0) { + log.debug("Event notification subscription is successfully updated."); + isUpdated = true; + } + } catch (SQLException e) { + log.error("SQL exception when updating event notification subscription", e); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_UPDATING_EVENT_SUBSCRIPTION); + } + return isUpdated; + } + + private void mapResultSetToEventSubscription(EventSubscription response, ResultSet resultSet) throws SQLException { + response.setSubscriptionId(resultSet.getString(EventNotificationConstants.SUBSCRIPTION_ID)); + response.setClientId(resultSet.getString(EventNotificationConstants.CLIENT_ID)); + response.setCallbackUrl(resultSet.getString(EventNotificationConstants.CALLBACK_URL)); + response.setTimeStamp(resultSet.getLong(EventNotificationConstants.TIME_STAMP)); + response.setSpecVersion(resultSet.getString(EventNotificationConstants.SPEC_VERSION)); + response.setStatus(resultSet.getString(EventNotificationConstants.STATUS)); + response.setRequestData(resultSet.getString(EventNotificationConstants.REQUEST)); + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventNotificationErrorDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventNotificationErrorDTO.java new file mode 100644 index 00000000..280afad0 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventNotificationErrorDTO.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Error model for Event Notifications. + */ +public class EventNotificationErrorDTO { + + private String errorDescription; + private String error; + + @JsonProperty("error_description") + public String getErrorDescription() { + + return errorDescription; + } + + public void setErrorDescription(String errorDescription) { + + this.errorDescription = errorDescription; + } + + @JsonProperty("error") + public String getError() { + + return error; + } + + public void setError(String error) { + + this.error = error; + } + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventPollingDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventPollingDTO.java new file mode 100644 index 00000000..500e5fb9 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventPollingDTO.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dto; + +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationError; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Event Polling DTO. + */ +public class EventPollingDTO { + + //Set to true by default as WSO2 Financial Services don't support long polling + private final Boolean returnImmediately = true; + private String clientId = null; + private int maxEvents = 0; + private List ack = new ArrayList(); + private Map errors = new HashMap(); + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public Boolean getReturnImmediately() { + return returnImmediately; + } + + public int getMaxEvents() { + return maxEvents; + } + + public void setMaxEvents(int maxEvents) { + this.maxEvents = maxEvents; + } + + public List getAck() { + return ack; + } + + public void setAck(String ack) { + this.ack.add(ack); + } + + public Map getErrors() { + return errors; + } + + public void setErrors(String notificationId, NotificationError errorNotification) { + this.errors.put(notificationId, errorNotification); + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventSubscriptionDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventSubscriptionDTO.java new file mode 100644 index 00000000..64f896da --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventSubscriptionDTO.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dto; + +import org.json.JSONObject; + +/** + * Event Subscription DTO. + */ +public class EventSubscriptionDTO { + private String clientId = null; + private String subscriptionId = null; + private JSONObject requestData = null; + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getSubscriptionId() { + return subscriptionId; + } + + public void setSubscriptionId(String subscriptionId) { + this.subscriptionId = subscriptionId; + } + + public JSONObject getRequestData() { + return requestData; + } + + public void setRequestData(JSONObject requestData) { + this.requestData = requestData; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/NotificationCreationDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/NotificationCreationDTO.java new file mode 100644 index 00000000..f75c58ab --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/NotificationCreationDTO.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.dto; + +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; + +/** + * Event Creation DTO. + */ +public class NotificationCreationDTO { + + private Map events = new HashMap(); + private String clientId = null; + private String resourceId = null; + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public Map getEventPayload() { + return this.events; + } + + public void setEventPayload(String notificationType, JSONObject notificationInfo) { + this.events.put(notificationType, notificationInfo); + } +} + diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/exception/FSEventNotificationException.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/exception/FSEventNotificationException.java new file mode 100644 index 00000000..ffb4ce62 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/exception/FSEventNotificationException.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.exception; + +import org.wso2.financial.services.accelerator.common.exception.FinancialServicesException; + +/** + * Event Notification Exceptions. + */ +public class FSEventNotificationException extends FinancialServicesException { + + public FSEventNotificationException(String message) { + super(message); + } + + public FSEventNotificationException(String message, Throwable e) { + super(message, e); + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java new file mode 100644 index 00000000..67fa8039 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.handler; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONObject; +import org.wso2.financial.services.accelerator.common.exception.ConsentManagementException; +import org.wso2.financial.services.accelerator.consent.mgt.dao.models.ConsentResource; +import org.wso2.financial.services.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.dto.NotificationCreationDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventCreationResponse; +import org.wso2.financial.services.accelerator.event.notifications.service.service.EventCreationService; +import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; + +/** + * This is to handle FS Event Creation. + */ +public class DefaultEventCreationServiceHandler implements EventCreationServiceHandler { + + private static final Log log = LogFactory.getLog(DefaultEventCreationServiceHandler.class); + private EventCreationService eventCreationService = new EventCreationService(); + + public void setEventCreationService(EventCreationService eventCreationService) { + this.eventCreationService = eventCreationService; + } + + /** + * This method is used to publish FS events in the accelerator database. + * + * @param notificationCreationDTO Notification details DTO + * @return EventCreationResponse Response after event creation + */ + public EventCreationResponse publishEvent(NotificationCreationDTO notificationCreationDTO) { + + //validate if the resourceID is existing + ConsentResource consentResource = null; + ConsentCoreServiceImpl consentCoreService = EventNotificationServiceUtil.getConsentCoreServiceImpl(); + EventCreationResponse eventCreationResponse = new EventCreationResponse(); + + try { + consentResource = consentCoreService.getConsent(notificationCreationDTO.getResourceId(), + false); + + if (log.isDebugEnabled()) { + log.debug("Consent resource available for resource ID " + + consentResource.getConsentID().replaceAll("[\r\n]", "")); + } + } catch (ConsentManagementException e) { + log.error("Consent Management Exception when validating the consent resource", e); + eventCreationResponse.setErrorResponse(String.format("A resource was not found for the resource " + + "id : '%s' in the database. ", notificationCreationDTO.getResourceId())); + eventCreationResponse.setStatus(EventNotificationConstants.BAD_REQUEST); + return eventCreationResponse; + } + + //validate if the clientID is existing + try { + EventNotificationServiceUtil.validateClientId(notificationCreationDTO.getClientId()); + + } catch (FSEventNotificationException e) { + log.error("Invalid client ID", e); + eventCreationResponse.setErrorResponse(String.format("A client was not found" + + " for the client id : '%s' in the database. ", + notificationCreationDTO.getClientId().replaceAll("[\r\n]", ""))); + eventCreationResponse.setStatus(EventNotificationConstants.BAD_REQUEST); + return eventCreationResponse; + } + + String registrationResponse = ""; + try { + registrationResponse = eventCreationService.publishEventNotification(notificationCreationDTO); + JSONObject responseJSON = new JSONObject(); + responseJSON.put(EventNotificationConstants.NOTIFICATIONS_ID, registrationResponse); + eventCreationResponse.setStatus(EventNotificationConstants.CREATED); + eventCreationResponse.setResponseBody(responseJSON); + return eventCreationResponse; + + } catch (FSEventNotificationException e) { + log.error("FS Event Notification Creation error", e); + } + + eventCreationResponse.setStatus(EventNotificationConstants.BAD_REQUEST); + eventCreationResponse.setErrorResponse("Error in event creation request payload"); + return eventCreationResponse; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java new file mode 100644 index 00000000..b5930e69 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.handler; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONObject; +import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; +import org.wso2.financial.services.accelerator.common.util.Generated; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventPollingDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.AggregatedPollingResponse; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPolling; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPollingResponse; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationError; +import org.wso2.financial.services.accelerator.event.notifications.service.service.EventPollingService; +import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; + +import java.util.Locale; + +/** + * This is the service handler for event polling. + */ +public class DefaultEventPollingServiceHandler implements EventPollingServiceHandler { + + private static final Log log = LogFactory.getLog(DefaultEventPollingServiceHandler.class); + + public void setEventPollingService(EventPollingService eventPollingService) { + this.eventPollingService = eventPollingService; + } + + private EventPollingService eventPollingService = new EventPollingService(); + + + /** + * This method is used to Poll Events as per request params. + * @param eventPollingDTO Event polling DTO + * @return EventPollingResponse + */ + public EventPollingResponse pollEvents(EventPollingDTO eventPollingDTO) { + + EventPollingResponse eventPollingResponse = new EventPollingResponse(); + + //Validate clientID of the polling request + try { + EventNotificationServiceUtil.validateClientId(eventPollingDTO.getClientId()); + } catch (FSEventNotificationException e) { + log.error("Invalid client ID", e); + eventPollingResponse.setStatus(EventNotificationConstants.BAD_REQUEST); + eventPollingResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, String.format("A client was not found" + + " for the client id : '%s' in the database.. ", eventPollingDTO.getClientId()))); + return eventPollingResponse; + } + + EventPolling eventPolling = mapEventPollingDtoToModel(eventPollingDTO); + //Poll events + try { + AggregatedPollingResponse aggregatedPollingResponse = eventPollingService.pollEvents(eventPolling); + eventPollingResponse.setStatus(aggregatedPollingResponse.getStatus()); + eventPollingResponse.setResponseBody(getPollingResponseJSON(aggregatedPollingResponse)); + return eventPollingResponse; + } catch (FSEventNotificationException e) { + log.error("OB Event Notification error" , e); + eventPollingResponse.setStatus(EventNotificationConstants.BAD_REQUEST); + eventPollingResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + return eventPollingResponse; + } + + } + + /** + * This method will map the event subscription DTO to event subscription model + * to be passed to the dao layer. + * + * @param eventPollingDTO Event polling DTO + * @return EventPolling Event polling Model mapped + */ + private EventPolling mapEventPollingDtoToModel(EventPollingDTO eventPollingDTO) { + + EventPolling eventPolling = new EventPolling(); + eventPolling.setClientId(eventPollingDTO.getClientId()); + eventPolling.setMaxEvents(eventPollingDTO.getMaxEvents()); + eventPolling.setReturnImmediately(eventPollingDTO.getReturnImmediately()); + eventPolling.setSetsToReturn(FinancialServicesConfigParser.getInstance().getNumberOfSetsToReturn()); + eventPollingDTO.getAck().forEach(eventPolling::setAck); + eventPollingDTO.getErrors().forEach(eventPolling::setErrors); + + return eventPolling; + } + + /** + * This method will map the eventPollingRequest JSON to EventPollingDTO. + * @param eventPollingRequest JSON request for event polling + * @return EventPollingDTO + */ + public EventPollingDTO mapPollingRequest(JSONObject eventPollingRequest) { + + EventPollingDTO eventPollingDTO = new EventPollingDTO(); + eventPollingDTO.setClientId(eventPollingRequest.get(EventNotificationConstants.X_WSO2_CLIENT_ID).toString()); + + if (eventPollingRequest.length() == 0) { + + eventPollingDTO.setMaxEvents(FinancialServicesConfigParser.getInstance().getNumberOfSetsToReturn()); + + return eventPollingDTO; + } + + //Set acknowledged events to DTO + if (eventPollingRequest.has(EventNotificationConstants.ACK.toLowerCase(Locale.ROOT))) { + JSONArray acknowledgedEvents = (JSONArray) eventPollingRequest. + get(EventNotificationConstants.ACK.toLowerCase(Locale.ROOT)); + acknowledgedEvents.forEach((event -> { + eventPollingDTO.setAck(event.toString()); + })); + } + + //Set error events to DTO + if (eventPollingRequest.has(EventNotificationConstants.SET_ERRORS)) { + JSONObject errorEvents = (JSONObject) eventPollingRequest. + get(EventNotificationConstants.SET_ERRORS); + errorEvents.keySet().forEach(errorEvent -> { + JSONObject errorEventInformation = (JSONObject) errorEvents.get(errorEvent); + NotificationError notificationError = getNotificationError(errorEventInformation); + notificationError.setNotificationId(errorEvent); + eventPollingDTO.setErrors(errorEvent, notificationError); + }); + } + + //Set maxEvents count to return + if (eventPollingRequest.has(EventNotificationConstants.MAX_EVENTS)) { + eventPollingDTO.setMaxEvents(Integer.parseInt(eventPollingRequest. + get(EventNotificationConstants.MAX_EVENTS).toString())); + } else { + eventPollingDTO.setMaxEvents(FinancialServicesConfigParser.getInstance().getNumberOfSetsToReturn()); + } + + return eventPollingDTO; + } + + @Generated(message = "Private method tested when testing the invoked method") + private NotificationError getNotificationError(JSONObject errorEvent) { + + NotificationError notificationError = new NotificationError(); + notificationError.setErrorCode(errorEvent.get( + EventNotificationConstants.ERROR.toLowerCase(Locale.ROOT)).toString()); + notificationError.setErrorDescription( + errorEvent.get(EventNotificationConstants.DESCRIPTION).toString()); + return notificationError; + } + + @Generated(message = "Private method tested when testing the invoked method") + private JSONObject getPollingResponseJSON(AggregatedPollingResponse aggregatedPollingResponse) { + + JSONObject responseJSON = new JSONObject(); + responseJSON.put(EventNotificationConstants.SETS, aggregatedPollingResponse.getSets()); + responseJSON.put(EventNotificationConstants.MORE_AVAILABLE, + aggregatedPollingResponse.isMoreAvailable()); + return responseJSON; + } + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java new file mode 100644 index 00000000..cba1d7b6 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java @@ -0,0 +1,345 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.handler; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpStatus; +import org.json.JSONObject; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventSubscriptionDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscription; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscriptionResponse; +import org.wso2.financial.services.accelerator.event.notifications.service.service.EventSubscriptionService; +import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * This is the default service handler for event notification subscription. + */ +public class DefaultEventSubscriptionServiceHandler implements EventSubscriptionServiceHandler { + private static final Log log = LogFactory.getLog(DefaultEventSubscriptionServiceHandler.class); + + private EventSubscriptionService eventSubscriptionService = new EventSubscriptionService(); + + public void setEventSubscriptionService(EventSubscriptionService eventSubscriptionService) { + this.eventSubscriptionService = eventSubscriptionService; + } + + /** + * This method is used to create event subscriptions. + * + * @param eventSubscriptionRequestDto Event Subscription DTO + * @return EventSubscriptionResponse Event Subscription Response + */ + public EventSubscriptionResponse createEventSubscription(EventSubscriptionDTO eventSubscriptionRequestDto) { + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + + EventSubscriptionResponse clientIdValidation = validateClientId(eventSubscriptionRequestDto.getClientId()); + // check whether clientIdValidation is not null, then return the error response + if (clientIdValidation != null) { + return clientIdValidation; + } + + EventSubscription eventSubscription = mapEventSubscriptionDtoToModel(eventSubscriptionRequestDto); + + try { + EventSubscription createEventSubscriptionResponse = eventSubscriptionService. + createEventSubscription(eventSubscription); + eventSubscriptionResponse.setStatus(HttpStatus.SC_CREATED); + eventSubscriptionResponse. + setResponseBody(mapSubscriptionModelToResponseJson(createEventSubscriptionResponse)); + return eventSubscriptionResponse; + } catch (FSEventNotificationException e) { + log.error("Error occurred while creating event subscription", e); + eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + return eventSubscriptionResponse; + } + + } + + /** + * This method is used to retrieve a single event subscription. + * + * @param clientId Client ID of the subscription created + * @param subscriptionId Subscription ID of the subscription created + * @return EventSubscriptionResponse Event Subscription Response containing subscription + * details for the given subscription ID + */ + public EventSubscriptionResponse getEventSubscription(String clientId, String subscriptionId) { + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + + EventSubscriptionResponse clientIdValidation = validateClientId(clientId); + // check whether clientIdValidation is not null, then return the error response + if (clientIdValidation != null) { + return clientIdValidation; + } + + try { + EventSubscription eventSubscription = eventSubscriptionService. + getEventSubscriptionBySubscriptionId(subscriptionId); + eventSubscriptionResponse.setStatus(HttpStatus.SC_OK); + eventSubscriptionResponse.setResponseBody(mapSubscriptionModelToResponseJson(eventSubscription)); + return eventSubscriptionResponse; + } catch (FSEventNotificationException e) { + log.error("Error occurred while retrieving event subscription", e); + if (e.getMessage().equals(EventNotificationConstants.EVENT_SUBSCRIPTION_NOT_FOUND)) { + eventSubscriptionResponse.setStatus(HttpStatus.SC_BAD_REQUEST); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + } else { + eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + } + return eventSubscriptionResponse; + } + } + + /** + * This method is used to retrieve all event subscriptions of a client. + * + * @param clientId Client ID + * @return EventSubscriptionResponse Event Subscription Response containing all the subscriptions + */ + public EventSubscriptionResponse getAllEventSubscriptions(String clientId) { + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + + EventSubscriptionResponse clientIdValidation = validateClientId(clientId); + // check whether clientIdValidation is not null, then return the error response + if (clientIdValidation != null) { + return clientIdValidation; + } + + try { + List eventSubscriptionList = eventSubscriptionService. + getEventSubscriptionsByClientId(clientId); + List eventSubscriptionResponseList = new ArrayList<>(); + for (EventSubscription eventSubscription : eventSubscriptionList) { + eventSubscriptionResponseList.add(mapSubscriptionModelToResponseJson(eventSubscription)); + } + eventSubscriptionResponse.setStatus(HttpStatus.SC_OK); + eventSubscriptionResponse.setResponseBody(eventSubscriptionResponseList); + return eventSubscriptionResponse; + } catch (FSEventNotificationException e) { + log.error("Error occurred while retrieving event subscriptions", e); + eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + return eventSubscriptionResponse; + } + } + + /** + * This method is used to retrieve all event subscriptions by event type. + * + * @param clientId Client ID + * @param eventType Event Type to retrieve subscriptions + * @return EventSubscriptionResponse Event Subscription Response containing subscriptions per specified + * event type + */ + public EventSubscriptionResponse getEventSubscriptionsByEventType(String clientId, String eventType) { + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + + EventSubscriptionResponse clientIdValidation = validateClientId(clientId); + // check whether clientIdValidation is not null, then return the error response + if (clientIdValidation != null) { + return clientIdValidation; + } + + try { + List eventSubscriptionList = eventSubscriptionService. + getEventSubscriptionsByEventType(eventType); + List eventSubscriptionResponseList = new ArrayList<>(); + for (EventSubscription eventSubscription : eventSubscriptionList) { + eventSubscriptionResponseList.add(mapSubscriptionModelToResponseJson(eventSubscription)); + } + eventSubscriptionResponse.setStatus(HttpStatus.SC_OK); + eventSubscriptionResponse.setResponseBody(eventSubscriptionResponseList); + return eventSubscriptionResponse; + } catch (FSEventNotificationException e) { + log.error("Error occurred while retrieving event subscriptions", e); + eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + return eventSubscriptionResponse; + } + } + + /** + * This method is used to update an event subscription. + * + * @param eventSubscriptionUpdateRequestDto Event Subscription Update Request DTO + * @return EventSubscriptionResponse Event Subscription Response containing the updated subscription + */ + public EventSubscriptionResponse updateEventSubscription(EventSubscriptionDTO eventSubscriptionUpdateRequestDto) { + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + + EventSubscriptionResponse clientIdValidation = validateClientId(eventSubscriptionUpdateRequestDto. + getClientId()); + // check whether clientIdValidation is not null, then return the error response + if (clientIdValidation != null) { + return clientIdValidation; + } + + EventSubscription eventSubscription = mapEventSubscriptionDtoToModel(eventSubscriptionUpdateRequestDto); + + try { + Boolean isUpdated = eventSubscriptionService.updateEventSubscription(eventSubscription); + if (!isUpdated) { + eventSubscriptionResponse.setStatus(HttpStatus.SC_BAD_REQUEST); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, + "Event subscription not found.")); + return eventSubscriptionResponse; + } + eventSubscriptionResponse.setStatus(HttpStatus.SC_OK); + EventSubscription eventSubscriptionUpdateResponse = eventSubscriptionService. + getEventSubscriptionBySubscriptionId(eventSubscriptionUpdateRequestDto.getSubscriptionId()); + eventSubscriptionResponse. + setResponseBody(mapSubscriptionModelToResponseJson(eventSubscriptionUpdateResponse)); + return eventSubscriptionResponse; + } catch (FSEventNotificationException e) { + log.error("Error occurred while updating event subscription", e); + eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + return eventSubscriptionResponse; + } + } + + /** + * This method is used to delete an event subscription. + * + * @param clientId Client ID + * @param subscriptionId Subscription ID to be deleted + * @return EventSubscriptionResponse Event Subscription Response containing the deleted subscription + */ + public EventSubscriptionResponse deleteEventSubscription(String clientId, String subscriptionId) { + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + + EventSubscriptionResponse clientIdValidation = validateClientId(clientId); + // check whether clientIdValidation is not null, then return the error response + if (clientIdValidation != null) { + return clientIdValidation; + } + try { + Boolean isDeleted = eventSubscriptionService.deleteEventSubscription(subscriptionId); + if (!isDeleted) { + eventSubscriptionResponse.setStatus(HttpStatus.SC_BAD_REQUEST); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, + "Event subscription not found")); + return eventSubscriptionResponse; + } + eventSubscriptionResponse.setStatus(HttpStatus.SC_NO_CONTENT); + return eventSubscriptionResponse; + } catch (FSEventNotificationException e) { + log.error("Error occurred while deleting event subscription", e); + eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + return eventSubscriptionResponse; + } + } + + /** + * This method is used to validate the client ID. + * + * @param clientId Client ID + * @return EventSubscriptionResponse Return EventSubscriptionResponse if the client ID is + * invalid, if the client ID is valid, null will be returned. + */ + private EventSubscriptionResponse validateClientId(String clientId) { + try { + EventNotificationServiceUtil.validateClientId(clientId); + } catch (FSEventNotificationException e) { + log.error("Invalid client ID", e); + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + eventSubscriptionResponse.setStatus(HttpStatus.SC_BAD_REQUEST); + eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( + EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + return eventSubscriptionResponse; + } + return null; + } + + /** + * This method will map the event subscription DTO to event subscription model + * to be passed to the dao layer. + * + * @param eventSubscriptionDTO Event Subscription DTO + * @return EventSubscription Event Subscription Model mapped + */ + private EventSubscription mapEventSubscriptionDtoToModel(EventSubscriptionDTO eventSubscriptionDTO) { + EventSubscription eventSubscription = new EventSubscription(); + + eventSubscription.setSubscriptionId(eventSubscriptionDTO.getSubscriptionId()); + + JSONObject payload = eventSubscriptionDTO.getRequestData(); + List eventTypes = new ArrayList<>(); + Object eventTypesObj = payload.get(EventNotificationConstants.EVENT_TYPE_PARAM); + if (eventTypesObj instanceof List) { + List eventTypesList = (List) eventTypesObj; + for (Object item : eventTypesList) { + if (item instanceof String) { + eventTypes.add((String) item); + } + } + } + eventSubscription.setEventTypes(eventTypes); + eventSubscription.setCallbackUrl(payload.get(EventNotificationConstants.CALLBACK_URL_PARAM) != null ? + payload.get(EventNotificationConstants.CALLBACK_URL_PARAM).toString() : null); + eventSubscription.setSpecVersion(payload.get(EventNotificationConstants.VERSION_PARAM) != null ? + payload.get(EventNotificationConstants.VERSION_PARAM).toString() : null); + eventSubscription.setClientId(eventSubscriptionDTO.getClientId()); + eventSubscription.setRequestData(payload.toString()); + return eventSubscription; + } + + /** + * This method is used to create the response JSON object from the event subscription model. + * + * @param eventSubscription Event Subscription Model + * @return JSONObject containing mapped subscription + */ + public JSONObject mapSubscriptionModelToResponseJson(EventSubscription eventSubscription) { + JSONObject responsePayload = new JSONObject(); + + if (eventSubscription.getSubscriptionId() != null) { + responsePayload.put(EventNotificationConstants.SUBSCRIPTION_ID_PARAM, + eventSubscription.getSubscriptionId()); + } + if (eventSubscription.getCallbackUrl() != null) { + responsePayload.put(EventNotificationConstants.CALLBACK_URL_PARAM, eventSubscription.getCallbackUrl()); + } + if (eventSubscription.getSpecVersion() != null) { + responsePayload.put(EventNotificationConstants.VERSION_PARAM, eventSubscription.getSpecVersion()); + } + if (eventSubscription.getEventTypes() != null) { + responsePayload.put(EventNotificationConstants.EVENT_TYPE_PARAM, eventSubscription.getEventTypes()); + } + return responsePayload; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventCreationServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventCreationServiceHandler.java new file mode 100644 index 00000000..85a861a7 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventCreationServiceHandler.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.handler; + +import org.wso2.financial.services.accelerator.event.notifications.service.dto.NotificationCreationDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventCreationResponse; + +/** + * Event creation service handler is used to map the creation request and validate the date before + * calling the service. In need of a custom handling this class can be extended and the extended class + * can be added to the deployment.toml under event_creation_handler to execute the specific class. + */ +public interface EventCreationServiceHandler { + /** + * This method is used to publish FS events in the accelerator database. The method is a generic + * method that is used to persist data into the FS_NOTIFICATION and FS_NOTIFICATION_EVENT tables. + * @param notificationCreationDTO Notification details DTO + * @return For successful request the API will return a JSON with the notificationID + */ + EventCreationResponse publishEvent(NotificationCreationDTO notificationCreationDTO); + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventPollingServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventPollingServiceHandler.java new file mode 100644 index 00000000..2b5fc735 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventPollingServiceHandler.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.handler; + +import org.json.JSONObject; +import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventPollingDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPollingResponse; + +/** + * EventPolling Service handler is used to validate and map the polling request to the DTO before calling the + * polling service. For custom validations this class can be extended and the extended class + * can be added to the deployment.toml under event_polling_handler to execute the specific class. + */ +public interface EventPollingServiceHandler { + /** + * This method follows the IETF Specification for SET delivery over HTTP. + * The method supports event acknowledgment in both positive and negative. + * Also, can be used to POLL for available OPEN notifications. + * @param eventPollingDTO Event polling DTO + * @return EventPollingResponse to the polling endpoint. + */ + EventPollingResponse pollEvents(EventPollingDTO eventPollingDTO); + + /** + * This method is used to map the eventPollingRequest to EventPollingDTO. + * @param eventPollingRequest JSON request for event polling + * @return eventPollingDTO with the request parameters. + */ + EventPollingDTO mapPollingRequest(JSONObject eventPollingRequest); + +} + diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventSubscriptionServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventSubscriptionServiceHandler.java new file mode 100644 index 00000000..d755bd14 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventSubscriptionServiceHandler.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.handler; + +import org.json.JSONObject; +import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventSubscriptionDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscription; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscriptionResponse; + +/** + * EventSubscription Service handler is used to validate subscription requests before calling the + * subscription service. For custom validations this class can be extended and the extended class + * can be added to the deployment.toml under event_subscription_handler to execute the specific class. + */ +public interface EventSubscriptionServiceHandler { + + /** + * This method is used to create event subscriptions in the accelerator database. The method is a generic + * method that is used to persist data into the NOTIFICATION_SUBSCRIPTION and NOTIFICATION_SUBSCRIPTION_EVENT + * tables. + * + * @param eventSubscriptionRequestDto The request DTO that contains the subscription details. + * @return For successful request the API will return a JSON with the subscriptionId + */ + EventSubscriptionResponse createEventSubscription(EventSubscriptionDTO eventSubscriptionRequestDto); + + /** + * This method is used to retrieve an event subscription by its subscription ID. + * + * @param clientId The client ID of the subscription. + * @param subscriptionId The subscription ID of the subscription. + * @return For successful request the API will return a JSON with the retrieved Subscription. + */ + EventSubscriptionResponse getEventSubscription(String clientId, String subscriptionId); + + /** + * This method is used to retrieve all event subscriptions of a client. + * + * @param clientId The client ID of the subscription. + * @return For successful request the API will return a JSON with the retrieved Subscriptions. + */ + EventSubscriptionResponse getAllEventSubscriptions(String clientId); + + /** + * This method is used to retrieve all event subscriptions by event type. + * + * @param clientId The client ID of the subscription. + * @param eventType The event type that needs to be subscribed by the retrieving subscriptions. + * @return For successful request the API will return a JSON with the retrieved Subscriptions. + */ + EventSubscriptionResponse getEventSubscriptionsByEventType(String clientId, String eventType); + + /** + * This method is used to update an event subscription. + * + * @param eventSubscriptionUpdateRequestDto The request DTO that contains the updating subscription details. + * @return For successful request the API will return a JSON with the updated Subscription. + */ + EventSubscriptionResponse updateEventSubscription(EventSubscriptionDTO eventSubscriptionUpdateRequestDto); + + /** + * This method is used to delete an event subscription. + * + * @param clientId The client ID of the subscription. + * @param subscriptionId The subscription ID of the subscription. + * @return For successful request the API will an OK response. + */ + EventSubscriptionResponse deleteEventSubscription(String clientId, String subscriptionId); + + /** + * This method is used to create the response JSON object from the event subscription model. + * + * @param eventSubscription The event subscription model. + * @return JSONObject + */ + JSONObject mapSubscriptionModelToResponseJson(EventSubscription eventSubscription); + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationComponent.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationComponent.java new file mode 100644 index 00000000..547d5f15 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationComponent.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.internal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.wso2.carbon.identity.oauth2.OAuth2Service; +import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; +import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigurationService; + +/** + * The Component class for activating event notification osgi service. + */ +@Component( + name = "org.wso2.financial.services.accelerator.event.notifications.service.internal.EventNotificationComponent", + immediate = true) +public class EventNotificationComponent { + private static Log log = LogFactory.getLog(EventNotificationComponent.class); + + @Activate + protected void activate(ComponentContext context) { + if (log.isDebugEnabled()) { + log.debug("Event Notification Service Component Activated"); + } + + // Check if realtime event notification enabled + if (FinancialServicesConfigParser.getInstance().isRealtimeEventNotificationEnabled()) { + /* + * Initialize the blocking queue for storing the realtime event notifications + * Initialize the quartz job for consuming the realtime event notifications + * Initialize the thread for producing the open state realtime event notifications + */ + //TODO: +// new Thread(new RealtimeEventNotificationLoaderService()).start(); +// new PeriodicalEventNotificationConsumerJobActivator().activate(); + } + } + + /** + * Setters for the descendent OSGI services of the EventNotificationComponent. + * This is added to run the EventNotification OSGI component after the Common module + * @param configService OpenBankingConfigurationService + */ + @Reference( + service = FinancialServicesConfigurationService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetConfigService" + ) + public void setConfigService(FinancialServicesConfigurationService configService) { + EventNotificationDataHolder.getInstance().setFinancialServicesConfigurationService(configService); + } + + public void unsetConfigService(FinancialServicesConfigurationService configService) { + EventNotificationDataHolder.getInstance().setFinancialServicesConfigurationService(null); + } + + /** + * Setters for the descendent OSGI services of the EventNotificationComponent. + * This is added to run the EventNotification OSGI component after the OAuth2Service + */ + @Reference( + service = OAuth2Service.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetOAuth2Service" + ) + + /** + * Setters for the descendent OSGI services of the EventNotificationComponent. + * @param oAuth2Service OAuth2Service + */ + public void setOAuth2Service(OAuth2Service oAuth2Service) { + } + + public void unsetOAuth2Service(OAuth2Service oAuth2Service) { + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationDataHolder.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationDataHolder.java new file mode 100644 index 00000000..d16bb2f7 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationDataHolder.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.internal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigurationService; + +/** + * Data holder for Open Banking Event Notifications. + */ +public class EventNotificationDataHolder { + private static Log log = LogFactory.getLog(EventNotificationDataHolder.class); + private static volatile EventNotificationDataHolder instance; +// private volatile LinkedBlockingQueue realtimeEventNotificationQueue; + private FinancialServicesConfigurationService configService; + + private EventNotificationDataHolder() { + //TODO +// this.realtimeEventNotificationQueue = new LinkedBlockingQueue<>(); + } + + /** + * Return a singleton instance of the data holder. + * + * @return A singleton instance of the data holder + */ + public static synchronized EventNotificationDataHolder getInstance() { + if (instance == null) { + synchronized (EventNotificationDataHolder.class) { + if (instance == null) { + instance = new EventNotificationDataHolder(); + } + } + } + return instance; + } + +// public LinkedBlockingQueue getRealtimeEventNotificationQueue() { +// return realtimeEventNotificationQueue; +// } + + public FinancialServicesConfigurationService getFinancialServicesConfigurationService() { + + return configService; + } + + public void setFinancialServicesConfigurationService( + FinancialServicesConfigurationService configService) { + + this.configService = configService; + } + +// public void setRealtimeEventNotificationQueue(LinkedBlockingQueue queue) { +// this.realtimeEventNotificationQueue = queue; +// } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/AggregatedPollingResponse.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/AggregatedPollingResponse.java new file mode 100644 index 00000000..cc1d89d4 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/AggregatedPollingResponse.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +import java.util.HashMap; +import java.util.Map; + +/** + * Default Polling Response Implementation. + */ +public class AggregatedPollingResponse { + + private Map sets = new HashMap<>(); + + //For more available parameter + private int count = 0; + + private String status; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Map getSets() { + return sets; + } + + public void setSets(Map sets) { + this.sets = sets; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public Boolean isMoreAvailable() { + return count > 0; + } + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventCreationResponse.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventCreationResponse.java new file mode 100644 index 00000000..394b3726 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventCreationResponse.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +import org.json.JSONObject; + +/** + * This class is to pass the event creation response to the api endpoint. + */ +public class EventCreationResponse { + + private String status; + private JSONObject responseBody; + private String errorResponse; + + public String getErrorResponse() { + return errorResponse; + } + + public void setErrorResponse(String errorResponse) { + this.errorResponse = errorResponse; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public JSONObject getResponseBody() { + return responseBody; + } + + public void setResponseBody(JSONObject responseBody) { + this.responseBody = responseBody; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPolling.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPolling.java new file mode 100644 index 00000000..7b415809 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPolling.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Event Polling model. + */ +public class EventPolling { + + private Boolean returnImmediately = true; + private String clientId = null; + private int maxEvents = 0; + private int setsToReturn = 0; + private List ack = new ArrayList(); + private Map errors = new HashMap(); + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public Boolean getReturnImmediately() { + return returnImmediately; + } + + public void setReturnImmediately(Boolean returnImmediately) { + this.returnImmediately = returnImmediately; + } + + public int getMaxEvents() { + return maxEvents; + } + + public void setMaxEvents(int maxEvents) { + this.maxEvents = maxEvents; + } + + public int getSetsToReturn() { + return setsToReturn; + } + + public void setSetsToReturn(int setsToReturn) { + this.setsToReturn = setsToReturn; + } + + public List getAck() { + return ack; + } + + + public void setAck(String ack) { + this.ack.add(ack); + } + + public Map getErrors() { + return errors; + } + + public void setErrors(String notificationId, NotificationError errorNotification) { + this.errors.put(notificationId, errorNotification); + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPollingResponse.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPollingResponse.java new file mode 100644 index 00000000..fdf494b2 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPollingResponse.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +import org.json.JSONObject; + +/** + * This class is used to map the Event Polling service response to the API response. + */ +public class EventPollingResponse { + + private String status; + private JSONObject responseBody; + private Object errorResponse; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public JSONObject getResponseBody() { + return responseBody; + } + + public void setResponseBody(JSONObject responseBody) { + this.responseBody = responseBody; + } + + public Object getErrorResponse() { + return errorResponse; + } + + public void setErrorResponse(Object errorResponse) { + this.errorResponse = errorResponse; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscription.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscription.java new file mode 100644 index 00000000..9776d179 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscription.java @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +import java.util.List; + +/** + * This is the Event Subscription Model. + */ +public class EventSubscription { + private String subscriptionId = null; + private String clientId = null; + private String callbackUrl = null; + private Long timeStamp = null; + private String specVersion = null; + private String status = null; + private List eventTypes = null; + private String requestData = null; + + public String getSubscriptionId() { + return subscriptionId; + } + + public void setSubscriptionId(String subscriptionId) { + this.subscriptionId = subscriptionId; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getCallbackUrl() { + return callbackUrl; + } + + public void setCallbackUrl(String callbackUrl) { + this.callbackUrl = callbackUrl; + } + + public Long getTimeStamp() { + return timeStamp; + } + + public void setTimeStamp(Long timeStamp) { + this.timeStamp = timeStamp; + } + + public String getSpecVersion() { + return specVersion; + } + + public void setSpecVersion(String specVersion) { + this.specVersion = specVersion; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public List getEventTypes() { + return eventTypes; + } + + public void setEventTypes(List eventTypes) { + this.eventTypes = eventTypes; + } + + public String getRequestData() { + return requestData; + } + + public void setRequestData(String requestData) { + this.requestData = requestData; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscriptionResponse.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscriptionResponse.java new file mode 100644 index 00000000..1809db08 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscriptionResponse.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +/** + * This class is used to map the Event Subscription service response to the API response. + */ +public class EventSubscriptionResponse { + + private int status; + private Object responseBody; + private Object errorResponse; + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public Object getResponseBody() { + return responseBody; + } + + public void setResponseBody(Object responseBody) { + this.responseBody = responseBody; + } + + public Object getErrorResponse() { + return errorResponse; + } + + public void setErrorResponse(Object errorResponse) { + this.errorResponse = errorResponse; + } + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/Notification.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/Notification.java new file mode 100644 index 00000000..0c8d5157 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/Notification.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +/** + * This is the notification model. + */ +public class Notification { + + String notificationId = null; + String clientId = null; + String resourceId = null; + String status = null; + Long updatedTimeStamp = null; + + public String getNotificationId() { + return notificationId; + } + + public void setNotificationId(String notificationId) { + this.notificationId = notificationId; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Long getUpdatedTimeStamp() { + return updatedTimeStamp; + } + + public void setUpdatedTimeStamp(Long updatedTimeStamp) { + this.updatedTimeStamp = updatedTimeStamp; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationError.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationError.java new file mode 100644 index 00000000..0a9a8058 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationError.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +/** + * The notification error model. + */ +public class NotificationError { + private String notificationId = null; + private String errorCode = null; + private String errorDescription = null; + + public String getNotificationId() { + return notificationId; + } + + public void setNotificationId(String notificationId) { + this.notificationId = notificationId; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorDescription() { + return errorDescription; + } + + public void setErrorDescription(String errorDescription) { + this.errorDescription = errorDescription; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationEvent.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationEvent.java new file mode 100644 index 00000000..09713cec --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationEvent.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +import org.json.JSONObject; + +/** + * This is the notification event model class. + */ +public class NotificationEvent { + + private Integer eventId = null; + private String notificationId = null; + private String eventType = null; + private JSONObject eventInformation; + + public Integer getEventId() { + return eventId; + } + + public void setEventId(Integer eventId) { + this.eventId = eventId; + } + + public String getNotificationId() { + return notificationId; + } + + public void setNotificationId(String notificationId) { + this.notificationId = notificationId; + } + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public JSONObject getEventInformation() { + return eventInformation; + } + + public void setEventInformation(JSONObject eventInformation) { + this.eventInformation = eventInformation; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationResponse.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationResponse.java new file mode 100644 index 00000000..d34ded42 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/NotificationResponse.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.model; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.JOSEException; +import org.json.JSONObject; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This is the notification response model. + */ +public class NotificationResponse { + private String iss = null; + private Long iat = null; + private String jti = null; + private String sub = null; + private String aud = null; + private String txn = null; + private Long toe = null; + private Map events = new HashMap(); + + public Long getIat() { + return iat; + } + + public void setIat(Long iat) { + this.iat = iat; + } + + public String getJti() { + return jti; + } + + public void setJti(String jti) { + this.jti = jti; + } + + public String getSub() { + return sub; + } + + public void setSub(String sub) { + this.sub = sub; + } + + public String getAud() { + return aud; + } + + public void setAud(String aud) { + this.aud = aud; + } + + public String getTxn() { + return txn; + } + + public void setTxn(String txn) { + this.txn = txn; + } + + public Long getToe() { + return toe; + } + + public void setToe(Long toe) { + this.toe = toe; + } + + public Map getEvents() { + return events; + } + + public void setEvents(List eventsList) { + + for (NotificationEvent notificationEvent : eventsList) { + this.events.put(notificationEvent.getEventType(), notificationEvent.getEventInformation()); + } + } + + public String getIss() { + return iss; + } + + public void setIss(String iss) { + this.iss = iss; + } + + /** + * This method is to convert the class to a JSONObject. + * @param notificationResponse Notification Respnse + * @return JSONObject + * @throws IOException IOException when converting the class to JSONObject + * @throws JOSEException JOSEException when converting the class to JSONObject + * @throws IdentityOAuth2Exception IdentityOAuth2Exception when converting the class to JSONObject + */ + public static JsonNode getJsonNode(NotificationResponse notificationResponse) + throws IOException, JOSEException, IdentityOAuth2Exception { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.convertValue(notificationResponse, JsonNode.class); + } + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/persistence/EventNotificationStoreInitializer.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/persistence/EventNotificationStoreInitializer.java new file mode 100644 index 00000000..4c701bc2 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/persistence/EventNotificationStoreInitializer.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.persistence; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.financial.services.accelerator.common.persistence.JDBCPersistenceManager; +import org.wso2.financial.services.accelerator.event.notifications.service.dao.EventNotificationDAO; +import org.wso2.financial.services.accelerator.event.notifications.service.dao.EventNotificationDAOImpl; +import org.wso2.financial.services.accelerator.event.notifications.service.dao.EventSubscriptionDAO; +import org.wso2.financial.services.accelerator.event.notifications.service.dao.EventSubscriptionDAOImpl; +import org.wso2.financial.services.accelerator.event.notifications.service.dao.PostgreSqlEventNotificationDAOImpl; +import org.wso2.financial.services.accelerator.event.notifications.service.dao.PostgreSqlEventSubscriptionDAOImpl; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.queries.EventNotificationSqlStatements; +import org.wso2.financial.services.accelerator.event.notifications.service.queries.EventSubscriptionSqlStatements; +import org.wso2.financial.services.accelerator.event.notifications.service.queries.MSSQLEventNotificationSqlStatements; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Initializer Class for Event Notification Service DB. + */ +public class EventNotificationStoreInitializer { + + private static Log log = LogFactory.getLog(EventNotificationStoreInitializer.class); + private static final String MYSQL = "MySQL"; + private static final String POSTGRE = "PostgreSQL"; + private static final String MSSQL = "Microsoft"; + private static final String ORACLE = "Oracle"; + private static final String H2 = "h2"; + + public static EventNotificationDAO initializeEventNotificationDAO() throws FSEventNotificationException { + + EventNotificationDAO eventNotificationDAO; + try (Connection connection = JDBCPersistenceManager.getInstance().getDBConnection()) { + String driverName = connection.getMetaData().getDriverName(); + + if (driverName.contains(MYSQL) || driverName.contains(H2)) { + eventNotificationDAO = new EventNotificationDAOImpl(new EventNotificationSqlStatements()); + } else if (driverName.contains(POSTGRE)) { + eventNotificationDAO = new PostgreSqlEventNotificationDAOImpl(new EventNotificationSqlStatements()); + } else if (driverName.contains(MSSQL)) { + eventNotificationDAO = new EventNotificationDAOImpl(new MSSQLEventNotificationSqlStatements()); + } else if (driverName.contains(ORACLE)) { + eventNotificationDAO = new EventNotificationDAOImpl(new EventNotificationSqlStatements()); + } else { + throw new FSEventNotificationException("Unhandled DB driver: " + driverName + " detected"); + } + + } catch (SQLException e) { + throw new FSEventNotificationException("Error while getting the database connection : ", e); + } + return eventNotificationDAO; + } + + public static EventNotificationDAO getEventNotificationDAO() throws FSEventNotificationException { + + return initializeEventNotificationDAO(); + } + + public static EventSubscriptionDAO initializeSubscriptionDAO() throws FSEventNotificationException { + + EventSubscriptionDAO eventSubscriptionDao; + try (Connection connection = JDBCPersistenceManager.getInstance().getDBConnection()) { + String driverName = connection.getMetaData().getDriverName(); + + if (driverName.contains(MYSQL) || driverName.contains(H2)) { + eventSubscriptionDao = new EventSubscriptionDAOImpl(new EventSubscriptionSqlStatements()); + } else if (driverName.contains(POSTGRE)) { + eventSubscriptionDao = new PostgreSqlEventSubscriptionDAOImpl(new EventSubscriptionSqlStatements()); + } else if (driverName.contains(MSSQL)) { + eventSubscriptionDao = new EventSubscriptionDAOImpl(new EventSubscriptionSqlStatements()); + } else if (driverName.contains(ORACLE)) { + eventSubscriptionDao = new EventSubscriptionDAOImpl(new EventSubscriptionSqlStatements()); + } else { + throw new FSEventNotificationException("Unhandled DB driver: " + driverName + " detected"); + } + } catch (SQLException e) { + throw new FSEventNotificationException("Error while getting the database connection : ", e); + } + + return eventSubscriptionDao; + } + + public static EventSubscriptionDAO getEventSubscriptionDAO() throws FSEventNotificationException { + + return initializeSubscriptionDAO(); + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/EventNotificationSqlStatements.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/EventNotificationSqlStatements.java new file mode 100644 index 00000000..6dfe0ae5 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/EventNotificationSqlStatements.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.queries; + +/** + * SQL queries to store and retrieve event notifications. + */ +public class EventNotificationSqlStatements { + + public String getStoreNotification() { + + return "INSERT INTO FS_NOTIFICATION (NOTIFICATION_ID, CLIENT_ID, RESOURCE_ID, STATUS) VALUES (?,?,?,?)"; + } + + public String getStoreNotificationEvents() { + + return "INSERT INTO FS_NOTIFICATION_EVENT (NOTIFICATION_ID, EVENT_TYPE, EVENT_INFO) VALUES (?,?,?)"; + } + + public String getEventsByNotificationIdQuery() { + + return "SELECT * FROM FS_NOTIFICATION_EVENT WHERE NOTIFICATION_ID = ?"; + } + + public String getMaxNotificationsQuery() { + + return "SELECT * FROM FS_NOTIFICATION WHERE CLIENT_ID = ? AND STATUS = ? LIMIT ?"; + } + + public String getNotificationsCountQuery() { + + return "SELECT COUNT(*) AS NOTIFICATION_COUNT FROM FS_NOTIFICATION WHERE CLIENT_ID = ? AND STATUS = ?"; + } + + public String storeErrorNotificationQuery() { + + return "INSERT INTO FS_NOTIFICATION_ERROR (NOTIFICATION_ID, ERROR_CODE, DESCRIPTION) VALUES (?,?,?)"; + } + + public String updateNotificationStatusQueryById() { + + return "UPDATE FS_NOTIFICATION SET STATUS = ?, UPDATED_TIMESTAMP= ? WHERE NOTIFICATION_ID = ?"; + } + + public String getNotificationByNotificationId() { + + return "SELECT NOTIFICATION_ID, STATUS FROM FS_NOTIFICATION WHERE NOTIFICATION_ID = ?"; + } + + public String getNotificationsByState() { + + return "SELECT * FROM FS_NOTIFICATION WHERE STATUS = ?"; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/EventSubscriptionSqlStatements.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/EventSubscriptionSqlStatements.java new file mode 100644 index 00000000..818424bb --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/EventSubscriptionSqlStatements.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.queries; + +/** + * SQL queries to store, retrieve, update and delete event notification subscriptions. + */ +public class EventSubscriptionSqlStatements { + + public String storeEventSubscriptionQuery() { + return "INSERT INTO FS_NOTIFICATION_SUBSCRIPTION (SUBSCRIPTION_ID, CLIENT_ID, CALLBACK_URL, TIMESTAMP, " + + "SPEC_VERSION, STATUS, REQUEST) VALUES (?,?,?,?,?,?,?)"; + } + + public String storeSubscribedEventTypesQuery() { + return "INSERT INTO FS_NOTIFICATION_SUBSCRIBED_EVENTS (SUBSCRIPTION_ID, EVENT_TYPE) VALUES (?,?)"; + } + + public String getEventSubscriptionBySubscriptionIdQuery() { + return "SELECT ns.SUBSCRIPTION_ID, ns.CLIENT_ID, ns.REQUEST, ns.CALLBACK_URL, ns.TIMESTAMP, ns.SPEC_VERSION, " + + "ns.STATUS, nse.EVENT_TYPE FROM FS_NOTIFICATION_SUBSCRIPTION ns LEFT JOIN " + + "FS_NOTIFICATION_SUBSCRIBED_EVENTS nse ON ns.SUBSCRIPTION_ID = nse.SUBSCRIPTION_ID WHERE " + + "ns.SUBSCRIPTION_ID = ? AND ns.STATUS = 'CREATED'"; + } + + public String getEventSubscriptionsByClientIdQuery() { + return "SELECT ns.SUBSCRIPTION_ID, ns.CLIENT_ID, ns.REQUEST, ns.CALLBACK_URL, ns.TIMESTAMP, ns.SPEC_VERSION, " + + "ns.STATUS, nse.EVENT_TYPE FROM FS_NOTIFICATION_SUBSCRIPTION ns LEFT JOIN " + + "FS_NOTIFICATION_SUBSCRIBED_EVENTS nse ON ns.SUBSCRIPTION_ID = nse.SUBSCRIPTION_ID WHERE " + + "ns.CLIENT_ID = ? AND ns.STATUS = 'CREATED'"; + } + + public String getEventSubscriptionsByEventTypeQuery() { + return "SELECT ns.SUBSCRIPTION_ID, ns.CLIENT_ID, ns.REQUEST, ns.CALLBACK_URL, ns.TIMESTAMP, ns.SPEC_VERSION, " + + "ns.STATUS, nse.EVENT_TYPE FROM FS_NOTIFICATION_SUBSCRIPTION ns LEFT JOIN " + + "FS_NOTIFICATION_SUBSCRIBED_EVENTS nse ON ns.SUBSCRIPTION_ID = nse.SUBSCRIPTION_ID WHERE " + + "ns.SUBSCRIPTION_ID IN (SELECT ns.SUBSCRIPTION_ID FROM FS_NOTIFICATION_SUBSCRIPTION ns LEFT " + + "JOIN FS_NOTIFICATION_SUBSCRIBED_EVENTS nse ON ns.SUBSCRIPTION_ID = nse.SUBSCRIPTION_ID WHERE " + + "nse.EVENT_TYPE = ? AND ns.STATUS = 'CREATED')"; + } + + public String updateEventSubscriptionQuery() { + return "UPDATE FS_NOTIFICATION_SUBSCRIPTION SET CALLBACK_URL = ?, TIMESTAMP = ?, REQUEST = ?" + + "WHERE SUBSCRIPTION_ID = ?"; + } + + public String updateEventSubscriptionStatusQuery() { + return "UPDATE FS_NOTIFICATION_SUBSCRIPTION SET STATUS = ? WHERE SUBSCRIPTION_ID = ? AND STATUS = 'CREATED'"; + } + + public String deleteSubscribedEventTypesQuery() { + return "DELETE FROM FS_NOTIFICATION_SUBSCRIBED_EVENTS WHERE SUBSCRIPTION_ID = ?"; + } + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/MSSQLEventNotificationSqlStatements.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/MSSQLEventNotificationSqlStatements.java new file mode 100644 index 00000000..ba4e35ef --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/queries/MSSQLEventNotificationSqlStatements.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.queries; + +/** + * MSSQL Queries for Event Notifications. + */ +public class MSSQLEventNotificationSqlStatements extends EventNotificationSqlStatements { + + @Override + public String getMaxNotificationsQuery() { + + return "SELECT * FROM FS_NOTIFICATION WHERE CLIENT_ID = ? AND STATUS = ? ORDER BY NOTIFICATION_ID " + + "OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/DefaultEventNotificationGenerator.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/DefaultEventNotificationGenerator.java new file mode 100644 index 00000000..6eaecc0b --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/DefaultEventNotificationGenerator.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.service; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.registry.core.utils.UUIDGenerator; +import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; +import org.wso2.financial.services.accelerator.common.util.Generated; +import org.wso2.financial.services.accelerator.common.util.JWTUtils; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.Notification; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationEvent; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationResponse; +import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; + +import java.time.Instant; +import java.util.List; + +/** + * Default Event Notification Response Generator Class. + */ +public class DefaultEventNotificationGenerator implements EventNotificationGenerator { + + private static Log log = LogFactory.getLog(DefaultEventNotificationGenerator.class); + + @Override + public NotificationResponse generateEventNotificationBody(Notification notification, + List notificationEventList) + throws FSEventNotificationException { + + NotificationResponse notificationResponse = new NotificationResponse(); + //get current time in milliseconds + Long currentTime = Instant.now().getEpochSecond(); + + //generate transaction Identifier + String transactionIdentifier = UUIDGenerator.generateUUID(); + + notificationResponse.setIss(FinancialServicesConfigParser.getInstance().getEventNotificationTokenIssuer()); + notificationResponse.setIat(currentTime); + notificationResponse.setAud(notification.getClientId()); + notificationResponse.setJti(notification.getNotificationId()); + notificationResponse.setTxn(transactionIdentifier); + notificationResponse.setToe(notification.getUpdatedTimeStamp()); + notificationResponse.setSub(generateSubClaim(notification)); + notificationResponse.setEvents(notificationEventList); + return notificationResponse; + } + + @Generated(message = "Excluded from tests as using a util method from a different package") + public String generateEventNotification(JsonNode jsonNode) throws FSEventNotificationException { + + String payload = EventNotificationServiceUtil.getCustomNotificationPayload(jsonNode); + try { + return JWTUtils.signJWTWithDefaultKey(payload); + } catch (Exception e) { + log.error("Error while signing the JWT token", e); + throw new FSEventNotificationException("Error while signing the JWT token", e); + } + + } + + @Generated(message = "Private method tested when the used method is tested") + private String generateSubClaim(Notification notification) { + return notification.getClientId(); + } + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventCreationService.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventCreationService.java new file mode 100644 index 00000000..e4e09fab --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventCreationService.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.service; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONObject; +import org.wso2.carbon.registry.core.utils.UUIDGenerator; +import org.wso2.financial.services.accelerator.common.util.DatabaseUtils; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.dao.EventNotificationDAO; +import org.wso2.financial.services.accelerator.event.notifications.service.dto.NotificationCreationDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.Notification; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationEvent; +import org.wso2.financial.services.accelerator.event.notifications.service.persistence.EventNotificationStoreInitializer; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Map; + +/** + * This is the event creation service class. + */ +public class EventCreationService { + + private static Log log = LogFactory.getLog(EventCreationService.class); + + /** + * The publishEventNotification methods will call the dao layer to persist the event + * notifications for event polling request. + * + * @param notificationCreationDTO Notification creation DTO + * @return Event Response + * @throws FSEventNotificationException Exception when persisting event notification data + */ + public String publishEventNotification(NotificationCreationDTO notificationCreationDTO) + throws FSEventNotificationException { + + Connection connection = DatabaseUtils.getDBConnection(); + Notification notification = getNotification(notificationCreationDTO); + ArrayList eventsList = getEvents(notificationCreationDTO.getEventPayload()); + + EventNotificationDAO eventCreationDAO = EventNotificationStoreInitializer.getEventNotificationDAO(); + String eventResponse = null; + + try { + eventResponse = eventCreationDAO.persistEventNotification(connection, notification, eventsList); + DatabaseUtils.commitTransaction(connection); + + //TODO: + // Check whether the real time event notification is enabled. +// if (FinancialServicesConfigParser.getInstance().isRealtimeEventNotificationEnabled()) { +// new Thread(new EventNotificationProducerService(notification, eventsList)).start(); +// } + return eventResponse; + } catch (FSEventNotificationException e) { + DatabaseUtils.rollbackTransaction(connection); + throw new FSEventNotificationException("Error when persisting event notification data", e); + } finally { + log.debug(EventNotificationConstants.DATABASE_CONNECTION_CLOSE_LOG_MSG); + DatabaseUtils.closeConnection(connection); + } + } + + /** + * The getEvents method is used to get the NotificationEvents Array list from payload. + * + * @param notificationEvents Notification Events to convert + * @return Event notification List + */ + private ArrayList getEvents(Map notificationEvents) { + + ArrayList eventsList = new ArrayList<>(); + for (Map.Entry entry : notificationEvents.entrySet()) { + NotificationEvent notificationEvent = new NotificationEvent(); + notificationEvent.setEventType(entry.getKey()); + notificationEvent.setEventInformation(entry.getValue()); + eventsList.add(notificationEvent); + } + + return eventsList; + } + + /** + * The getNotification method is used to get the NotificationDAO from payload. + * + * @param notificationCreationDTO Notification Creation DTO + * @return Notification Details + */ + private Notification getNotification(NotificationCreationDTO notificationCreationDTO) { + + Notification notification = new Notification(); + notification.setNotificationId(UUIDGenerator.generateUUID()); + notification.setClientId(notificationCreationDTO.getClientId()); + notification.setResourceId(notificationCreationDTO.getResourceId()); + notification.setStatus(EventNotificationConstants.OPEN); + + return notification; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventNotificationGenerator.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventNotificationGenerator.java new file mode 100644 index 00000000..93425643 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventNotificationGenerator.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.service; + +import com.fasterxml.jackson.databind.JsonNode; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.Notification; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationEvent; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationResponse; + +import java.util.List; + +/** + * Interface for event notification generation. For custom class extensions the class name + * is to be referred from the event_notification_generator in deployment.toml + */ +public interface EventNotificationGenerator { + + /** + * This method is to generate event notification body. To generate custom values + * for the body this method should be extended. + * + * @param notification Notification details + * @param notificationEventList List of notification events + * @return Event Notification Response Body + * @throws FSEventNotificationException Exception when generating event notification body + */ + NotificationResponse generateEventNotificationBody(Notification notification, List + notificationEventList) throws FSEventNotificationException; + + String generateEventNotification(JsonNode jsonNode) throws FSEventNotificationException; +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventPollingService.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventPollingService.java new file mode 100644 index 00000000..ef24bdf3 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventPollingService.java @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.service; + +import com.nimbusds.jose.JOSEException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.financial.services.accelerator.common.util.DatabaseUtils; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.dao.EventNotificationDAO; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.AggregatedPollingResponse; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPolling; +import org.wso2.financial.services.accelerator.event.notifications.service.model.Notification; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationError; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationEvent; +import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationResponse; +import org.wso2.financial.services.accelerator.event.notifications.service.persistence.EventNotificationStoreInitializer; +import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; + +import java.io.IOException; +import java.sql.Connection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This is the event polling service. + */ +public class EventPollingService { + + private static Log log = LogFactory.getLog(EventPollingService.class); + + /** + * The pollEvents methods will return the Aggregated Polling Response for + * event polling request. + * @param eventPolling Event polling request + * @return AggregatedPollingResponse Aggregated Polling Response + * @throws FSEventNotificationException Exception when polling events + */ + public AggregatedPollingResponse pollEvents(EventPolling eventPolling) + throws FSEventNotificationException { + + Connection connection = DatabaseUtils.getDBConnection(); + AggregatedPollingResponse aggregatedPollingResponse = new AggregatedPollingResponse(); + EventNotificationDAO eventNotificationDAO = EventNotificationStoreInitializer.getEventNotificationDAO(); + + EventNotificationGenerator eventNotificationGenerator = EventNotificationServiceUtil. + getEventNotificationGenerator(); + + Map sets = new HashMap<>(); + + //Short polling + if (eventPolling.getReturnImmediately()) { + + try { + //Update notifications with ack + for (String notificationId : eventPolling.getAck()) { + eventNotificationDAO.updateNotificationStatusById(connection, notificationId, + EventNotificationConstants.ACK); + } + + //Update notifications with err + for (Map.Entry entry: eventPolling.getErrors().entrySet()) { + //Check if the notification is in OPEN status + if (eventNotificationDAO.getNotificationStatus(connection, entry.getKey())) { + eventNotificationDAO.updateNotificationStatusById(connection, entry.getKey(), + EventNotificationConstants.ERROR); + eventNotificationDAO.storeErrorNotification(connection, entry.getValue()); + } + } + + //Retrieve notifications + int maxEvents = eventPolling.getMaxEvents(); + + if (maxEvents == 0) { + aggregatedPollingResponse.setSets(sets); + aggregatedPollingResponse.setStatus(EventNotificationConstants.OK); + } else { + + int setsToReturn = eventPolling.getSetsToReturn(); + + List notificationList; + + if (maxEvents < setsToReturn) { + notificationList = eventNotificationDAO.getNotificationsByClientIdAndStatus(connection, + eventPolling.getClientId(), EventNotificationConstants.OPEN, maxEvents); + + } else { + notificationList = eventNotificationDAO.getNotificationsByClientIdAndStatus(connection, + eventPolling.getClientId(), EventNotificationConstants.OPEN, setsToReturn); + } + + if (notificationList.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug(String.format("No FS Event Notifications available for for the client " + + "with ID : '%s'.", eventPolling.getClientId().replaceAll("[\r\n]", ""))); + } + aggregatedPollingResponse.setStatus(EventNotificationConstants.NOT_FOUND); + } else { + if (log.isDebugEnabled()) { + log.debug(String.format("FS Event Notifications available for the client " + + "with ID : '%s'.", eventPolling.getClientId().replaceAll("[\r\n]", ""))); + } + aggregatedPollingResponse.setStatus(EventNotificationConstants.OK); + + for (Notification notification : notificationList) { + + //Get events by notificationId + List notificationEvents = eventNotificationDAO. + getEventsByNotificationID(connection, notification.getNotificationId()); + + NotificationResponse responseNotification = eventNotificationGenerator. + generateEventNotificationBody(notification, notificationEvents); + sets.put(notification.getNotificationId(), eventNotificationGenerator + .generateEventNotification(NotificationResponse.getJsonNode(responseNotification))); + log.info("Retrieved FS event notifications"); + } + aggregatedPollingResponse.setSets(sets); + } + } + + int count = eventNotificationDAO.getNotificationCountByClientIdAndStatus(connection, + eventPolling.getClientId(), EventNotificationConstants.OPEN) + - aggregatedPollingResponse.getSets().size(); + + aggregatedPollingResponse.setCount(count); + DatabaseUtils.commitTransaction(connection); + + return aggregatedPollingResponse; + } catch (FSEventNotificationException | + IOException | JOSEException | IdentityOAuth2Exception e) { + log.debug("Error when retrieving FS event notifications.", e); + DatabaseUtils.rollbackTransaction(connection); + throw new FSEventNotificationException("Error when retrieving FS event notifications.", e); + } finally { + log.debug(EventNotificationConstants.DATABASE_CONNECTION_CLOSE_LOG_MSG); + DatabaseUtils.closeConnection(connection); + } + } + + return null; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventSubscriptionService.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventSubscriptionService.java new file mode 100644 index 00000000..c28a5991 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventSubscriptionService.java @@ -0,0 +1,260 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.service; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.wso2.financial.services.accelerator.common.util.DatabaseUtils; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.dao.EventSubscriptionDAO; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscription; +import org.wso2.financial.services.accelerator.event.notifications.service.persistence.EventNotificationStoreInitializer; + +import java.sql.Connection; +import java.util.List; + +/** + * This is the event subscription service class. + */ +public class EventSubscriptionService { + private static final Log log = LogFactory.getLog(EventSubscriptionService.class); + + /** + * This method will call the dao layer to persist the event subscription. + * + * @param eventSubscription event subscription object that needs to be persisted + * @return event subscription object that is persisted + * @throws FSEventNotificationException if an error occurred while persisting the event subscription + */ + public EventSubscription createEventSubscription(EventSubscription eventSubscription) + throws FSEventNotificationException { + + EventSubscriptionDAO eventSubscriptionDAO = EventNotificationStoreInitializer.getEventSubscriptionDAO(); + + Connection connection = DatabaseUtils.getDBConnection(); + + try { + //store event subscription data in the database + EventSubscription storeEventSubscriptionResult = eventSubscriptionDAO. + storeEventSubscription(connection, eventSubscription); + //store subscribed event types in the database + if (eventSubscription.getEventTypes() != null && !eventSubscription.getEventTypes().isEmpty()) { + List storedEventTypes = eventSubscriptionDAO.storeSubscribedEventTypes(connection, + storeEventSubscriptionResult.getSubscriptionId(), eventSubscription.getEventTypes()); + storeEventSubscriptionResult.setEventTypes(storedEventTypes); + } + log.debug("Event subscription created successfully."); + DatabaseUtils.commitTransaction(connection); + return storeEventSubscriptionResult; + } catch (FSEventNotificationException e) { + log.error("Error while creating event subscription.", e); + DatabaseUtils.rollbackTransaction(connection); + throw new FSEventNotificationException(EventNotificationConstants.ERROR_STORING_EVENT_SUBSCRIPTION, e); + } finally { + DatabaseUtils.closeConnection(connection); + } + } + + /** + * This method will call the dao layer to retrieve a single event subscription. + * + * @param subscriptionId subscription id of the event subscription + * @return event subscription object that is retrieved + * @throws FSEventNotificationException if an error occurred while retrieving the event subscription + */ + public EventSubscription getEventSubscriptionBySubscriptionId(String subscriptionId) + throws FSEventNotificationException { + + EventSubscriptionDAO eventSubscriptionDAO = EventNotificationStoreInitializer.getEventSubscriptionDAO(); + + Connection connection = DatabaseUtils.getDBConnection(); + + try { + EventSubscription eventSubscription = eventSubscriptionDAO + .getEventSubscriptionBySubscriptionId(connection, subscriptionId); + if (log.isDebugEnabled()) { + log.debug(String.format("Event subscription for subscription Id %s retrieved successfully.", + subscriptionId.replaceAll("[\r\n]", ""))); + } + DatabaseUtils.commitTransaction(connection); + return eventSubscription; + } catch (FSEventNotificationException e) { + log.error("Error while retrieving event subscription.", e); + DatabaseUtils.rollbackTransaction(connection); + throw new FSEventNotificationException(e.getMessage(), e); + } finally { + DatabaseUtils.closeConnection(connection); + } + } + + /** + * This method will call the dao layer to retrieve all event subscriptions of a client. + * + * @param clientId client id of the event subscription + * @return list of event subscriptions that are retrieved + * @throws FSEventNotificationException if an error occurred while retrieving the event subscriptions + */ + public List getEventSubscriptionsByClientId(String clientId) + throws FSEventNotificationException { + + Connection connection = DatabaseUtils.getDBConnection(); + try { + EventSubscriptionDAO eventSubscriptionDAO = EventNotificationStoreInitializer.getEventSubscriptionDAO(); + List eventSubscriptions = eventSubscriptionDAO + .getEventSubscriptionsByClientId(connection, clientId); + if (log.isDebugEnabled()) { + log.debug(String.format("Event subscriptions for client Id %s retrieved successfully.", + clientId.replaceAll("[\r\n]", ""))); + } + DatabaseUtils.commitTransaction(connection); + return eventSubscriptions; + } catch (FSEventNotificationException e) { + log.error("Error while retrieving event subscriptions.", e); + DatabaseUtils.rollbackTransaction(connection); + throw new FSEventNotificationException(e.getMessage(), e); + } finally { + DatabaseUtils.closeConnection(connection); + } + } + + /** + * This method will call the dao layer to retrieve all event subscriptions by event type. + * + * @param eventType event type that needs to be subscribed by the retrieving event subscriptions. + * @return list of event subscriptions that are retrieved + * @throws FSEventNotificationException if an error occurred while retrieving the event subscriptions + */ + public List getEventSubscriptionsByEventType(String eventType) + throws FSEventNotificationException { + + + Connection connection = DatabaseUtils.getDBConnection(); + try { + EventSubscriptionDAO eventSubscriptionDAO = EventNotificationStoreInitializer.getEventSubscriptionDAO(); + List eventSubscriptions = eventSubscriptionDAO + .getEventSubscriptionsByEventType(connection, eventType); + if (log.isDebugEnabled()) { + log.debug(String.format("Event subscriptions for event type %s retrieved successfully.", + eventType.replaceAll("[\r\n]", ""))); + } + DatabaseUtils.commitTransaction(connection); + return eventSubscriptions; + } catch (FSEventNotificationException e) { + log.error("Error while retrieving event subscriptions.", e); + DatabaseUtils.rollbackTransaction(connection); + throw new FSEventNotificationException(e.getMessage(), e); + } finally { + DatabaseUtils.closeConnection(connection); + } + } + + /** + * This method will call the dao layer to update an event subscription. + * + * @param eventSubscription event subscription object that needs to be updated + * @return true if the event subscription is updated successfully + * @throws FSEventNotificationException if an error occurred while updating the event subscription + */ + public Boolean updateEventSubscription(EventSubscription eventSubscription) + throws FSEventNotificationException { + + Connection connection = DatabaseUtils.getDBConnection(); + + EventSubscriptionDAO eventSubscriptionDAO = EventNotificationStoreInitializer.getEventSubscriptionDAO(); + + try { + //get the stored event subscription + EventSubscription retrievedEventSubscription = eventSubscriptionDAO. + getEventSubscriptionBySubscriptionId(connection, eventSubscription.getSubscriptionId()); + + //update request data column + JSONObject storedRequestData = new JSONObject(retrievedEventSubscription.getRequestData()); + JSONObject receivedRequestData = new JSONObject(eventSubscription.getRequestData()); + for (String key : storedRequestData.keySet()) { + if (receivedRequestData.has(key)) { + storedRequestData.put(key, receivedRequestData.get(key)); + } + } + eventSubscription.setRequestData(storedRequestData.toString()); + + //update event subscription + boolean isUpdated = eventSubscriptionDAO.updateEventSubscription(connection, eventSubscription); + + //update subscribed event types + if (isUpdated && eventSubscription.getEventTypes() != null && + !eventSubscription.getEventTypes().isEmpty()) { + //delete the existing subscribed event types + eventSubscriptionDAO.deleteSubscribedEventTypes(connection, eventSubscription.getSubscriptionId()); + //store the updated subscribed event types + List storedEventTypes = eventSubscriptionDAO.storeSubscribedEventTypes(connection, + eventSubscription.getSubscriptionId(), eventSubscription.getEventTypes()); + eventSubscription.setEventTypes(storedEventTypes); + } else if (!isUpdated) { + log.debug("Event subscription update failed."); + DatabaseUtils.rollbackTransaction(connection); + } + log.debug("Event subscription updated successfully."); + DatabaseUtils.commitTransaction(connection); + return isUpdated; + } catch (JSONException e) { + log.error("Error while Parsing the stored request Object", e); + throw new FSEventNotificationException("Error while Parsing the stored request Object", e); + } catch (FSEventNotificationException e) { + log.error("Error while updating event subscription.", e); + DatabaseUtils.rollbackTransaction(connection); + throw new FSEventNotificationException(e.getMessage(), e); + } finally { + DatabaseUtils.closeConnection(connection); + } + } + + /** + * This method will call the dao layer to delete an event subscription. + * + * @param subscriptionId subscription id of the event subscription + * @return true if the event subscription is deleted successfully + * @throws FSEventNotificationException if an error occurred while deleting the event subscription + */ + public Boolean deleteEventSubscription(String subscriptionId) throws FSEventNotificationException { + + Connection connection = DatabaseUtils.getDBConnection(); + + try { + EventSubscriptionDAO eventSubscriptionDAO = EventNotificationStoreInitializer.getEventSubscriptionDAO(); + boolean isDeleted = eventSubscriptionDAO.deleteEventSubscription(connection, subscriptionId); + if (isDeleted) { + log.debug("Event subscription deleted successfully."); + DatabaseUtils.commitTransaction(connection); + } else { + log.debug("Event subscription deletion failed."); + DatabaseUtils.rollbackTransaction(connection); + } + return isDeleted; + } catch (FSEventNotificationException e) { + log.error("Error while deleting event subscription.", e); + DatabaseUtils.rollbackTransaction(connection); + throw new FSEventNotificationException(e.getMessage(), e); + } finally { + DatabaseUtils.closeConnection(connection); + } + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java new file mode 100644 index 00000000..c7a2788b --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.event.notifications.service.util; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.wso2.carbon.identity.application.common.model.ServiceProvider; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.util.OAuth2Util; +import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; +import org.wso2.financial.services.accelerator.common.util.FinancialServicesUtils; +import org.wso2.financial.services.accelerator.common.util.Generated; +import org.wso2.financial.services.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; +import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; +import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventNotificationErrorDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; +import org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventCreationServiceHandler; +import org.wso2.financial.services.accelerator.event.notifications.service.service.EventNotificationGenerator; + +import java.util.Optional; + +/** + * Default event notification validations. + */ +public class EventNotificationServiceUtil { + + private static final Log log = LogFactory.getLog(EventNotificationServiceUtil.class); + private static volatile ConsentCoreServiceImpl consentCoreService; + + /** + * This method is used to send the polling generator as per config. + * + * @return EventNotificationGenerator + */ + public static EventNotificationGenerator getEventNotificationGenerator() { + + return (EventNotificationGenerator) + FinancialServicesUtils.getClassInstanceFromFQN(FinancialServicesConfigParser.getInstance() + .getEventNotificationGenerator()); + } + + /** + * This method is used to send the default realtime event notification request generator. + * + * @return RealtimeEventNotificationRequestGenerator + */ + //TODO +// public static RealtimeEventNotificationRequestGenerator getRealtimeEventNotificationRequestGenerator() { +// +// RealtimeEventNotificationRequestGenerator realtimeEventNotificationRequestGenerator = +// (RealtimeEventNotificationRequestGenerator) FinancialServicesUtils +// .getClassInstanceFromFQN(FinancialServicesConfigParser.getInstance(). +// getRealtimeEventNotificationRequestGenerator()); +// return realtimeEventNotificationRequestGenerator; +// } + + /** + * Method to modify event notification payload with custom eventValues. + * + * @param jsonNode Json Node to convert + * @return String eventNotificationPayload + */ + public static String getCustomNotificationPayload(JsonNode jsonNode) { + + return jsonNode.toString(); + } + + /** + * Method to get event JSON from eventInformation payload string. + * @param eventInformation String event Information + * @return JSONObject converted event json + * @throws JSONException Exception when parsing event information + */ + public static JSONObject getEventJSONFromString(String eventInformation) throws JSONException { + + return new JSONObject(eventInformation); + } + + /** + * Validate if the client ID is existing. + * @param clientId client ID of the TPP + * @throws FSEventNotificationException Exception when validating client ID + */ + @Generated(message = "Excluded since this needs OAuth2Util service provider") + public static void validateClientId(String clientId) throws FSEventNotificationException { + + if (StringUtils.isNotEmpty(clientId)) { + Optional serviceProvider; + try { + serviceProvider = Optional.ofNullable(OAuth2Util.getServiceProvider(clientId)); + if (!serviceProvider.isPresent()) { + log.error(EventNotificationConstants.INVALID_CLIENT_ID); + throw new FSEventNotificationException(EventNotificationConstants.INVALID_CLIENT_ID); + } + } catch (IdentityOAuth2Exception e) { + log.error(EventNotificationConstants.INVALID_CLIENT_ID, e); + throw new FSEventNotificationException(EventNotificationConstants.INVALID_CLIENT_ID); + } + } + } + + @Generated(message = "Creating a single instance for ConsentCoreService") + public static synchronized ConsentCoreServiceImpl getConsentCoreServiceImpl() { + if (consentCoreService == null) { + synchronized (ConsentCoreServiceImpl.class) { + if (consentCoreService == null) { + consentCoreService = new ConsentCoreServiceImpl(); + } + } + } + return consentCoreService; + } + + /** + * Get the callback URL of the TPP from the Subscription Object. + * + * @param clientID client ID of the TPP + * @return callback URL of the TPP + */ + public static String getCallbackURL(String clientID) { + + return "http://localhost:8080/sample-tpp-server"; + } + + /** + * Get the default event creation service handler. + * + * @return DefaultEventCreationServiceHandler + */ + public static DefaultEventCreationServiceHandler getDefaultEventCreationServiceHandler() { + return new DefaultEventCreationServiceHandler(); + } + + /** + * Method to map Event subscription Service error to API response. + * + * @param error Error code + * @param errorDescription Error description + * @return EventNotificationErrorDTO + */ + public static EventNotificationErrorDTO getErrorDTO(String error, String errorDescription) { + EventNotificationErrorDTO eventNotificationErrorDTO = new EventNotificationErrorDTO(); + eventNotificationErrorDTO.setError(error); + eventNotificationErrorDTO.setErrorDescription(errorDescription); + return eventNotificationErrorDTO; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-exclude.xml b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-exclude.xml new file mode 100644 index 00000000..43ad701d --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-exclude.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-include.xml b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-include.xml new file mode 100644 index 00000000..8932a22e --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-include.xml @@ -0,0 +1,22 @@ + + + + + + From 358e98b5e0daf06563ba2315f548dde031bfc6aa Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Sun, 27 Oct 2024 23:18:14 +0530 Subject: [PATCH 14/23] Removing conflicts --- .../IdempotencyValidationException.java | 6 +- .../IdempotencyValidationUtils.java | 38 +- .../config/OpenBankingConfigParser.java | 1479 ----------------- .../common/constant/OpenBankingConstants.java | 260 --- .../idempotency/IdempotencyConstants.java | 39 - .../idempotency/IdempotencyValidator.java | 247 --- .../IdempotencyValidatorTests.java | 283 ---- pom.xml | 717 +++----- 8 files changed, 302 insertions(+), 2767 deletions(-) delete mode 100644 open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java delete mode 100644 open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java delete mode 100644 open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java delete mode 100644 open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java delete mode 100644 open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/idempotency/IdempotencyValidationException.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/idempotency/IdempotencyValidationException.java index 821287ca..54a5fb5f 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/idempotency/IdempotencyValidationException.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/idempotency/IdempotencyValidationException.java @@ -16,14 +16,14 @@ * under the License. */ -package com.wso2.openbanking.accelerator.consent.extensions.common.idempotency; +package org.wso2.financial.services.accelerator.consent.mgt.extensions.common.idempotency; -import com.wso2.openbanking.accelerator.common.exception.OpenBankingException; +import org.wso2.financial.services.accelerator.common.exception.ConsentManagementException; /** * Used for handling exceptions in Idempotency Validation. */ -public class IdempotencyValidationException extends OpenBankingException { +public class IdempotencyValidationException extends ConsentManagementException { public IdempotencyValidationException(String message) { super(message); diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/idempotency/IdempotencyValidationUtils.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/idempotency/IdempotencyValidationUtils.java index ce1119b1..afcaf827 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/idempotency/IdempotencyValidationUtils.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/idempotency/IdempotencyValidationUtils.java @@ -16,15 +16,15 @@ * under the License. */ -package com.wso2.openbanking.accelerator.consent.extensions.common.idempotency; +package org.wso2.financial.services.accelerator.consent.mgt.extensions.common.idempotency; -import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; -import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; -import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; -import com.wso2.openbanking.accelerator.consent.mgt.service.ConsentCoreService; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; +import org.wso2.financial.services.accelerator.common.exception.ConsentManagementException; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.internal.ConsentExtensionsDataHolder; +import org.wso2.financial.services.accelerator.consent.mgt.service.ConsentCoreService; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -32,6 +32,9 @@ import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Class to hold idempotency validation utils. @@ -49,8 +52,8 @@ public class IdempotencyValidationUtils { * @param idempotencyKeyValue Idempotency Key Value * @return List of consent ids if available, else an empty list will be returned */ - static ArrayList getConsentIdsFromIdempotencyKey(String idempotencyKeyName, - String idempotencyKeyValue) { + static List getConsentIdsFromIdempotencyKey(String idempotencyKeyName, + String idempotencyKeyValue) { try { return consentCoreService.getConsentIdByConsentAttributeNameAndValue( idempotencyKeyName, idempotencyKeyValue); @@ -60,6 +63,21 @@ static ArrayList getConsentIdsFromIdempotencyKey(String idempotencyKeyNa } } + /** + * Method to retrieve the consent ids and idempotency key value using the idempotency key. + * + * @param idempotencyKeyName Idempotency Key Name + * @return Map of consent ids and idempotency key vallue if available, else an empty map will be returned + */ + static Map getAttributesFromIdempotencyKey(String idempotencyKeyName) { + try { + return consentCoreService.getConsentAttributesByName(idempotencyKeyName); + } catch (ConsentManagementException e) { + log.debug("No consent ids found for the idempotency key value"); + return new HashMap<>(); + } + } + /** * Method to compare the client ID sent in the request and client id retrieved from the database. * @@ -86,13 +104,13 @@ static boolean isRequestReceivedWithinAllowedTime(long createdTime) { log.debug("Created time is of the previous request is not correctly set. Hence returning false"); return false; } - String allowedTimeDuration = OpenBankingConfigParser.getInstance().getIdempotencyAllowedTime(); + String allowedTimeDuration = FinancialServicesConfigParser.getInstance().getIdempotencyAllowedTime(); if (StringUtils.isNotBlank(allowedTimeDuration)) { OffsetDateTime createdDate = OffsetDateTime.parse(toISO8601DateTime(createdTime)); OffsetDateTime currDate = OffsetDateTime.now(createdDate.getOffset()); - long diffInMinutes = Duration.between(createdDate, currDate).toMinutes(); - return diffInMinutes <= Long.parseLong(allowedTimeDuration); + long diffInHours = Duration.between(createdDate, currDate).toHours(); + return diffInHours <= Long.parseLong(allowedTimeDuration); } else { log.error("Idempotency allowed duration is not configured in the system. Hence returning false"); return false; diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java deleted file mode 100644 index 2ad025f9..00000000 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java +++ /dev/null @@ -1,1479 +0,0 @@ -/** - * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.wso2.openbanking.accelerator.common.config; - -import com.wso2.openbanking.accelerator.common.constant.OpenBankingConstants; -import com.wso2.openbanking.accelerator.common.exception.OpenBankingRuntimeException; -import com.wso2.openbanking.accelerator.common.util.CarbonUtils; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.apache.axiom.om.OMElement; -import org.apache.axiom.om.OMException; -import org.apache.axiom.om.impl.builder.StAXOMBuilder; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.wso2.securevault.SecretResolver; -import org.wso2.securevault.SecretResolverFactory; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Stack; -import java.util.stream.Collectors; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLStreamException; - -import static java.util.Map.Entry.comparingByKey; - -/** - * Config parser for open-banking.xml. - */ -public class OpenBankingConfigParser { - - // To enable attempted thread-safety using double-check locking - private static final Object lock = new Object(); - private static final Log log = LogFactory.getLog(OpenBankingConfigParser.class); - private static final Map configuration = new HashMap<>(); - private static final Map> obExecutors = new HashMap<>(); - private static final Map> dataPublishingStreams = new HashMap<>(); - private static final Map> dataPublishingValidationMap = new HashMap<>(); - private static final Map> dcrRegistrationConfigs = new HashMap<>(); - private static final Map> authorizeSteps = new HashMap<>(); - private static final Map> allowedScopes = new HashMap<>(); - private static final Map> allowedAPIs = new HashMap<>(); - private static final Map revocationValidators = new HashMap<>(); - private static final List serviceActivatorSubscribers = new ArrayList<>(); - private static final Map> keyManagerAdditionalProperties - = new HashMap<>(); - private static Map obEventExecutors = new HashMap<>(); - private static OpenBankingConfigParser parser; - private static String configFilePath; - private static SecretResolver secretResolver; - private OMElement rootElement; - - private Map authWorkerConfig = new HashMap<>(); - - /** - * Private Constructor of config parser. - */ - private OpenBankingConfigParser() { - - buildConfiguration(); - } - - /** - * Singleton getInstance method to create only one object. - * - * @return OpenBankingConfigParser object - */ - public static OpenBankingConfigParser getInstance() { - - if (parser == null) { - synchronized (lock) { - if (parser == null) { - parser = new OpenBankingConfigParser(); - } - } - } - return parser; - } - - /** - * Method to get an instance of ConfigParser when custom file path is provided. - * - * This method is deprecated as it allows custom absolute file paths which could result in - * path traversal attacks. Do not use this method unless the custom path is trusted. - * - * @param filePath Custom file path - * @return OpenBankingConfigParser object - * @Deprecated use OpenBankingConfigParser.getInstance() - */ - @Deprecated - public static OpenBankingConfigParser getInstance(String filePath) { - - configFilePath = filePath; - return getInstance(); - } - - /** - * Method to obtain map of configs. - * - * @return Config map - */ - public Map getConfiguration() { - - return configuration; - } - - /** - * Method to read the configuration (in a recursive manner) as a model and put them in the configuration map. - */ - @SuppressFBWarnings("PATH_TRAVERSAL_IN") - // Suppressed content - new FileInputStream(configFilePath) - // Suppression reason - False Positive : Method for passing configFilePath is deprecated and is used for testing - // purposes only. Therefore, it can be assumed that configFilePath is a trusted filepath - // Suppressed warning count - 1 - private void buildConfiguration() { - - InputStream inStream = null; - StAXOMBuilder builder; - String warningMessage = ""; - try { - if (configFilePath != null) { - File openBankingConfigXml = new File(configFilePath); - if (openBankingConfigXml.exists()) { - inStream = new FileInputStream(openBankingConfigXml); - } - } else { - File openBankingConfigXml = new File(CarbonUtils.getCarbonConfigDirPath(), - OpenBankingConstants.OB_CONFIG_FILE); - if (openBankingConfigXml.exists()) { - inStream = new FileInputStream(openBankingConfigXml); - } - } - if (inStream == null) { - String message = - "open-banking configuration not found at: " + configFilePath + " . Cause - " + warningMessage; - if (log.isDebugEnabled()) { - log.debug(message.replaceAll("[\r\n]", "")); - } - throw new FileNotFoundException(message); - } - builder = new StAXOMBuilder(inStream); - builder.setDoDebug(false); - rootElement = builder.getDocumentElement(); - Stack nameStack = new Stack<>(); - secretResolver = SecretResolverFactory.create(rootElement, true); - readChildElements(rootElement, nameStack); - buildOBExecutors(); - buildDataPublishingStreams(); - buildDCRParameters(); - buildConsentAuthSteps(); - buildAllowedScopes(); - buildAllowedSubscriptions(); - buildServiceActivatorSubscribers(); - buildKeyManagerProperties(); - buildOBEventExecutors(); - buildWorkers(); - } catch (IOException | XMLStreamException | OMException e) { - throw new OpenBankingRuntimeException("Error occurred while building configuration from open-banking.xml", - e); - } finally { - try { - if (inStream != null) { - inStream.close(); - } - } catch (IOException e) { - log.error("Error closing the input stream for open-banking.xml", e); - } - } - } - - private void buildOBExecutors() { - - OMElement gatewayElement = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.GATEWAY_CONFIG_TAG)); - - if (gatewayElement != null) { - - OMElement openBankingGatewayExecutors = gatewayElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.GATEWAY_EXECUTOR_CONFIG_TAG)); - - if (openBankingGatewayExecutors != null) { - //obtaining each consent type element under OpenBankingGatewayExecutors tag - Iterator consentTypeElement = openBankingGatewayExecutors.getChildElements(); - while (consentTypeElement.hasNext()) { - OMElement consentType = (OMElement) consentTypeElement.next(); - String consentTypeName = consentType.getLocalName(); - Map executors = new HashMap<>(); - //obtaining each Executor element under each consent type - Iterator obExecutor = consentType.getChildrenWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.EXECUTOR_CONFIG_TAG)); - if (obExecutor != null) { - while (obExecutor.hasNext()) { - OMElement executorElement = obExecutor.next(); - //Retrieve class name and priority from executor config - String obExecutorClass = executorElement.getAttributeValue(new QName("class")); - String obExecutorPriority = executorElement.getAttributeValue(new QName("priority")); - - if (StringUtils.isEmpty(obExecutorClass)) { - //Throwing exceptions since we cannot proceed without invalid executor names - throw new OpenBankingRuntimeException("Executor class is not defined " + - "correctly in open-banking.xml"); - } - int priority = Integer.MAX_VALUE; - if (!StringUtils.isEmpty(obExecutorPriority)) { - priority = Integer.parseInt(obExecutorPriority); - } - executors.put(priority, obExecutorClass); - } - } - //Ordering the executors based on the priority number - LinkedHashMap priorityMap = executors.entrySet() - .stream() - .sorted(comparingByKey()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2, - LinkedHashMap::new)); - obExecutors.put(consentTypeName, priorityMap); - } - } - } - } - - protected void buildKeyManagerProperties() { - - OMElement keyManagerElement = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.KEY_MANAGER_CONFIG_TAG)); - - if (keyManagerElement != null) { - OMElement keyManagerProperties = keyManagerElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.KEY_MANAGER_ADDITIONAL_PROPERTIES_CONFIG_TAG)); - - if (keyManagerProperties != null) { - Iterator properties = keyManagerProperties.getChildrenWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.PROPERTY_CONFIG_TAG)); - if (properties != null) { - while (properties.hasNext()) { - OMElement propertyElement = properties.next(); - - //Retrieve attributes from key manager config - Map property = new HashMap<>(); - property.put("priority", propertyElement.getAttributeValue(new QName("priority"))); - property.put("label", propertyElement.getAttributeValue(new QName("label"))); - property.put("type", propertyElement.getAttributeValue(new QName("type"))); - property.put("tooltip", propertyElement.getAttributeValue(new QName("tooltip"))); - property.put("default", propertyElement.getAttributeValue(new QName("default"))); - property.put("required", propertyElement.getAttributeValue(new QName("required"))); - property.put("mask", propertyElement.getAttributeValue(new QName("mask"))); - property.put("multiple", propertyElement.getAttributeValue(new QName("multiple"))); - property.put("values", propertyElement.getAttributeValue(new QName("values"))); - String propertyName = propertyElement.getAttributeValue(new QName("name")); - - if (StringUtils.isBlank(propertyName)) { - //Throwing exceptions since we cannot proceed without property names - throw new OpenBankingRuntimeException("Additional property name is not defined " + - "correctly in open-banking.xml"); - } - - keyManagerAdditionalProperties.put(propertyName, property); - } - } - } - } - } - - protected void buildDataPublishingStreams() { - - OMElement dataPublishingElement = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.DATA_PUBLISHING_CONFIG_TAG)); - - if (dataPublishingElement != null) { - OMElement thriftElement = dataPublishingElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.THRIFT_CONFIG_TAG)); - - if (thriftElement != null) { - OMElement streams = thriftElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.STREAMS_CONFIG_TAG)); - - if (streams != null) { - Iterator dataStreamElement = streams.getChildElements(); - while (dataStreamElement.hasNext()) { - OMElement dataStream = (OMElement) dataStreamElement.next(); - String dataStreamName = dataStream.getLocalName(); - Map attributes = new HashMap<>(); - //obtaining attributes under each stream - Iterator attribute = dataStream.getChildrenWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.ATTRIBUTE_CONFIG_TAG)); - if (attribute != null) { - while (attribute.hasNext()) { - OMElement attributeElement = attribute.next(); - //Retrieve attribute name and priority from config - String attributeName = attributeElement.getAttributeValue(new QName("name")); - String attributePriority = attributeElement.getAttributeValue(new QName("priority")); - String isRequired = attributeElement.getAttributeValue(new QName("required")); - String type = attributeElement.getAttributeValue(new QName("type")); - - if (StringUtils.isEmpty(attributeName)) { - //Throwing exceptions since we cannot proceed without valid attribute names - throw new OpenBankingRuntimeException( - "Data publishing attribute name is not defined " + - "correctly in open-banking.xml"); - } - int priority = Integer.MAX_VALUE; - if (!StringUtils.isEmpty(attributePriority)) { - priority = Integer.parseInt(attributePriority); - } - boolean required = false; - if (!StringUtils.isEmpty(isRequired)) { - required = Boolean.parseBoolean(isRequired); - } - - String attributeType = "string"; - if (!StringUtils.isEmpty(type)) { - attributeType = type; - } - - Map metadata = new HashMap<>(); - metadata.put(OpenBankingConstants.REQUIRED, required); - metadata.put(OpenBankingConstants.ATTRIBUTE_TYPE, attributeType); - - attributes.put(priority, attributeName); - String attributeKey = dataStreamName + "_" + attributeName; - dataPublishingValidationMap.put(attributeKey, metadata); - } - } - //Ordering the attributes based on the priority number - LinkedHashMap priorityMap = attributes.entrySet() - .stream() - .sorted(comparingByKey()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2, - LinkedHashMap::new)); - dataPublishingStreams.put(dataStreamName, priorityMap); - } - } - } - } - } - - private void buildDCRParameters() { - - OMElement dcrElement = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.DCR_CONFIG_TAG)); - - if (dcrElement != null) { - OMElement registrationElement = dcrElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.DCR_REGISTRATION_CONFIG_TAG)); - - if (registrationElement != null) { - //obtaining each parameter type element under RegistrationRequestPrams tag - Iterator parameterTypeElement = registrationElement.getChildElements(); - while (parameterTypeElement.hasNext()) { - OMElement parameterType = (OMElement) parameterTypeElement.next(); - String parameterTypeName = parameterType.getLocalName(); - Map parameterValues = new HashMap<>(); - //obtaining each element under each parameter type - Iterator childValues = parameterType.getChildElements(); - while (childValues.hasNext()) { - OMElement child = (OMElement) childValues.next(); - if (OpenBankingConstants.DCR_REGISTRATION_PARAM_ALLOWED_VALUE_TAG - .equalsIgnoreCase(child.getLocalName())) { - - OMElement allowedValuesElement = parameterType.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.DCR_REGISTRATION_PARAM_ALLOWED_VALUE_TAG)); - - List values = new ArrayList<>(); - if (allowedValuesElement != null) { - Iterator allowedValues = allowedValuesElement.getChildElements(); - while (allowedValues.hasNext()) { - OMElement value = (OMElement) allowedValues.next(); - values.add(value.getText()); - } - parameterValues.put(child.getLocalName(), values); - } - } else { - parameterValues.put(child.getLocalName(), child.getText()); - } - } - dcrRegistrationConfigs.put(parameterTypeName, parameterValues); - } - } - } - - } - - private void buildConsentAuthSteps() { - - OMElement consentElement = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.CONSENT_CONFIG_TAG)); - - if (consentElement != null) { - OMElement consentAuthorizeSteps = consentElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.AUTHORIZE_STEPS_CONFIG_TAG)); - - if (consentAuthorizeSteps != null) { - //obtaining each step type element under AuthorizeSteps tag - Iterator stepTypeElement = consentAuthorizeSteps.getChildElements(); - while (stepTypeElement.hasNext()) { - OMElement stepType = (OMElement) stepTypeElement.next(); - String consentTypeName = stepType.getLocalName(); - Map executors = new HashMap<>(); - //obtaining each step under each consent type - Iterator obExecutor = stepType.getChildrenWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.STEP_CONFIG_TAG)); - if (obExecutor != null) { - while (obExecutor.hasNext()) { - OMElement executorElement = obExecutor.next(); - //Retrieve class name and priority from executor config - String obExecutorClass = executorElement.getAttributeValue(new QName("class")); - String obExecutorPriority = executorElement.getAttributeValue(new QName("priority")); - - if (StringUtils.isEmpty(obExecutorClass)) { - //Throwing exceptions since we cannot proceed without invalid executor names - throw new OpenBankingRuntimeException("Executor class is not defined " + - "correctly in open-banking.xml"); - } - int priority = Integer.MAX_VALUE; - if (!StringUtils.isEmpty(obExecutorPriority)) { - priority = Integer.parseInt(obExecutorPriority); - } - executors.put(priority, obExecutorClass); - } - } - //Ordering the executors based on the priority number - LinkedHashMap priorityMap = executors.entrySet() - .stream() - .sorted(comparingByKey()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2, - LinkedHashMap::new)); - authorizeSteps.put(consentTypeName, priorityMap); - } - } - } - } - - /** - * Method to read text configs from xml when root element is given. - * - * @param serverConfig XML root element object - * @param nameStack stack of config names - */ - private void readChildElements(OMElement serverConfig, Stack nameStack) { - - for (Iterator childElements = serverConfig.getChildElements(); childElements.hasNext(); ) { - OMElement element = (OMElement) childElements.next(); - nameStack.push(element.getLocalName()); - if (elementHasText(element)) { - String key = getKey(nameStack); - Object currentObject = configuration.get(key); - String value = replaceSystemProperty(element.getText()); - if (secretResolver != null && secretResolver.isInitialized() && - secretResolver.isTokenProtected(key)) { - value = secretResolver.resolve(key); - } - if (currentObject == null) { - configuration.put(key, value); - } else if (currentObject instanceof ArrayList) { - ArrayList list = (ArrayList) currentObject; - if (!list.contains(value)) { - list.add(value); - configuration.put(key, list); - } - } else { - if (!value.equals(currentObject)) { - ArrayList arrayList = new ArrayList<>(2); - arrayList.add(currentObject); - arrayList.add(value); - configuration.put(key, arrayList); - } - } - } else if (OpenBankingConstants.REVOCATION_VALIDATORS_CONFIG_TAG.equalsIgnoreCase(element.getLocalName())) { - Iterator environmentIterator = element - .getChildrenWithLocalName(OpenBankingConstants.REVOCATION_VALIDATOR_CONFIG_TAG); - - while (environmentIterator.hasNext()) { - OMElement environmentElem = (OMElement) environmentIterator.next(); - String revocationType = environmentElem.getAttributeValue(new QName("type")); - Integer priority; - try { - priority = Integer.parseInt(environmentElem.getAttributeValue(new QName("priority"))); - } catch (NumberFormatException e) { - log.warn("Consent retrieval RevocationValidator " + revocationType.replaceAll("[\r\n]", "") - + " priority invalid. Hence skipped"); - continue; - } - revocationValidators.put(priority, revocationType); - } - } - readChildElements(element, nameStack); - nameStack.pop(); - } - } - - private void buildAllowedScopes() { - OMElement gatewayElement = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.GATEWAY_CONFIG_TAG)); - - if (gatewayElement != null) { - OMElement tppManagementElement = gatewayElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.TPP_MANAGEMENT_CONFIG_TAG)); - - if (tppManagementElement != null) { - OMElement allowedScopesElement = tppManagementElement.getFirstChildWithName(new QName( - OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.ALLOWED_SCOPES_CONFIG_TAG)); - - //obtaining each scope under allowed scopes - Iterator environmentIterator = - allowedScopesElement.getChildrenWithLocalName(OpenBankingConstants.SCOPE_CONFIG_TAG); - - while (environmentIterator.hasNext()) { - OMElement scopeElem = (OMElement) environmentIterator.next(); - String scopeName = scopeElem.getAttributeValue(new QName("name")); - String rolesStr = scopeElem.getAttributeValue(new QName("roles")); - if (StringUtils.isNotEmpty(rolesStr)) { - List rolesList = Arrays.stream(rolesStr.split(",")) - .map(String::trim) - .collect(Collectors.toList()); - allowedScopes.put(scopeName, rolesList); - } - } - } - } - } - - private void buildAllowedSubscriptions() { - - OMElement dcrElement = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.DCR_CONFIG_TAG)); - - if (dcrElement != null) { - OMElement regulatoryAPINames = dcrElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.REGULATORY_APINAMES)); - - if (regulatoryAPINames != null) { - - //obtaining each scope under allowed scopes - Iterator environmentIterator = - regulatoryAPINames.getChildrenWithLocalName(OpenBankingConstants.REGULATORY_API); - - while (environmentIterator.hasNext()) { - OMElement scopeElem = (OMElement) environmentIterator.next(); - String scopeName = scopeElem.getAttributeValue(new QName("name")); - String rolesStr = scopeElem.getAttributeValue(new QName("roles")); - if (StringUtils.isNotEmpty(rolesStr)) { - List rolesList = Arrays.stream(rolesStr.split(",")) - .map(String::trim) - .collect(Collectors.toList()); - allowedAPIs.put(scopeName, rolesList); - } - } - } - } - - } - - private void buildOBEventExecutors() { - - OMElement eventElement = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.EVENT_CONFIG_TAG)); - - if (eventElement != null) { - - OMElement openBankingEventExecutors = eventElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, - OpenBankingConstants.EVENT_EXECUTOR_CONFIG_TAG)); - - if (openBankingEventExecutors != null) { - //obtaining each executor element under EventExecutors tag - //Ordering the executors based on the priority number - Iterator eventExecutor = openBankingEventExecutors.getChildrenWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.EXECUTOR_CONFIG_TAG)); - if (eventExecutor != null) { - while (eventExecutor.hasNext()) { - OMElement executorElement = eventExecutor.next(); - //Retrieve class name and priority from executor config - String obExecutorClass = executorElement.getAttributeValue(new QName("class")); - String obExecutorPriority = executorElement.getAttributeValue(new QName("priority")); - - if (StringUtils.isEmpty(obExecutorClass)) { - //Throwing exceptions since we cannot proceed without invalid executor names - throw new OpenBankingRuntimeException("Event Executor class is not defined " + - "correctly in open-banking.xml"); - } - int priority = Integer.MAX_VALUE; - if (!StringUtils.isEmpty(obExecutorPriority)) { - priority = Integer.parseInt(obExecutorPriority); - } - obEventExecutors.put(priority, obExecutorClass); - } - } - //Ordering the executors based on the priority number - obEventExecutors = obEventExecutors.entrySet() - .stream() - .sorted(comparingByKey()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2, - LinkedHashMap::new)); - } - } - } - - /** - * Method to build configurations for Authentication Worker Extension point. - */ - private void buildWorkers() { - - OMElement workersOMEList = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.AUTHENTICATION_WORKER_LIST_TAG)); - - if (workersOMEList != null) { - Iterator workerConfigs = workersOMEList.getChildrenWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.AUTHENTICATION_WORKER_TAG)); - if (workerConfigs != null) { - while (workerConfigs.hasNext()) { - OMElement executorElement = workerConfigs.next(); - //Retrieve class name and implementation from executor config - String workerClass = executorElement.getAttributeValue(new QName("class")); - String workerName = executorElement.getAttributeValue(new QName("name")); - - if (StringUtils.isEmpty(workerClass) || StringUtils.isEmpty(workerName)) { - //Throwing exceptions since we cannot proceed without invalid worker names - throw new OpenBankingRuntimeException("Authentication worker class is not defined " + - "correctly in open-banking.xml"); - } - authWorkerConfig.put(workerName, workerClass); - } - } - } - } - - /** - * Method to obtain config key from stack. - * - * @param nameStack Stack of strings with names. - * @return key as a String - */ - private String getKey(Stack nameStack) { - - StringBuilder key = new StringBuilder(); - for (int index = 0; index < nameStack.size(); index++) { - String name = nameStack.elementAt(index); - key.append(name).append("."); - } - key.deleteCharAt(key.lastIndexOf(".")); - return key.toString(); - } - - /** - * Method to replace system properties in configs. - * - * @param text String that may require modification - * @return modified string - */ - private String replaceSystemProperty(String text) { - - int indexOfStartingChars = -1; - int indexOfClosingBrace; - - // The following condition deals with properties. - // Properties are specified as ${system.property}, - // and are assumed to be System properties - StringBuilder textBuilder = new StringBuilder(text); - while (indexOfStartingChars < textBuilder.indexOf("${") - && (indexOfStartingChars = textBuilder.indexOf("${")) != -1 - && (indexOfClosingBrace = textBuilder.indexOf("}")) != -1) { // Is a property used? - String sysProp = textBuilder.substring(indexOfStartingChars + 2, indexOfClosingBrace); - String propValue = System.getProperty(sysProp); - if (propValue != null) { - textBuilder = new StringBuilder(textBuilder.substring(0, indexOfStartingChars) + propValue - + textBuilder.substring(indexOfClosingBrace + 1)); - } - if (sysProp.equals(OpenBankingConstants.CARBON_HOME) && - System.getProperty(OpenBankingConstants.CARBON_HOME).equals(".")) { - textBuilder.insert(0, new File(".").getAbsolutePath() + File.separator); - } - } - return textBuilder.toString(); - } - - /** - * Method to check whether config element has text value. - * - * @param element root element as a object - * @return availability of text in the config - */ - private boolean elementHasText(OMElement element) { - - String text = element.getText(); - return text != null && text.trim().length() != 0; - } - - public Map> getOpenBankingExecutors() { - - return obExecutors; - } - - public Map getOpenBankingEventExecutors() { - - return obEventExecutors; - } - - public Map> getDataPublishingStreams() { - - return dataPublishingStreams; - } - - public Map> getDataPublishingValidationMap() { - - return dataPublishingValidationMap; - } - - public Map> getConsentAuthorizeSteps() { - - return authorizeSteps; - } - - public Map> getKeyManagerAdditionalProperties() { - - return keyManagerAdditionalProperties; - } - - /** - * Returns the element with the provided key. - * - * @param key local part name - * @return Corresponding value for key - */ - public Object getConfigElementFromKey(String key) { - - return configuration.get(key); - } - - public String getDataSourceName() { - - return getConfigElementFromKey(OpenBankingConstants.JDBC_PERSISTENCE_CONFIG) == null ? "" : - ((String) getConfigElementFromKey(OpenBankingConstants.JDBC_PERSISTENCE_CONFIG)).trim(); - } - - /** - * Returns the database connection verification timeout in seconds configured in open-banking.xml. - * - * @return 1 if nothing is configured - */ - public int getConnectionVerificationTimeout() { - - return getConfigElementFromKey(OpenBankingConstants.DB_CONNECTION_VERIFICATION_TIMEOUT) == null ? 1 : - Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.DB_CONNECTION_VERIFICATION_TIMEOUT).toString().trim()); - } - - /** - * Returns the retention datasource name configured in open-banking.xml. - * @return - */ - public String getRetentionDataSourceName() { - - return getConfigElementFromKey(OpenBankingConstants.JDBC_RETENTION_DATA_PERSISTENCE_CONFIG) == null ? "" : - ((String) getConfigElementFromKey(OpenBankingConstants.JDBC_RETENTION_DATA_PERSISTENCE_CONFIG)).trim(); - } - - /** - * Returns the retention database connection verification timeout in seconds configured in open-banking.xml. - * - * @return 1 if nothing is configured - */ - public int getRetentionDataSourceConnectionVerificationTimeout() { - - return getConfigElementFromKey(OpenBankingConstants.RETENTION_DATA_DB_CONNECTION_VERIFICATION_TIMEOUT) - == null ? 1 : Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.RETENTION_DATA_DB_CONNECTION_VERIFICATION_TIMEOUT).toString().trim()); - } - - /** - * Method to get isEnabled config for consent data retention feature. - * @return consent data retention is enabled - */ - public boolean isConsentDataRetentionEnabled() { - - return getConfigElementFromKey(OpenBankingConstants.IS_CONSENT_DATA_RETENTION_ENABLED) == null ? false : - (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.IS_CONSENT_DATA_RETENTION_ENABLED).toString().trim())); - } - - - /** - * Method to get isEnabled config for consent data retention periodical job. - * @return consent data retention is enabled - */ - public boolean isRetentionDataDBSyncEnabled() { - - return getConfigElementFromKey(OpenBankingConstants.IS_CONSENT_RETENTION_DATA_DB_SYNC_ENABLED) == null ? false : - (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.IS_CONSENT_RETENTION_DATA_DB_SYNC_ENABLED).toString().trim())); - } - - - /** - * Method to get configs for data retention db sync periodical job's cron value. - * @return data retention job's cron string - */ - public String getRetentionDataDBSyncCronExpression() { - - return getConfigElementFromKey(OpenBankingConstants.CONSENT_RETENTION_DATA_DB_SYNC_CRON) == null - ? OpenBankingConstants.DEFAULT_MIDNIGHT_CRON : - ((String) getConfigElementFromKey(OpenBankingConstants.CONSENT_RETENTION_DATA_DB_SYNC_CRON)).trim(); - } - - /** - * Truststore dynamic loading interval. - * - * @return truststore dynamic loading time in seconds - */ - public Long getTruststoreDynamicLoadingInterval() { - try { - Object truststoreDynamicLoadingInterval = - getConfigElementFromKey(OpenBankingConstants.TRUSTSTORE_DYNAMIC_LOADING_INTERVAL); - if (truststoreDynamicLoadingInterval != null) { - return Long.parseLong((String) truststoreDynamicLoadingInterval); - } else { - return Long.parseLong("86400"); - } - } catch (NumberFormatException e) { - throw new NumberFormatException("Error occurred while reading the truststore dynamic loading interval " + - "value in open-banking.xml. " + e.getMessage()); - } - } - - /** - * Returns the revocation validators map. - *

- * The revocation validator map contains revocation type (OCSP/CRL) and its executing priority. - * The default priority value has set as 1 for OCSP type, as OCSP validation is faster than the CRL validation - * - * @return certificate revocation validators map - */ - public Map getCertificateRevocationValidators() { - return revocationValidators; - } - - public Map> getOpenBankingDCRRegistrationParams() { - return dcrRegistrationConfigs; - } - - public String getAuthServletExtension() { - return getConfigElementFromKey(OpenBankingConstants.AUTH_SERVLET_EXTENSION) == null ? "" : - ((String) getConfigElementFromKey(OpenBankingConstants.AUTH_SERVLET_EXTENSION)).trim(); - } - - public String getCibaServletExtension() { - return getConfigElementFromKey(OpenBankingConstants.CIBA_SERVLET_EXTENSION) == null ? "" : - ((String) getConfigElementFromKey(OpenBankingConstants.CIBA_SERVLET_EXTENSION)).trim(); - } - - public String getJWKSConnectionTimeOut() { - - return getConfigElementFromKey(OpenBankingConstants.DCR_JWKS_CONNECTION_TIMEOUT) == null ? "3000" : - ((String) getConfigElementFromKey(OpenBankingConstants.DCR_JWKS_CONNECTION_TIMEOUT)).trim(); - } - - public String getJWKSReadTimeOut() { - - return getConfigElementFromKey(OpenBankingConstants.DCR_JWKS_READ_TIMEOUT) == null ? "3000" : - ((String) getConfigElementFromKey(OpenBankingConstants.DCR_JWKS_READ_TIMEOUT)).trim(); - } - - public String getSPMetadataFilterExtension() { - return getConfigElementFromKey(OpenBankingConstants.SP_METADATA_FILTER_EXTENSION) == null ? "" : - ((String) getConfigElementFromKey(OpenBankingConstants.SP_METADATA_FILTER_EXTENSION)).trim(); - } - - public Map> getAllowedScopes() { - return allowedScopes; - } - - public Map> getAllowedAPIs() { - return allowedAPIs; - } - - /** - * Method to get configs for periodical consent expiration job's cron value. - * @return consent expiration job's cron string - */ - public String getConsentExpiryCronExpression() { - - return getConfigElementFromKey(OpenBankingConstants.CONSENT_PERIODICAL_EXPIRATION_CRON) == null - ? OpenBankingConstants.DEFAULT_MIDNIGHT_CRON : - ((String) getConfigElementFromKey(OpenBankingConstants.CONSENT_PERIODICAL_EXPIRATION_CRON)).trim(); - } - - /** - * Method to get statue for expired consents. - * @return statue for expired consents - */ - public String getStatusWordingForExpiredConsents() { - - return getConfigElementFromKey(OpenBankingConstants.STATUS_FOR_EXPIRED_CONSENT) == null - ? OpenBankingConstants.DEFAULT_STATUS_FOR_EXPIRED_CONSENTS : - ((String) getConfigElementFromKey(OpenBankingConstants.STATUS_FOR_EXPIRED_CONSENT)).trim(); - } - - /** - * Method to get eligible statues for evaluate expiration logic. - * @return eligible statues for evaluate expiration logic - */ - public String getEligibleStatusesForConsentExpiry() { - - return getConfigElementFromKey(OpenBankingConstants.ELIGIBLE_STATUSES_FOR_CONSENT_EXPIRY) == null ? "" : - ((String) getConfigElementFromKey(OpenBankingConstants.ELIGIBLE_STATUSES_FOR_CONSENT_EXPIRY)).trim(); - } - - /** - * Method to get isEnabled config for periodical consent expiration job. - * @return consent expiration job is enabled - */ - public boolean isConsentExpirationPeriodicalJobEnabled() { - - return getConfigElementFromKey(OpenBankingConstants.IS_CONSENT_PERIODICAL_EXPIRATION_ENABLED) == null ? false : - (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.IS_CONSENT_PERIODICAL_EXPIRATION_ENABLED).toString().trim())); - } - - public boolean isConsentAmendmentHistoryEnabled() { - - return getConfigElementFromKey(OpenBankingConstants.IS_CONSENT_AMENDMENT_HISTORY_ENABLED) == null ? false : - (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.IS_CONSENT_AMENDMENT_HISTORY_ENABLED).toString().trim())); - } - - public String getOBKeyManagerExtensionImpl() { - return getConfigElementFromKey(OpenBankingConstants.OB_KEYMANAGER_EXTENSION_IMPL) == null ? "" : - ((String) getConfigElementFromKey(OpenBankingConstants.OB_KEYMANAGER_EXTENSION_IMPL)) - .trim(); - } - - /** - * ConnectionPool maximum connection count. - * - * @return maximum connections count, default value is 2000 - */ - public int getConnectionPoolMaxConnections() { - try { - Object maxConnectionsCount = - getConfigElementFromKey(OpenBankingConstants.CONNECTION_POOL_MAX_CONNECTIONS); - if (maxConnectionsCount != null) { - return Integer.parseInt(String.valueOf(maxConnectionsCount)); - } else { - return 2000; - } - } catch (NumberFormatException e) { - throw new NumberFormatException("Error occurred while reading the MaxConnections " + - "value in open-banking.xml. " + e.getMessage()); - } - } - - /** - * ConnectionPool maximum connection per route count. - * - * @return maximum connections per route value, default value is 1500 - */ - public int getConnectionPoolMaxConnectionsPerRoute() { - try { - Object maxConnectionsPerRouteCount = - getConfigElementFromKey(OpenBankingConstants.CONNECTION_POOL_MAX_CONNECTIONS_PER_ROUTE); - if (maxConnectionsPerRouteCount != null) { - return Integer.parseInt(String.valueOf(maxConnectionsPerRouteCount)); - } else { - return 1500; - } - } catch (NumberFormatException e) { - throw new NumberFormatException("Error occurred while reading the MaxConnectionsPerRoute " + - "value in open-banking.xml. " + e.getMessage()); - } - } - - private void buildServiceActivatorSubscribers() { - OMElement serviceActivatorElement = rootElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.SERVICE_ACTIVATOR_TAG)); - - if (serviceActivatorElement != null) { - OMElement subscribers = serviceActivatorElement.getFirstChildWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.SA_SUBSCRIBERS_TAG)); - - if (subscribers != null) { - Iterator subscriber = subscribers.getChildrenWithName( - new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.SA_SUBSCRIBER_TAG)); - if (subscriber != null) { - while (subscriber.hasNext()) { - OMElement executorElement = subscriber.next(); - //Retrieve subscriber class name from service activator configs - final String subscriberClass = executorElement.getText(); - - if (!StringUtils.isEmpty(subscriberClass)) { - serviceActivatorSubscribers.add(subscriberClass); - } - } - } - } - } - } - - /** - * Returns a list of FQNs of the OBServiceObserver interface implementations. - * - * @return ServiceActivator subscribers FQNs. - */ - public List getServiceActivatorSubscribers() { - return serviceActivatorSubscribers; - } - - //Event notifications configurations. - public String getEventNotificationTokenIssuer() { - - return getConfigElementFromKey(OpenBankingConstants.TOKEN_ISSUER) == null ? "www.wso2.com" : - ((String) getConfigElementFromKey(OpenBankingConstants.TOKEN_ISSUER)).trim(); - } - - public int getNumberOfSetsToReturn() { - - return getConfigElementFromKey(OpenBankingConstants.MAX_SETS_TO_RETURN) == null ? 5 : - Integer.parseInt((String) getConfigElementFromKey(OpenBankingConstants.MAX_SETS_TO_RETURN)); - } - - public boolean isSubClaimIncluded() { - - return getConfigElementFromKey(OpenBankingConstants.IS_SUB_CLAIM_INCLUDED) == null ? false : - (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.IS_SUB_CLAIM_INCLUDED).toString().trim())); - } - - public boolean isToeClaimIncluded() { - return getConfigElementFromKey(OpenBankingConstants.IS_TOE_CLAIM_INCLUDED) == null ? false : - (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.IS_TOE_CLAIM_INCLUDED).toString().trim())); - } - - public boolean isTxnClaimIncluded() { - return getConfigElementFromKey(OpenBankingConstants.IS_TXN_CLAIM_INCLUDED) == null ? false : - (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.IS_TXN_CLAIM_INCLUDED).toString().trim())); - } - - /** - * Returns the expiry time for cache modification. - * - * @return String Expiry time. - */ - public String getCommonCacheModifiedExpiryTime() { - - return getConfigElementFromKey(OpenBankingConstants.COMMON_IDENTITY_CACHE_MODIFY_EXPIRY) == null ? "60" : - ((String) getConfigElementFromKey(OpenBankingConstants.COMMON_IDENTITY_CACHE_MODIFY_EXPIRY)).trim(); - } - - /** - * Returns the expiry time for cache access. - * - * @return String Expiry time. - */ - public String getCommonCacheAccessExpiryTime() { - return getConfigElementFromKey(OpenBankingConstants.COMMON_IDENTITY_CACHE_ACCESS_EXPIRY) == null ? "60" : - ((String) getConfigElementFromKey(OpenBankingConstants.COMMON_IDENTITY_CACHE_ACCESS_EXPIRY)).trim(); - } - - /** - * Alias of the signing certificate in Production Environment. - * - * @return signing certificate alias - */ - public String getOBIdnRetrieverSigningCertificateAlias() { - - return getConfigElementFromKey(OpenBankingConstants.OB_IDN_RETRIEVER_SIG_ALIAS) == null ? "wso2carbon" : - ((String) getConfigElementFromKey(OpenBankingConstants.OB_IDN_RETRIEVER_SIG_ALIAS)).trim(); - } - - /** - * Alias of the signing certificate in Sandbox Environment. - * - * @return signing certificate alias - */ - public String getOBIdnRetrieverSandboxSigningCertificateAlias() { - - return getConfigElementFromKey(OpenBankingConstants.OB_IDN_RETRIEVER_SANDBOX_SIG_ALIAS) == null ? "wso2carbon" : - ((String) getConfigElementFromKey(OpenBankingConstants.OB_IDN_RETRIEVER_SANDBOX_SIG_ALIAS)).trim(); - } - - /** - * Key ID of the public key of the corresponding private key used for signing. - * - * @return signing certificate Kid in Production environment - */ - public String getOBIdnRetrieverSigningCertificateKid() { - - return getConfigElementFromKey(OpenBankingConstants.OB_IDN_RETRIEVER_SIG_KID) == null ? "1234" : - ((String) getConfigElementFromKey(OpenBankingConstants.OB_IDN_RETRIEVER_SIG_KID)).trim(); - } - - /** - * Key ID of the public key of the corresponding private key used for signing. - * - * @return signing certificate Kid in sandbox environment - */ - public String getOBIdnRetrieverSandboxCertificateKid() { - - return getConfigElementFromKey(OpenBankingConstants.OB_IDN_RETRIEVER_SANDBOX_KID) == null ? "5678" : - ((String) getConfigElementFromKey(OpenBankingConstants.OB_IDN_RETRIEVER_SANDBOX_KID)).trim(); - } - - /** - * JWKS Retriever Size Limit for JWS Signature Handling. - * - * @return - */ - public String getJwksRetrieverSizeLimit() { - - return getConfigElementFromKey(OpenBankingConstants.JWKS_RETRIEVER_SIZE_LIMIT) == null ? "51200" : - ((String) getConfigElementFromKey(OpenBankingConstants.JWKS_RETRIEVER_SIZE_LIMIT)).trim(); - } - - /** - * JWKS Retriever Connection Timeout for JWS Signature Handling. - * - * @return - */ - public String getJwksRetrieverConnectionTimeout() { - - return getConfigElementFromKey(OpenBankingConstants.JWKS_RETRIEVER_CONN_TIMEOUT) == null ? "2000" : - ((String) getConfigElementFromKey(OpenBankingConstants.JWKS_RETRIEVER_CONN_TIMEOUT)).trim(); - } - - /** - * JWKS Retriever Read Timeout for JWS Signature Handling. - * - * @return - */ - public String getJwksRetrieverReadTimeout() { - - return getConfigElementFromKey(OpenBankingConstants.JWKS_RETRIEVER_READ_TIMEOUT) == null ? "2000" : - ((String) getConfigElementFromKey(OpenBankingConstants.JWKS_RETRIEVER_READ_TIMEOUT)).trim(); - } - - /** - * Check if Jws Signature Validation is enabled. - * - * @return if Jws Signature Validation is enabled - */ - public boolean isJwsSignatureValidationEnabled() { - - return getConfigElementFromKey(OpenBankingConstants.JWS_SIG_VALIDATION_ENABLE) != null && - Boolean.parseBoolean(((String) getConfigElementFromKey(OpenBankingConstants.JWS_SIG_VALIDATION_ENABLE)) - .trim()); - } - - /** - * Check if Jws Response signing is enabled. - * - * @return if Jws message Response is enabled - */ - public boolean isJwsResponseSigningEnabled() { - - return getConfigElementFromKey(OpenBankingConstants.JWS_RESP_SIGNING_ENABLE) != null && - Boolean.parseBoolean(((String) getConfigElementFromKey(OpenBankingConstants.JWS_RESP_SIGNING_ENABLE)) - .trim()); - } - - /** - * Jws Request Signing allowed algorithms. - * - * @return - */ - public List getJwsRequestSigningAlgorithms() { - - Object allowedAlgorithmsElement = getConfigElementFromKey( - OpenBankingConstants.JWS_SIG_VALIDATION_ALGO) == null ? new String[] {"PS256"} : - (getConfigElementFromKey(OpenBankingConstants.JWS_SIG_VALIDATION_ALGO)); - List allowedAlgorithmsList = new ArrayList<>(); - if (allowedAlgorithmsElement instanceof ArrayList) { - allowedAlgorithmsList.addAll((ArrayList) allowedAlgorithmsElement); - } else if (allowedAlgorithmsElement instanceof String) { - allowedAlgorithmsList.add((String) allowedAlgorithmsElement); - } - return allowedAlgorithmsList.isEmpty() ? Arrays.asList("PS256") : allowedAlgorithmsList; - } - - /** - * Jws Response Signing allowed algorithm. - * - * @return - */ - public String getJwsResponseSigningAlgorithm() { - - return getConfigElementFromKey(OpenBankingConstants.JWS_RESP_SIGNING_ALGO) == null ? "PS256" : - ((String) getConfigElementFromKey(OpenBankingConstants.JWS_RESP_SIGNING_ALGO)).trim(); - } - - public Map getAuthWorkerConfig() { - return authWorkerConfig; - } - - /** - * Method to check if the Dispute Resolution feature is enabled. - * @return true if Dispute Resolution is enabled. - */ - public boolean isDisputeResolutionEnabled() { - - return getConfigElementFromKey(OpenBankingConstants.IS_DISPUTE_RESOLUTION_ENABLED) == null ? false : - (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.IS_DISPUTE_RESOLUTION_ENABLED).toString().trim())); - } - - /** - * Method to check if the Dispute Resolution feature is enabled for Non Error Scenarios. - * @return true if Dispute Resolution feature is enabled for Non Error scenarios - */ - public boolean isNonErrorDisputeDataPublishingEnabled() { - - return getConfigElementFromKey(OpenBankingConstants.PUBLISH_NON_ERROR_DISPUTE_DATA) == null ? false : - (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.PUBLISH_NON_ERROR_DISPUTE_DATA).toString().trim())); - } - - /** - * Method to get maximum length for publish response body in Dispute Resolution Feature. - * @return maximum length for response body. - */ - public int getMaxResponseBodyLength() { - - return getConfigElementFromKey(OpenBankingConstants.MAX_RESPONSE_BODY_LENGTH) - == null ? 4096 : (Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.MAX_RESPONSE_BODY_LENGTH).toString().trim())); - } - - /** - * Method to get maximum length for publish request body in Dispute Resolution Feature. - * @return maximum length for request body. - */ - public int getMaxRequestBodyLength() { - - return getConfigElementFromKey(OpenBankingConstants.MAX_REQUEST_BODY_LENGTH) - == null ? 4096 : (Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.MAX_REQUEST_BODY_LENGTH).toString().trim())); - } - - /** - *Method to get maximum length for publish headers in Dispute Resolution Feature. - * @return maximum length for headers. - */ - public int getMaxHeaderLength() { - - return getConfigElementFromKey(OpenBankingConstants.MAX_HEADER_LENGTH) - == null ? 2048 : (Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.MAX_HEADER_LENGTH).toString().trim())); - } - - /** - * Method to determine real-time event notification feature is enabled or not from the configurations. - * - * @return boolean value indicating the state - */ - public boolean isRealtimeEventNotificationEnabled() { - return getConfigElementFromKey(OpenBankingConstants.REALTIME_EVENT_NOTIFICATION_ENABLED) != null - && (Boolean.parseBoolean(getConfigElementFromKey( - OpenBankingConstants.REALTIME_EVENT_NOTIFICATION_ENABLED).toString().trim())); - } - - /** - * Method to get periodic Cron expression config for realtime event notifications scheduler. - * - * @return String Cron expression to trigger the Cron job for real-time event notification - */ - public String getRealtimeEventNotificationSchedulerCronExpression() { - return getConfigElementFromKey(OpenBankingConstants.PERIODIC_CRON_EXPRESSION) - == null ? "0 0/1 0 ? * * *" : (String) getConfigElementFromKey( - OpenBankingConstants.PERIODIC_CRON_EXPRESSION); - } - - /** - * Method to get TIMEOUT_IN_SECONDS config for realtime event notifications. - * - * @return integer timeout for the HTTP Client's POST requests - */ - public int getRealtimeEventNotificationTimeoutInSeconds() { - return getConfigElementFromKey(OpenBankingConstants.TIMEOUT_IN_SECONDS) - == null ? 60 : (Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.TIMEOUT_IN_SECONDS).toString().trim())); - } - - /** - * Method to get MAX_RETRIES config for realtime event notifications. - * - * @return integer maximum number of retries to the retry policy in real-time notification sender - */ - public int getRealtimeEventNotificationMaxRetries() { - return getConfigElementFromKey(OpenBankingConstants.MAX_RETRIES) - == null ? 5 : (Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.MAX_RETRIES).toString().trim())); - } - - /** - * Method to get INITIAL_BACKOFF_TIME_IN_SECONDS config for realtime event notifications. - * - * @return integer start waiting time for the retry policy before the first retry - */ - public int getRealtimeEventNotificationInitialBackoffTimeInSeconds() { - return getConfigElementFromKey(OpenBankingConstants.INITIAL_BACKOFF_TIME_IN_SECONDS) - == null ? 60 : (Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.INITIAL_BACKOFF_TIME_IN_SECONDS).toString().trim())); - } - - /** - * Method to get BACKOFF_FUNCTION config for realtime event notifications. - * Function name should be "EX", "CONSTANT" or "LINEAR". - * - * @return string indicating the retry function - */ - public String getRealtimeEventNotificationBackoffFunction() { - return getConfigElementFromKey(OpenBankingConstants.BACKOFF_FUNCTION) - == null ? "EX" : (String) getConfigElementFromKey( - OpenBankingConstants.BACKOFF_FUNCTION); - } - - /** - * Method to get CIRCUIT_BREAKER_OPEN_TIMEOUT_IN_SECONDS config for realtime event notifications. - * - * @return integer timeout to break the retrying process and make that notification as ERR - */ - public int getRealtimeEventNotificationCircuitBreakerOpenTimeoutInSeconds() { - return getConfigElementFromKey(OpenBankingConstants.CIRCUIT_BREAKER_OPEN_TIMEOUT_IN_SECONDS) - == null ? 600 : (Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.CIRCUIT_BREAKER_OPEN_TIMEOUT_IN_SECONDS).toString().trim())); - } - - /** - * Method to get EVENT_NOTIFICATION_THREADPOOL_SIZE config for realtime event notifications. - * - * @return integer fix size to set the Thread Pool size in the real-time event notification sender - */ - public int getEventNotificationThreadpoolSize() { - return getConfigElementFromKey(OpenBankingConstants.EVENT_NOTIFICATION_THREADPOOL_SIZE) - == null ? 20 : (Integer.parseInt(getConfigElementFromKey( - OpenBankingConstants.EVENT_NOTIFICATION_THREADPOOL_SIZE).toString().trim())); - } - - /** - * Method to get EVENT_NOTIFICATION_GENERATOR config for event notifications. - * - * @return String class name of the event notification generator to generate the event notification payload - */ - public String getEventNotificationGenerator() { - return getConfigElementFromKey(OpenBankingConstants.EVENT_NOTIFICATION_GENERATOR) == null ? - "com.wso2.openbanking.accelerator.event.notifications.service.service.DefaultEventNotificationGenerator" - : (String) getConfigElementFromKey(OpenBankingConstants.EVENT_NOTIFICATION_GENERATOR); - } - - /** - * Method to get REALTIME_EVENT_NOTIFICATION_REQUEST_GENERATOR config for realtime event notifications. - * - * @return String class path of the realtime event notification payload generator - */ - public String getRealtimeEventNotificationRequestGenerator() { - return getConfigElementFromKey(OpenBankingConstants.REALTIME_EVENT_NOTIFICATION_REQUEST_GENERATOR) == null ? - "com.wso2.openbanking.accelerator.event.notifications.service." + - "realtime.service.DefaultRealtimeEventNotificationRequestGenerator" - : (String) getConfigElementFromKey(OpenBankingConstants.REALTIME_EVENT_NOTIFICATION_REQUEST_GENERATOR); - } - - /** - * Method to get software environment identification SSA property name. - * - * @return String software environment identification SSA property name. - */ - public String getSoftwareEnvIdentificationSSAPropertyName() { - return getConfigElementFromKey(OpenBankingConstants.DCR_SOFTWARE_ENV_IDENTIFICATION_PROPERTY_NAME) == null ? - OpenBankingConstants.SOFTWARE_ENVIRONMENT : (String) getConfigElementFromKey( - OpenBankingConstants.DCR_SOFTWARE_ENV_IDENTIFICATION_PROPERTY_NAME); - } - - /** - * Method to get software environment identification value for sandbox in SSA. - * - * @return String software environment identification value for sandbox. - */ - public String getSoftwareEnvIdentificationSSAPropertyValueForSandbox() { - return getConfigElementFromKey(OpenBankingConstants.DCR_SOFTWARE_ENV_IDENTIFICATION_VALUE_FOR_SANDBOX) == null ? - "sandbox" : (String) getConfigElementFromKey( - OpenBankingConstants.DCR_SOFTWARE_ENV_IDENTIFICATION_VALUE_FOR_SANDBOX); - } - - /** - * Method to get software environment identification value for production in SSA. - * - * @return String software environment identification value for production. - */ - public String getSoftwareEnvIdentificationSSAPropertyValueForProduction() { - return getConfigElementFromKey( - OpenBankingConstants.DCR_SOFTWARE_ENV_IDENTIFICATION_VALUE_FOR_PRODUCTION) == null ? - "production" : (String) getConfigElementFromKey( - OpenBankingConstants.DCR_SOFTWARE_ENV_IDENTIFICATION_VALUE_FOR_PRODUCTION); - } - - /** - * Get config related for checking whether PSU is a federated user or not. - * - * @return Boolean value indicating whether PSU is a federated user or not - */ - public boolean isPSUFederated() { - - Object isPSUFederated = getConfigElementFromKey(OpenBankingConstants.IS_PSU_FEDERATED); - if (isPSUFederated != null) { - return Boolean.parseBoolean((String) isPSUFederated); - } else { - return false; - } - } - - /** - * Get Federated PSU IDP Name. - * - * @return String Federated IDP name - */ - public String getFederatedIDPName() { - - return getConfigElementFromKey(OpenBankingConstants.PSU_FEDERATED_IDP_NAME) == null ? "" : - ((String) getConfigElementFromKey(OpenBankingConstants.PSU_FEDERATED_IDP_NAME)).trim(); - } - - /** - * Method to get the value Idempotency enable configuration. - * @return - */ - public boolean isIdempotencyValidationEnabled() { - return getConfigElementFromKey(OpenBankingConstants.IDEMPOTENCY_IS_ENABLED) != null && - Boolean.parseBoolean(((String) - getConfigElementFromKey(OpenBankingConstants.IDEMPOTENCY_IS_ENABLED)).trim()); - } - - /** - * Method to get the value Idempotency allowed time configuration. - * @return - */ - public String getIdempotencyAllowedTime() { - return getConfigElementFromKey(OpenBankingConstants.IDEMPOTENCY_ALLOWED_TIME) == null ? "1440" : - (String) getConfigElementFromKey(OpenBankingConstants.IDEMPOTENCY_ALLOWED_TIME); - } - -} diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java deleted file mode 100644 index 6f4a6e3f..00000000 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.wso2.openbanking.accelerator.common.constant; - - -/** - * Class containing the constants for Open Banking Common module. - */ -public class OpenBankingConstants { - - public static final String OB_CONFIG_FILE = "open-banking.xml"; - public static final String CARBON_HOME = "carbon.home"; - - public static final String OB_CONFIG_QNAME = "http://wso2.org/projects/carbon/open-banking.xml"; - public static final String GATEWAY_CONFIG_TAG = "Gateway"; - public static final String GATEWAY_EXECUTOR_CONFIG_TAG = "OpenBankingGatewayExecutors"; - public static final String EVENT_CONFIG_TAG = "Event"; - public static final String EVENT_EXECUTOR_CONFIG_TAG = "EventExecutors"; - public static final String EXECUTOR_CONFIG_TAG = "Executor"; - public static final String DCR_CONFIG_TAG = "DCR"; - public static final String DCR_REGISTRATION_CONFIG_TAG = "RegistrationRequestParams"; - public static final String DCR_REGISTRATION_PARAM_ALLOWED_VALUE_TAG = "AllowedValues"; - public static final String REGULATORY = "regulatory"; - public static final String DATA_PUBLISHING_CONFIG_TAG = "DataPublishing"; - public static final String THRIFT_CONFIG_TAG = "Thrift"; - public static final String STREAMS_CONFIG_TAG = "Streams"; - public static final String ATTRIBUTE_CONFIG_TAG = "Attribute"; - public static final String REQUIRED = "required"; - public static final String ATTRIBUTE_TYPE = "type"; - public static final String DEFAULT_MIDNIGHT_CRON = "0 0 0 * * ?"; - public static final String DEFAULT_STATUS_FOR_EXPIRED_CONSENTS = "Expired"; - public static final String DEFAULT_STATUS_FOR_REVOKED_CONSENTS = "Revoked"; - public static final String IS_CONSENT_REVOCATION_FLOW = "IS_CONSENT_REVOCATION_FLOW"; - - public static final String SIGNATURE_ALGORITHMS = "SignatureValidation.AllowedAlgorithms.Algorithm"; - public static final String AUTH_SERVLET_EXTENSION = "Identity.AuthenticationWebApp.ServletExtension"; - public static final String COMMON_IDENTITY_CACHE_ACCESS_EXPIRY = "Common.Identity.Cache.CacheAccessExpiry"; - public static final String COMMON_IDENTITY_CACHE_MODIFY_EXPIRY = "Common.Identity.Cache.CacheModifiedExpiry"; - public static final String JWKS_ENDPOINT_NAME = "DCR.JWKSEndpointName"; - public static final String SP_METADATA_FILTER_EXTENSION = - "Identity.ApplicationInformationEndpoint.SPMetadataFilterExtension"; - public static final String CIBA_SERVLET_EXTENSION = "Identity.CIBAAuthenticationEndpointWebApp.ServletExtension"; - public static final String DCR_JWKS_CONNECTION_TIMEOUT = "DCR.JWKS-Retriever.ConnectionTimeout"; - public static final String DCR_JWKS_READ_TIMEOUT = "DCR.JWKS-Retriever.ReadTimeout"; - public static final String DCR_USE_SOFTWAREID_AS_APPNAME = "DCR.UseSoftwareIdAsAppName"; - public static final String DCR_JWKS_NAME = "DCR.JWKSEndpointName"; - public static final String DCR_APPLICATION_NAME_KEY = "DCR.ApplicationName"; - public static final String OB_KM_NAME = "KeyManagerName"; - public static final String DCR_SOFTWARE_ENV_IDENTIFICATION_PROPERTY_NAME = - "DCR.RegistrationRequestParams.SoftwareEnvironmentIdentification.PropertyName"; - public static final String DCR_SOFTWARE_ENV_IDENTIFICATION_VALUE_FOR_SANDBOX = - "DCR.RegistrationRequestParams.SoftwareEnvironmentIdentification.PropertyValueForSandbox"; - public static final String DCR_SOFTWARE_ENV_IDENTIFICATION_VALUE_FOR_PRODUCTION = - "DCR.RegistrationRequestParams.SoftwareEnvironmentIdentification.PropertyValueForProduction"; - - public static final String APIM_APPCREATION = "DCR.APIMRESTEndPoints.AppCreation"; - public static final String APIM_KEYGENERATION = "DCR.APIMRESTEndPoints.KeyGeneration"; - public static final String APIM_GETAPIS = "DCR.APIMRESTEndPoints.RetrieveAPIS"; - public static final String APIM_SUBSCRIBEAPIS = "DCR.APIMRESTEndPoints.SubscribeAPIs"; - public static final String APIM_GETSUBSCRIPTIONS = "DCR.APIMRESTEndPoints.RetrieveSubscribedAPIs"; - public static final String REGULATORY_APINAMES = "RegulatoryAPINames"; - public static final String REGULATORY_API = "API"; - public static final String SOFTWARE_ROLES = "software_roles"; - public static final String SOFTWARE_STATEMENT = "software_statement"; - public static final String SOFTWARE_ENVIRONMENT = "software_environment"; - public static final String TOKEN_ENDPOINT = "DCR.TokenEndpoint"; - public static final String STORE_HOSTNAME = "PublisherURL"; - - public static final String JDBC_PERSISTENCE_CONFIG = "JDBCPersistenceManager.DataSource.Name"; - public static final String DB_CONNECTION_VERIFICATION_TIMEOUT = - "JDBCPersistenceManager.ConnectionVerificationTimeout"; - public static final String JDBC_RETENTION_DATA_PERSISTENCE_CONFIG = - "JDBCRetentionDataPersistenceManager.DataSource.Name"; - public static final String RETENTION_DATA_DB_CONNECTION_VERIFICATION_TIMEOUT = - "JDBCRetentionDataPersistenceManager.ConnectionVerificationTimeout"; - - public static final String TRUSTSTORE_CONF_TYPE_DEFAULT = "JKS"; - public static final String CLIENT_CERT_CACHE = "ClientCertCache"; - public static final String OB_CACHE_MANAGER = "OB_CERTIFICATE_CACHE"; - public static final String CERTIFICATE_REVOCATION_VALIDATION_RETRY_COUNT = "Gateway" + - ".CertificateManagement.CertificateRevocationValidationRetryCount"; - public static final String CERTIFICATE_REVOCATION_VALIDATION_CONNECT_TIMEOUT = "Gateway" + - ".CertificateManagement.CertificateRevocationValidationConnectTimeout"; - public static final String CERTIFICATE_REVOCATION_VALIDATION_CONNECTION_REQUEST_TIMEOUT = "Gateway" + - ".CertificateManagement.CertificateRevocationValidationConnectionRequestTimeout"; - public static final String CERTIFICATE_REVOCATION_VALIDATION_SOCKET_TIMEOUT = "Gateway" + - ".CertificateManagement.CertificateRevocationValidationSocketTimeout"; - public static final String CERTIFICATE_REVOCATION_VALIDATION_ENABLED = "Gateway" + - ".CertificateManagement.CertificateRevocationValidationEnabled"; - public static final String CERTIFICATE_REVOCATION_VALIDATION_EXCLUDED_ISSUERS = "Gateway" + - ".CertificateManagement.RevocationValidationExcludedIssuers.IssuerDN"; - public static final String TPP_VALIDATION_SERVICE_IMPL_CLASS = "Gateway" + - ".TPPManagement.TPPValidation.ServiceImplClass"; - public static final String TPP_VALIDATION_ENABLED = "Gateway" + - ".TPPManagement.TPPValidation.Enabled"; - public static final String PSD2_ROLE_VALIDATION_ENABLED = "Gateway" + - ".TPPManagement.PSD2RoleValidation.Enabled"; - public static final String CERTIFICATE_REVOCATION_PROXY_ENABLED = "Gateway" + - ".CertificateManagement.CertificateRevocationProxy.Enabled"; - public static final String CERTIFICATE_REVOCATION_PROXY_HOST = "Gateway" + - ".CertificateManagement.CertificateRevocationProxy.ProxyHost"; - public static final String CERTIFICATE_REVOCATION_PROXY_PORT = "Gateway" + - ".CertificateManagement.CertificateRevocationProxy.ProxyPort"; - public static final String TRANSPORT_CERT_ISSUER_VALIDATION_ENABLED = "Gateway" + - ".CertificateManagement.TransportCertIssuerValidationEnabled"; - public static final String TRUSTSTORE_DYNAMIC_LOADING_INTERVAL = "Gateway" + - ".CertificateManagement.TrustStoreDynamicLoadingInterval"; - public static final String CLIENT_CERTIFICATE_CACHE_EXPIRY = "Gateway" + - ".CertificateManagement.ClientCertificateCacheExpiry"; - public static final String TPP_VALIDATION_CACHE_EXPIRY = "Gateway" + - ".TPPManagement.TPPValidationCacheExpiry"; - public static final String TPP_VALIDATION_SERVICE_AISP_SCOPE_REGEX = "Gateway" + - ".CertificateManagement.TPPValidationService.ScopeRegexPatterns.AISP"; - public static final String TPP_VALIDATION_SERVICE_PISP_SCOPE_REGEX = "Gateway" + - ".CertificateManagement.TPPValidationService.ScopeRegexPatterns.PISP"; - public static final String TPP_VALIDATION_SERVICE_CBPII_SCOPE_REGEX = "Gateway" + - ".CertificateManagement.TPPValidationService.ScopeRegexPatterns.CBPII"; - public static final int PAGINATION_LIMIT_DEFAULT = 25; - public static final int PAGINATION_OFFSET_DEFAULT = 0; - public static final String CONSENT_CONFIG_TAG = "Consent"; - public static final String AUTHORIZE_STEPS_CONFIG_TAG = "AuthorizeSteps"; - public static final String STEP_CONFIG_TAG = "Step"; - public static final String ALLOWED_SCOPES_CONFIG_TAG = "AllowedScopes"; - public static final String SCOPE_CONFIG_TAG = "Scope"; - public static final String REVOCATION_VALIDATORS_CONFIG_TAG = "RevocationValidators"; - public static final String REVOCATION_VALIDATOR_CONFIG_TAG = "RevocationValidator"; - public static final String TPP_MANAGEMENT_CONFIG_TAG = "TPPManagement"; - public static final String CONNECTION_POOL_MAX_CONNECTIONS = "HTTPConnectionPool.MaxConnections"; - public static final String CONNECTION_POOL_MAX_CONNECTIONS_PER_ROUTE = "HTTPConnectionPool.MaxConnectionsPerRoute"; - public static final String PUSH_AUTH_EXPIRY_TIME = "PushAuthorisation.ExpiryTime"; - public static final String PUSH_AUTH_REQUEST_URI_SUBSTRING = "PushAuthorisation.RequestUriSubString"; - - public static final String CONSENT_PERIODICAL_EXPIRATION_CRON = "Consent.PeriodicalExpiration.CronValue"; - public static final String STATUS_FOR_EXPIRED_CONSENT = "Consent.PeriodicalExpiration.ExpiredConsentStatusValue"; - public static final String IS_CONSENT_PERIODICAL_EXPIRATION_ENABLED = "Consent.PeriodicalExpiration.Enabled"; - public static final String IS_CONSENT_AMENDMENT_HISTORY_ENABLED = "Consent.AmendmentHistory.Enabled"; - public static final String ELIGIBLE_STATUSES_FOR_CONSENT_EXPIRY = - "Consent.PeriodicalExpiration.EligibleStatuses"; - public static final String CONSENT_ID_CLAIM_NAME = "Identity.ConsentIDClaimName"; - - public static final String EVENT_QUEUE_SIZE = "Event.QueueSize"; - public static final String EVENT_WORKER_THREAD_COUNT = "Event.WorkerThreadCount"; - public static final String EVENT_EXECUTOR = "Event.EventExecutor"; - - // Data Retention Constants - public static final String IS_CONSENT_DATA_RETENTION_ENABLED = "Consent.DataRetention.Enabled"; - public static final String IS_CONSENT_RETENTION_DATA_DB_SYNC_ENABLED = "Consent.DataRetention.DBSyncEnabled"; - public static final String CONSENT_RETENTION_DATA_DB_SYNC_CRON = "Consent.DataRetention.CronValue"; - - // Service Activator Constants - public static final String SERVICE_ACTIVATOR_TAG = "ServiceActivator"; - public static final String SA_SUBSCRIBERS_TAG = "Subscribers"; - public static final String SA_SUBSCRIBER_TAG = "Subscriber"; - - //JWS handling related constants - public static final String JWS_SIG_VALIDATION_ENABLE = "JwsSignatureConfiguration.SignatureValidation.Enable"; - public static final String JWS_SIG_VALIDATION_ALGO = - "JwsSignatureConfiguration.SignatureValidation.AllowedAlgorithms"; - public static final String JWS_RESP_SIGNING_ENABLE = "JwsSignatureConfiguration.ResponseSigning.Enable"; - public static final String JWS_RESP_SIGNING_ALGO = "JwsSignatureConfiguration.ResponseSigning.AllowedAlgorithm"; - - // Open Banking Identity Manager - public static final String OB_IDN_RETRIEVER_SIG_ALIAS = "OBIdentityRetriever.Server.SigningCertificateAlias"; - public static final String OB_IDN_RETRIEVER_SANDBOX_SIG_ALIAS = - "OBIdentityRetriever.Server.SandboxSigningCertificateAlias"; - public static final String OB_IDN_RETRIEVER_SIG_KID = "OBIdentityRetriever.Server.SigningCertificateKid"; - public static final String OB_IDN_RETRIEVER_SANDBOX_KID = "OBIdentityRetriever.Server.SandboxCertificateKid"; - public static final String JWKS_RETRIEVER_SIZE_LIMIT = "OBIdentityRetriever.JWKSRetriever.SizeLimit"; - public static final String JWKS_RETRIEVER_CONN_TIMEOUT = "OBIdentityRetriever.JWKSRetriever.ConnectionTimeout"; - public static final String JWKS_RETRIEVER_READ_TIMEOUT = "OBIdentityRetriever.JWKSRetriever.ReadTimeout"; - - // Key Manager Additional Property Configs - public static final String KEY_MANAGER_CONFIG_TAG = "KeyManager"; - public static final String KEY_MANAGER_ADDITIONAL_PROPERTIES_CONFIG_TAG = "KeyManagerAdditionalProperties"; - public static final String PROPERTY_CONFIG_TAG = "Property"; - public static final String OB_KEYMANAGER_EXTENSION_IMPL = - "KeyManager.KeyManagerExtensionImpl"; - - //OB Event Notifications Constants - public static final String TOKEN_ISSUER = "OBEventNotifications.TokenIssuer"; - public static final String MAX_SETS_TO_RETURN = "OBEventNotifications.NumberOfSetsToReturn"; - public static final String SIGNING_ALIAS = "OBEventNotifications.SigningAlias"; - public static final String IS_SUB_CLAIM_INCLUDED = "OBEventNotifications.PollingResponseParams.IsSubClaimAvailable"; - public static final String IS_TXN_CLAIM_INCLUDED = "OBEventNotifications.PollingResponseParams.IsTxnClaimAvailable"; - public static final String IS_TOE_CLAIM_INCLUDED = "OBEventNotifications.PollingResponseParams.IsToeClaimAvailable"; - public static final String EVENT_CREATION_HANDLER = "OBEventNotifications.EventCreationHandler"; - public static final String EVENT_POLLING_HANDLER = "OBEventNotifications.EventPollingHandler"; - public static final String EVENT_SUBSCRIPTION_HANDLER = "OBEventNotifications.EventSubscriptionHandler"; - public static final String EVENT_NOTIFICATION_GENERATOR = "OBEventNotifications.NotificationGenerator"; - public static final String AUTHENTICATION_WORKER_LIST_TAG = "AuthenticationWorkers"; - public static final String AUTHENTICATION_WORKER_TAG = "AuthenticationWorker"; - - // Dispute Resolution Implementation Constants - public static final String IS_DISPUTE_RESOLUTION_ENABLED = "DataPublishing.DisputeResolution.Enabled"; - public static final String PUBLISH_NON_ERROR_DISPUTE_DATA = "DataPublishing" + - ".DisputeResolution.PublishNonErrorDisputeResolutionData"; - public static final String MAX_REQUEST_BODY_LENGTH = "DataPublishing.DisputeResolution.MaxRequestBodyLength"; - public static final String MAX_RESPONSE_BODY_LENGTH = "DataPublishing.DisputeResolution.MaxResponseBodyLength"; - public static final String MAX_HEADER_LENGTH = "DataPublishing.DisputeResolution.MaxHeaderLength"; - public static final String DISPUTE_RESOLUTION_STREAM_NAME = "DisputeResolutionStream"; - public static final String DISPUTE_RESOLUTION_STREAM_VERSION = "1.0.0"; - public static final String REQUEST_BODY = "requestBody"; - public static final String HTTP_METHOD = "httpMethod"; - public static final String STATUS_CODE = "statusCode"; - public static final String RESPONSE_BODY = "responseBody"; - public static final String ELECTED_RESOURCE = "electedResource"; - public static final String HEADERS = "headers"; - public static final String TIMESTAMP = "timestamp"; - - public static final String CUTOFF_DATE_ENABLED = "ConsentManagement.PaymentRestrictions.CutOffDateTime.Enabled"; - public static final String CUTOFF_DATE_POLICY = "ConsentManagement.PaymentRestrictions.CutOffDateTime" + - ".CutOffDateTimePolicy"; - public static final String ZONE_ID = "ZoneId"; - public static final String DAILY_CUTOFF = "ConsentManagement.PaymentRestrictions.CutOffDateTime" + - ".DailyCutOffTime"; - public static final String EXPECTED_EXECUTION_TIME = "ConsentManagement.PaymentRestrictions.CutOffDateTime" + - ".ExpectedExecutionTime"; - public static final String EXPECTED_SETTLEMENT_TIME = "ConsentManagement.PaymentRestrictions.CutOffDateTime" + - ".ExpectedSettlementTime"; - - // Realtime Event Notification Constants - public static final String REALTIME_EVENT_NOTIFICATION_ENABLED = "RealtimeEventNotification.Enable"; - public static final String PERIODIC_CRON_EXPRESSION = "RealtimeEventNotification.PeriodicCronExpression"; - public static final String TIMEOUT_IN_SECONDS = "RealtimeEventNotification.TimeoutInSeconds"; - public static final String MAX_RETRIES = "RealtimeEventNotification.MaxRetries"; - public static final String INITIAL_BACKOFF_TIME_IN_SECONDS - = "RealtimeEventNotification.InitialBackoffTimeInSeconds"; - public static final String BACKOFF_FUNCTION = "RealtimeEventNotification.BackoffFunction"; - public static final String CIRCUIT_BREAKER_OPEN_TIMEOUT_IN_SECONDS - = "RealtimeEventNotification.CircuitBreakerOpenTimeoutInSeconds"; - public static final String EVENT_NOTIFICATION_THREADPOOL_SIZE - = "RealtimeEventNotification.EventNotificationThreadPoolSize"; - public static final String REALTIME_EVENT_NOTIFICATION_REQUEST_GENERATOR - = "RealtimeEventNotification.RequestGenerator"; - public static final String CONTENT_TYPE_TAG = "Content-Type"; - public static final String JSON_CONTENT_TYPE = "application/json"; - public static final String SP_API_PATH = "/stores/query"; - public static final String APP_NAME_CC = "appName"; - public static final String QUERY = "query"; - public static final String IS_PSU_FEDERATED = "PSUFederatedAuthentication.Enabled"; - public static final String PSU_FEDERATED_IDP_NAME = "PSUFederatedAuthentication.IDPName"; - public static final String IDEMPOTENCY_IS_ENABLED = "Consent.Idempotency.Enabled"; - public static final String IDEMPOTENCY_ALLOWED_TIME = "Consent.Idempotency.AllowedTimeDuration"; -} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java deleted file mode 100644 index c3d3993f..00000000 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyConstants.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package com.wso2.openbanking.accelerator.consent.extensions.common.idempotency; - -/** - * Constants related to idempotency operations. - */ -public class IdempotencyConstants { - - public static final String CONTENT_TYPE_TAG = "content-type"; - public static final String X_IDEMPOTENCY_KEY = "x-idempotency-key"; - public static final String IDEMPOTENCY_KEY_NAME = "IdempotencyKey"; - public static final String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX"; - public static final String ERROR_PAYLOAD_NOT_SIMILAR = "Payloads are not similar. Hence this is not a valid" + - " idempotent request"; - public static final String ERROR_AFTER_ALLOWED_TIME = "Request received after the allowed time., Hence this is" + - " not a valid idempotent request"; - public static final String ERROR_MISMATCHING_CLIENT_ID = "Client ID sent in the request does not match with the" + - " client ID in the retrieved consent. Hence this is not a valid idempotent request"; - public static final String ERROR_NO_CONSENT_DETAILS = "No consent details found for the consent ID %s, Hence this" + - " is not a valid idempotent request"; - public static final String JSON_COMPARING_ERROR = "Error occurred while comparing JSON payloads"; - public static final String CONSENT_RETRIEVAL_ERROR = "Error while retrieving detailed consent data"; -} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java deleted file mode 100644 index 19b1d193..00000000 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidator.java +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.wso2.openbanking.accelerator.consent.extensions.common.idempotency; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; -import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; -import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; -import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; -import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; -import com.wso2.openbanking.accelerator.consent.mgt.service.ConsentCoreService; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.io.IOException; -import java.util.ArrayList; - -/** - * Class to handle idempotency related operations. - */ -public class IdempotencyValidator { - - private static final Log log = LogFactory.getLog(IdempotencyValidator.class); - private static final ConsentCoreService consentCoreService = ConsentExtensionsDataHolder.getInstance() - .getConsentCoreService(); - - /** - * Method to check whether the request is idempotent. - * This method will first check whether idempotency validation is enabled. After that it will validate whether - * required parameters for validation is present. - * For validation, need to check whether the idempotency key values is present as a consent attribute, if present - * the consent will be retrieved. Finally following conditions will be validated. - * - Whether the client id sent in the request and client id retrieved from the database are equal - * - Whether the difference between two dates is less than the configured time - * - Whether payloads are equal - * - * @param consentManageData Consent Manage Data - * @return IdempotencyValidationResult - */ - public IdempotencyValidationResult validateIdempotency(ConsentManageData consentManageData) - throws IdempotencyValidationException { - - if (!OpenBankingConfigParser.getInstance().isIdempotencyValidationEnabled()) { - return new IdempotencyValidationResult(false, false); - } - // If request is empty then cannot proceed with idempotency validation - if (consentManageData.getPayload() == null) { - log.error("Request payload is empty. Hence cannot proceed with idempotency validation"); - return new IdempotencyValidationResult(false, false); - } - // If client id is empty then cannot proceed with idempotency validation - if (StringUtils.isBlank(consentManageData.getClientId())) { - log.error("Client ID is empty. Hence cannot proceed with idempotency validation"); - return new IdempotencyValidationResult(false, false); - } - String idempotencyKeyValue = consentManageData.getHeaders().get(getIdempotencyHeaderName()); - // If idempotency key value is empty then cannot proceed with idempotency validation - if (StringUtils.isBlank(idempotencyKeyValue)) { - log.error("Idempotency Key Valueis empty. Hence cannot proceed with idempotency validation"); - return new IdempotencyValidationResult(false, false); - } - try { - String idempotencyKeyName = getIdempotencyAttributeName(consentManageData.getRequestPath()); - // Retrieve consent ids that have the idempotency key name and value as attribute - ArrayList consentIds = IdempotencyValidationUtils - .getConsentIdsFromIdempotencyKey(idempotencyKeyName, idempotencyKeyValue); - // Check whether the consent id list is not empty. If idempotency key exists in the database then - // the consent Id list will be not empty. - if (!consentIds.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug(String.format("Idempotency Key %s exists in the database. Hence this is an" + - " idempotent request", idempotencyKeyValue)); - } - for (String consentId : consentIds) { - DetailedConsentResource consentRequest = consentCoreService.getDetailedConsent(consentId); - if (consentRequest != null) { - return validateIdempotencyConditions(consentManageData, consentRequest); - } else { - String errorMsg = String.format(IdempotencyConstants.ERROR_NO_CONSENT_DETAILS, consentId); - log.error(errorMsg); - throw new IdempotencyValidationException(errorMsg); - } - } - } - } catch (IOException e) { - log.error(IdempotencyConstants.JSON_COMPARING_ERROR, e); - throw new IdempotencyValidationException(IdempotencyConstants.JSON_COMPARING_ERROR); - } catch (ConsentManagementException e) { - log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); - return new IdempotencyValidationResult(true, false); - } - return new IdempotencyValidationResult(false, false); - } - - /** - * Method to check whether the idempotency conditions are met. - * This method will validate the following conditions. - * - Whether the client id sent in the request and client id retrieved from the database are equal - * - Whether the difference between two dates is less than the configured time - * - Whether payloads are equal - * - * @param consentManageData Consent Manage Data - * @param consentRequest Detailed Consent Resource - * @return IdempotencyValidationResult - */ - private IdempotencyValidationResult validateIdempotencyConditions(ConsentManageData consentManageData, - DetailedConsentResource consentRequest) - throws IdempotencyValidationException, IOException { - // Compare the client ID sent in the request and client id retrieved from the database - // to validate whether the request is received from the same client - if (IdempotencyValidationUtils.isClientIDEqual(consentRequest.getClientID(), consentManageData.getClientId())) { - // Check whether difference between two dates is less than the configured time - if (IdempotencyValidationUtils.isRequestReceivedWithinAllowedTime(getCreatedTimeOfPreviousRequest( - consentManageData.getRequestPath(), consentRequest.getConsentID()))) { - // Compare whether JSON payloads are equal - if (isPayloadSimilar(consentManageData, getPayloadOfPreviousRequest( - consentManageData.getRequestPath(), consentRequest.getConsentID()))) { - log.debug("Payloads are similar and request received within allowed" + - " time. Hence this is a valid idempotent request"); - return new IdempotencyValidationResult(true, true, - consentRequest, consentRequest.getConsentID()); - } else { - log.error(IdempotencyConstants.ERROR_PAYLOAD_NOT_SIMILAR); - throw new IdempotencyValidationException(IdempotencyConstants - .ERROR_PAYLOAD_NOT_SIMILAR); - } - } else { - log.error(IdempotencyConstants.ERROR_AFTER_ALLOWED_TIME); - throw new IdempotencyValidationException(IdempotencyConstants - .ERROR_AFTER_ALLOWED_TIME); - } - } else { - log.error(IdempotencyConstants.ERROR_MISMATCHING_CLIENT_ID); - throw new IdempotencyValidationException(IdempotencyConstants.ERROR_MISMATCHING_CLIENT_ID); - } - } - - /** - * Method to get the Idempotency Attribute Name store in consent Attributes. - * - * @param resourcePath Resource Path - * @return idempotency Attribute Name. - */ - public String getIdempotencyAttributeName(String resourcePath) { - return IdempotencyConstants.IDEMPOTENCY_KEY_NAME; - } - - /** - * Method to get the Idempotency Header Name according to the request. - * - * @return idempotency Header Name. - */ - public String getIdempotencyHeaderName() { - return IdempotencyConstants.X_IDEMPOTENCY_KEY; - } - - /** - * Method to get created time from the Detailed Consent Resource. - * - * @param resourcePath Resource Path - * @param consentId ConsentId - * @return Created Time. - */ - public long getCreatedTimeOfPreviousRequest(String resourcePath, String consentId) { - DetailedConsentResource consentRequest = null; - try { - consentRequest = consentCoreService.getDetailedConsent(consentId); - } catch (ConsentManagementException e) { - log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); - return 0L; - } - if (consentRequest == null) { - return 0L; - } - return consentRequest.getCreatedTime(); - } - - /** - * Method to get payload from previous request. - * - * @param resourcePath Resource Path - * @param consentId ConsentId - * @return Map containing the payload. - */ - public String getPayloadOfPreviousRequest(String resourcePath, String consentId) { - DetailedConsentResource consentRequest = null; - try { - consentRequest = consentCoreService.getDetailedConsent(consentId); - } catch (ConsentManagementException e) { - log.error(IdempotencyConstants.CONSENT_RETRIEVAL_ERROR, e); - return null; - } - if (consentRequest == null) { - return null; - } - return consentRequest.getReceipt(); - } - - /** - * Method to compare whether payloads are equal. - * - * @param consentManageData Consent Manage Data Object - * @param consentReceipt Payload received from database - * @return Whether payloads are equal - */ - public boolean isPayloadSimilar(ConsentManageData consentManageData, String consentReceipt) { - - if (consentManageData.getPayload() == null || consentReceipt == null) { - return false; - } - - JsonNode expectedNode = null; - JsonNode actualNode = null; - try { - ObjectMapper mapper = new ObjectMapper(); - expectedNode = mapper.readTree(consentManageData.getPayload().toString()); - actualNode = mapper.readTree(consentReceipt); - if (log.isDebugEnabled()) { - log.debug(String.format("Expected payload for idempotent request is: %s. But actual payload " + - "received is %s", expectedNode.toString(), actualNode.toString())); - } - } catch (JsonProcessingException e) { - log.error(IdempotencyConstants.JSON_COMPARING_ERROR, e); - return false; - } - return expectedNode.equals(actualNode); - } -} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java deleted file mode 100644 index aa6f09ae..00000000 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/common/idempotency/IdempotencyValidatorTests.java +++ /dev/null @@ -1,283 +0,0 @@ -/** - * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.wso2.openbanking.accelerator.consent.extensions.common.idempotency; - -import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; -import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; -import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; -import com.wso2.openbanking.accelerator.consent.extensions.manage.model.ConsentManageData; -import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; -import com.wso2.openbanking.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.testng.PowerMockTestCase; -import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -/** - * Test class for IdempotencyValidator. - */ -@PrepareForTest({OpenBankingConfigParser.class, ConsentExtensionsDataHolder.class}) -@PowerMockIgnore("jdk.internal.reflect.*") -public class IdempotencyValidatorTests extends PowerMockTestCase { - - @Mock - private ConsentManageData consentManageData; - private ConsentCoreServiceImpl consentCoreServiceImpl; - private ArrayList consentIdList; - private String consentId; - private Map configs; - private Map headers; - private static final String CLIENT_ID = "testClientId"; - - private static final String PAYLOAD = "{\n" + - " \"Data\": {\n" + - " \"ReadRefundAccount\": \"Yes\",\n" + - " \"Initiation\": {\n" + - " \"InstructionIdentification\": \"ACME412\",\n" + - " \"EndToEndIdentification\": \"FRESCO.21302.GFX.20\",\n" + - " \"InstructedAmount\": {\n" + - " \"Amount\": \"165.88\",\n" + - " \"Currency\": \"GBP\"\n" + - " },\n" + - " \"CreditorAccount\": {\n" + - " \"SchemeName\": \"UK.OBIE.SortCodeAccountNumber\",\n" + - " \"Identification\": \"08080021325698\",\n" + - " \"Name\": \"ACME Inc\",\n" + - " \"SecondaryIdentification\": \"0002\"\n" + - " },\n" + - " \"RemittanceInformation\": {\n" + - " \"Reference\": \"FRESCO-101\",\n" + - " \"Unstructured\": \"Internal ops code 5120101\"\n" + - " }\n" + - " }\n" + - " },\n" + - " \"Risk\": {\n" + - " }\n" + - " }\n" + - "}"; - - private static final String DIFFERENT_PAYLOAD = "{\n" + - " \"Data\": {\n" + - " \"ReadRefundAccount\": \"No\",\n" + - " \"Initiation\": {\n" + - " \"InstructionIdentification\": \"ACME413\",\n" + - " \"EndToEndIdentification\": \"FRESCO.21302.GFX.20\",\n" + - " \"InstructedAmount\": {\n" + - " \"Amount\": \"165.88\",\n" + - " \"Currency\": \"GBP\"\n" + - " },\n" + - " \"CreditorAccount\": {\n" + - " \"SchemeName\": \"UK.OBIE.SortCodeAccountNumber\",\n" + - " \"Identification\": \"08080021325698\",\n" + - " \"Name\": \"ACME Inc\",\n" + - " \"SecondaryIdentification\": \"0002\"\n" + - " },\n" + - " \"RemittanceInformation\": {\n" + - " \"Reference\": \"FRESCO-101\",\n" + - " \"Unstructured\": \"Internal ops code 5120101\"\n" + - " }\n" + - " }\n" + - " },\n" + - " \"Risk\": {\n" + - " }\n" + - " }\n" + - "}"; - - - @BeforeClass - public void beforeTest() { - configs = new HashMap<>(); - - headers = new HashMap<>(); - headers.put(IdempotencyConstants.X_IDEMPOTENCY_KEY, "123456"); - headers.put(IdempotencyConstants.CONTENT_TYPE_TAG, "application/json"); - - consentManageData = Mockito.mock(ConsentManageData.class); - consentCoreServiceImpl = Mockito.mock(ConsentCoreServiceImpl.class); - - consentId = UUID.randomUUID().toString(); - consentIdList = new ArrayList<>(); - consentIdList.add(consentId); - } - - @BeforeMethod - public void beforeMethod() { - OpenBankingConfigParser openBankingConfigParserMock = PowerMockito.mock(OpenBankingConfigParser.class); - Mockito.doReturn(configs).when(openBankingConfigParserMock).getConfiguration(); - Mockito.doReturn(true).when(openBankingConfigParserMock).isIdempotencyValidationEnabled(); - Mockito.doReturn("1").when(openBankingConfigParserMock).getIdempotencyAllowedTime(); - ConsentExtensionsDataHolder consentExtensionsDataHolderMock = PowerMockito - .mock(ConsentExtensionsDataHolder.class); - - PowerMockito.mockStatic(OpenBankingConfigParser.class); - PowerMockito.when(OpenBankingConfigParser.getInstance()).thenReturn(openBankingConfigParserMock); - - PowerMockito.mockStatic(ConsentExtensionsDataHolder.class); - PowerMockito.when(ConsentExtensionsDataHolder.getInstance()).thenReturn(consentExtensionsDataHolderMock); - PowerMockito.when(consentExtensionsDataHolderMock.getConsentCoreService()).thenReturn(consentCoreServiceImpl); - } - - @Test - public void testValidateIdempotency() throws ConsentManagementException, IdempotencyValidationException { - OffsetDateTime offsetDateTime = OffsetDateTime.now(); - - Mockito.doReturn(consentIdList).when(consentCoreServiceImpl) - .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); - Mockito.doReturn(getConsent(offsetDateTime.toEpochSecond())).when(consentCoreServiceImpl) - .getDetailedConsent(Mockito.anyString()); - Mockito.doReturn(headers).when(consentManageData).getHeaders(); - Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); - Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); - IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); - - Assert.assertTrue(result.isIdempotent()); - Assert.assertTrue(result.isValid()); - Assert.assertNotNull(result.getConsent()); - Assert.assertEquals(consentId, result.getConsentId()); - } - - @Test - public void testValidateIdempotencyWithoutIdempotencyKeyValue() throws IdempotencyValidationException { - - Mockito.doReturn(new HashMap<>()).when(consentManageData).getHeaders(); - Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); - Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); - IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); - - Assert.assertFalse(result.isIdempotent()); - } - - @Test - public void testValidateIdempotencyWithoutRequest() throws IdempotencyValidationException { - Mockito.doReturn(headers).when(consentManageData).getHeaders(); - Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); - Mockito.doReturn("").when(consentManageData).getPayload(); - IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); - - Assert.assertFalse(result.isIdempotent()); - } - - @Test - public void testValidateIdempotencyRetrievingAttributesWithException() - throws ConsentManagementException, IdempotencyValidationException { - - Mockito.doThrow(ConsentManagementException.class).when(consentCoreServiceImpl) - .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); - Mockito.doReturn(headers).when(consentManageData).getHeaders(); - Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); - Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); - IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); - - Assert.assertFalse(result.isIdempotent()); - } - - @Test - public void testValidateIdempotencyWithoutAttribute() - throws ConsentManagementException, IdempotencyValidationException { - - Mockito.doReturn(new ArrayList<>()).when(consentCoreServiceImpl) - .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); - Mockito.doReturn(headers).when(consentManageData).getHeaders(); - Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); - Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); - IdempotencyValidationResult result = new IdempotencyValidator().validateIdempotency(consentManageData); - - Assert.assertFalse(result.isIdempotent()); - } - - @Test(expectedExceptions = IdempotencyValidationException.class) - public void testValidateIdempotencyWithNullConsentRequest() - throws ConsentManagementException, IdempotencyValidationException { - - Mockito.doReturn(consentIdList).when(consentCoreServiceImpl) - .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); - Mockito.doReturn(headers).when(consentManageData).getHeaders(); - Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); - Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); - Mockito.doReturn(null).when(consentCoreServiceImpl).getDetailedConsent(Mockito.anyString()); - new IdempotencyValidator().validateIdempotency(consentManageData); - } - - @Test(expectedExceptions = IdempotencyValidationException.class) - public void testValidateIdempotencyWithNonMatchingClientId() - throws ConsentManagementException, IdempotencyValidationException { - - Mockito.doReturn(consentIdList).when(consentCoreServiceImpl) - .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); - Mockito.doReturn(headers).when(consentManageData).getHeaders(); - Mockito.doReturn("sampleClientID").when(consentManageData).getClientId(); - Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); - Mockito.doReturn(null).when(consentCoreServiceImpl).getDetailedConsent(Mockito.anyString()); - new IdempotencyValidator().validateIdempotency(consentManageData); - } - - @Test(expectedExceptions = IdempotencyValidationException.class) - public void testValidateIdempotencyAfterAllowedTime() - throws ConsentManagementException, IdempotencyValidationException { - - OffsetDateTime offsetDateTime = OffsetDateTime.now().minusHours(2); - - Mockito.doReturn(consentIdList).when(consentCoreServiceImpl) - .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); - Mockito.doReturn(getConsent(offsetDateTime.toEpochSecond())).when(consentCoreServiceImpl) - .getDetailedConsent(Mockito.anyString()); - Mockito.doReturn(headers).when(consentManageData).getHeaders(); - Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); - Mockito.doReturn(PAYLOAD).when(consentManageData).getPayload(); - new IdempotencyValidator().validateIdempotency(consentManageData); - } - - @Test(expectedExceptions = IdempotencyValidationException.class) - public void testValidateIdempotencyWithNonMatchingPayload() - throws ConsentManagementException, IdempotencyValidationException { - - OffsetDateTime offsetDateTime = OffsetDateTime.now(); - - Mockito.doReturn(consentIdList).when(consentCoreServiceImpl) - .getConsentIdByConsentAttributeNameAndValue(Mockito.anyString(), Mockito.anyString()); - Mockito.doReturn(getConsent(offsetDateTime.toEpochSecond())).when(consentCoreServiceImpl) - .getDetailedConsent(Mockito.anyString()); - Mockito.doReturn(headers).when(consentManageData).getHeaders(); - Mockito.doReturn(CLIENT_ID).when(consentManageData).getClientId(); - Mockito.doReturn(DIFFERENT_PAYLOAD).when(consentManageData).getPayload(); - new IdempotencyValidator().validateIdempotency(consentManageData); - - } - - private DetailedConsentResource getConsent(long createdTime) { - DetailedConsentResource consent = new DetailedConsentResource(); - consent.setConsentID(consentId); - consent.setReceipt(PAYLOAD); - consent.setClientID(CLIENT_ID); - consent.setCreatedTime(createdTime); - return consent; - } -} diff --git a/pom.xml b/pom.xml index 0cf90812..8eb777e2 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,5 @@ - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.common - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.gateway - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.identity - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.runtime.identity.authn.filter - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.consent.extensions - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.data.publisher.common - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.keymanager - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.consent.service - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.consent.dao - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.throttler.dao - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.throttler.service - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.event.notifications.service - ${project.version} - - - com.wso2.openbanking.accelerator - com.wso2.openbanking.accelerator.event.notifications.endpoint - ${project.version} - - - - - org.wso2.carbon.identity.inbound.auth.oauth2 - org.wso2.carbon.identity.oauth.dcr - ${identity.inbound.auth.oauth.version} - - - org.wso2.carbon.identity.inbound.auth.oauth2 - org.wso2.carbon.identity.oauth - ${identity.inbound.auth.oauth.version} - - - org.wso2.carbon.identity.inbound.auth.oauth2 - org.wso2.carbon.identity.oauth.client.authn.filter - ${identity.inbound.auth.oauth.version} - - - org.wso2.carbon.identity.inbound.auth.oauth2 - org.wso2.carbon.identity.oauth.ciba - ${identity.inbound.auth.oauth.ciba.version} - - - org.wso2.carbon.identity.outbound.auth.push - org.wso2.carbon.identity.application.authenticator.push - ${identity.outbound.auth.push.authenticator.version} - - - org.wso2.carbon.identity.outbound.auth.push - org.wso2.carbon.identity.application.authenticator.push.device.handler - ${identity.outbound.auth.push.authenticator.version} - - - org.wso2.carbon.identity.outbound.auth.push - org.wso2.carbon.identity.application.authenticator.push.common - ${identity.outbound.auth.push.authenticator.version} - - - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.application.mgt - ${carbon.identity.framework.version} - - - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.application.authentication.framework - ${carbon.identity.version} - - - org.wso2.carbon.identity.application.auth.basic - org.wso2.carbon.identity.application.authenticator.basicauth - ${carbon.identity.application.authenticator.version} - - - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.application.common - ${carbon.identity.framework.version} - - - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.core - ${carbon.identity.version} - - - org.wso2.carbon.identity.local.auth.api - org.wso2.carbon.identity.local.auth.api.core - ${carbon.identity.local.auth.api.version} - provided - - - org.wso2.carbon.extension.identity.oauth.addons - org.wso2.carbon.identity.oauth2.token.handler.clientauth.mutualtls - ${carbon.identity.clientauth.mutualtls.version} - - - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.application.authentication.endpoint.util - ${carbon.identity.framework.version} - provided - - - org.apache.tomcat - tomcat-catalina - ${tomcat.catalina.version} - - - org.wso2.carbon - org.wso2.carbon.logging - ${carbon.kernel.ob.version} - - - org.wso2.carbon - org.wso2.carbon.core - ${carbon.kernel.version} - - - org.apache.ws.commons.axiom.wso2 - axiom - ${axiom.wso2.version} - commons-logging commons-logging @@ -430,17 +301,17 @@ org.apache.commons commons-lang3 - ${commons-lang.version} + ${commons-lang3.version} - commons-dbcp - commons-dbcp - ${commons-dbcp.version} + commons-beanutils + commons-beanutils + ${commons.bean.utils.version} - org.wso2.securevault - org.wso2.securevault - ${org.wso2.securevault.version} + net.minidev + json-smart + ${json-smart.version} org.wso2.eclipse.osgi @@ -453,103 +324,60 @@ ${org.osgi.bundle.version} - org.apache.synapse - synapse-core - ${apache.synapse.version} - - - commons-beanutils - commons-beanutils - ${commons.bean.utils.version} - - - org.javassist - javassist - ${javassist.version} - - - io.swagger - swagger-jaxrs - ${swagger-jaxrs.version} - - - io.swagger.parser.v3 - swagger-parser - ${swagger.parser.version} - - - javax.ws.rs - javax.ws.rs-api - ${javax.ws.rs-api.version} - - - org.eclipse.equinox - javax.servlet - ${equinox.javax.servlet.version} + org.wso2.securevault + org.wso2.securevault + ${org.wso2.securevault.version} - - org.jacoco - org.jacoco.agent - runtime - ${jacoco.version} + org.wso2.orbit.com.nimbusds + nimbus-jose-jwt + ${org.wso2.orbit.nimbus.version} - org.testng - testng - ${org.testng.version} - test + org.hibernate + hibernate-validator + ${hibernate-validator.version} - org.mockito - mockito-all - ${mockito.version} - test + org.apache.commons + commons-collections4 + ${commons-collections.version} - com.h2database - h2 - ${h2database.version} + com.github.spotbugs + spotbugs-annotations + ${spotbugs.annotations.version} - org.powermock - powermock-module-testng - ${powermock.version} - test + org.wso2.orbit.org.owasp.encoder + encoder + ${encoder.wso2.version} - org.powermock - powermock-api-mockito - ${powermock.version} - test + org.json.wso2 + json + ${org.json.version} - org.springframework - spring-test - ${spring-web-test.version} - test + org.apache.cxf + cxf-rt-frontend-jaxrs + ${org.apache.cxf.version} - org.springframework - spring-core - ${spring-web.version} - test + io.swagger + swagger-jaxrs + ${swagger-jaxrs.version} - org.apache.cxf - cxf-bundle-jaxrs - ${cxf-bundle-package.version} + javax.ws.rs + javax.ws.rs-api + ${javax.ws.rs-api.version} org.apache.cxf cxf-core ${org.apache.cxf.version} - - org.apache.cxf - cxf-rt-frontend-jaxrs - ${org.apache.cxf.version} - com.fasterxml.jackson.core jackson-databind @@ -561,98 +389,100 @@ ${jackson.databinding.version} - io.swagger - swagger-annotations - ${swagger-annotations.version} + org.springframework + spring-web + ${spring-web.version} - io.swagger.core.v3 - swagger-models - ${swagger.model.version} + org.apache.tomcat + tomcat-catalina + ${apache.tomcat-catalina.version} - javax.ws.rs - jsr311-api - ${javax.ws.rs.version} + org.apache.taglibs + taglibs-standard-impl + ${taglibs-standard-impl.version} - javax.validation - validation-api - ${javax.validation.api.version} + org.wso2.orbit.org.apache.httpcomponents + httpclient + ${orbit.version.commons.httpclient} - org.springframework - spring-web - ${spring-web.version} + org.apache.httpcomponents.wso2 + httpcore + ${orbit.httpcore.version} - net.minidev - json-smart - ${json-smart} + jstl + jstl + ${jstl.version} - org.wso2.carbon.apimgt - org.wso2.carbon.apimgt.keymgt - ${org.wso2.carbon.apimgt.version} + org.slf4j + slf4j-api + ${org.slf4j.verison} - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.user.profile - ${carbon.identity.version} + org.eclipse.equinox + javax.servlet + ${equinox.javax.servlet.version} - org.wso2.carbon.apimgt - org.wso2.carbon.apimgt.common.gateway - ${org.wso2.carbon.apimgt.version} + commons-io.wso2 + commons-io + ${commons.io.version} - io.jsonwebtoken - jjwt - ${jjwt.version} + javax.validation + validation-api + ${javax.validation.api.version} - - - org.wso2.carbon.analytics-common - org.wso2.carbon.databridge.agent - ${carbon.analytics.common.version} + org.wso2.orbit.org.apache.oltu.oauth2 + oltu + ${oltu.version} - org.wso2.am.analytics.publisher - org.wso2.am.analytics.publisher.client - ${analytics.publisher.version} + io.swagger.parser.v3 + swagger-parser + ${swagger.parser.version} - org.wso2.orbit.com.nimbusds - nimbus-jose-jwt - ${org.wso2.orbit.nimbus.version} + org.quartz-scheduler + quartz + ${quartz.version} + + + + org.wso2.carbon + org.wso2.carbon.core + ${carbon.kernel.version} - - - org.seleniumhq.selenium - selenium-server - ${selenium.version} + org.wso2.carbon.identity.inbound.auth.oauth2 + org.wso2.carbon.identity.oauth + ${identity.inbound.auth.oauth.version} - io.rest-assured - rest-assured - ${rest.assured.version} + org.wso2.carbon.identity.local.auth.api + org.wso2.carbon.identity.local.auth.api.core + ${carbon.identity.local.auth.api.version} - com.nimbusds - oauth2-oidc-sdk - ${com.nimbusds.oidc.version} + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.application.mgt + ${carbon.identity.framework.version} - org.codehaus.groovy - groovy-all - ${org.codehaus.groovy.version} + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.core + ${carbon.identity.framework.version} org.wso2.carbon.apimgt org.wso2.carbon.apimgt.impl - ${org.wso2.carbon.apimgt.keymgt.version} + ${org.wso2.carbon.apimgt.version} javassist @@ -662,152 +492,147 @@ org.wso2.carbon.apimgt - org.wso2.carbon.apimgt.api - ${org.wso2.carbon.apimgt.keymgt.version} + org.wso2.carbon.apimgt.common.gateway + ${org.wso2.carbon.apimgt.version} + - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.application.mgt.stub - ${carbon.identity.framework.version} + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.common + ${project.version} - com.fasterxml.jackson.core - jackson-core - ${jackson.databinding.version} + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.consent.mgt.dao + ${project.version} - org.hibernate - hibernate-validator - ${hibernate-validator} + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.consent.mgt.service + ${project.version} - javax.servlet - jstl - ${jstl.version} + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.consent.mgt.extensions + ${project.version} - org.bouncycastle - bcprov-jdk15on - ${org.bouncycastle.version} + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.identity.extensions + ${project.version} + - org.bouncycastle - bcpkix-jdk15on - ${org.bouncycastle.version} + org.testng + testng + ${org.testng.version} + test - org.json.wso2 - json - ${org.json.version} + org.mockito + mockito-core + ${mockito.version} + test - org.quartz-scheduler.wso2 - quartz - ${quartz.version} + org.mockito + mockito-testng + ${mockito.testng.version} + test - org.wso2.orbit.org.apache.oltu.oauth2 - oltu - ${oltu.version} + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + test + + + com.h2database.wso2 + h2-database-engine + ${orbit.version.h2.engine} + test + + + org.jacoco + org.jacoco.agent + runtime + ${jacoco.version} + - [6.4.111, 6.11.22] - 6.4.111 - 3.21.0-GA - 1.26 - 6.4.111 - 0.1.1 - 3.21.0-GA - 2.4.0 + 4.9.26 1.8 1.8 - 6.0.20.Final - [9.0.174, 9.28.117] - 9.28.116 - 9.28.116 - [5.2.24, 7.0.0) - [5.2.24, 6.0.10) - 1.0.0 - 1.2.11-wso2v16 - 1.2 - 3.4 - 1.4 - 1.9.3 + + 3.1.0 + 2.4 + 3.8.0 + 3.0.2 + 3.3.2 3.2.0 - 1.1.3 2.22.2 - 2.3.2 - 3.0.2 - 2.8.2 - 2.4 - 2.1.1.wso2v1 - 0.8.6 - 2.1.7-wso2v217 - 1.10.19 - 7.3.0 - 5.18.187 - 6.3.11 - 5.19.32 - 2.3.5 - 3.3.7 - 2.12.0 - 2.7.18 + 4.8.2.0 + 1.12.0 + 3.1.0 + 1.8 + + 1.2 + 3.4 + 1.9.4 + 2.4.10 + 3.5.100.v20160504-1419 + 3.9.1.v20130814-1242 + 1.1.3 + 7.9.0.wso2v1 + 6.0.20.Final + 4.4 + 4.7.3 + 1.2.0.wso2v1 + 3.0.0.wso2v4 + 2.16.1 + 1.0.0.wso2v3 1.6.1 - 1.5.10 - 1.1.1 - 2.0.1.Final - 3.3.2 - 6.6.0 + 2.1.1 5.1.2.RELEASE - 2.3 - [7.3.0,8.0.0) - 7.3.0.wso2v1 - [2.8.5, 3.0.0) - 1.3.175 - 5.3.3 - 1.7.1 2.5 - 9.0.11 - 2.0.1 - [5.11.0, 6.2.0) - [9.0.0, 9.5.0) - [2.6.0, 3.0.0) - 4.6.0 - [4.6, 5.0) - 4.4.32 - 3.0.0.v201112011016 - 3.0.0.wso2v1 - 0.9.1 + 3.3.7 1.2 - 1.59 - [1.6,2) - 4.2.3 - 1.10.1 - 4.7.3 - 3.1.0 - 1.0.0.wso2v3 - 1.12.0 - 1.2.0.wso2v1 - - - [1.7.0, 2.0.0) - [1.2.0, 2.0.0) - 3.5.100.v20160504-1419 - 3.9.1.v20130814-1242 - [6.4.111,6.9.6] - - - 3.3.0 - 3.141.59 - 3.8.1 - 6.13 - 2.4.11 - 2.1.7 + 1.7.21 + 3.0.0.v201112011016 + 4.5.13.wso2v1 + 4.3.3.wso2v1 + 2.15.1.wso2v1 + 1.2.5 + 9.0.11 + 2.0.1.Final 2.0.24 - 4.0.2 - 1.13.1 - + 2.3.2 + + 7.0.75 + [7.0.75, 8.0.0) + 7.0.26 + [6.13.0, 7.0.62) + 2.5.10 + 9.29.120 + [9.29.120, 9.29.121) + + 0.8.6 + 5.3.1 + 7.10.1 + 0.5.2 + 1.4 + 1.2.140.wso2v3 + + [1.7.0, 4.0.0) + [1.2.0, 4.0.0) + [2.6.0, 3.0.0) + [2.9,3) + [4.4.0, 5.0.0) + [1.2.11, 2.0.0) + [7.9.0, 10.0) + [2.15.1, 2.16.0) + [3.0.0, 4.0.0) From 339d87dcc910bcbc98926fbe576229530ab06865 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Mon, 28 Oct 2024 10:00:28 +0530 Subject: [PATCH 15/23] Fix unit test coverage issue --- .../repository/conf/financial-services.xml | 27 +++++ .../repository/conf/financial-services.xml.j2 | 12 +-- .../config/FinancialServicesConfigParser.java | 2 +- .../common/test/FSConfigParserTests.java | 99 +++++++++++++++++++ .../repository/conf/financial-services.xml | 79 ++++----------- .../DefaultEventNotificationGenerator.java | 2 +- .../{service => }/EventCreationService.java | 2 +- .../EventNotificationGenerator.java | 2 +- .../{service => }/EventPollingService.java | 2 +- .../EventSubscriptionService.java | 2 +- .../DefaultEventCreationServiceHandler.java | 2 +- .../DefaultEventPollingServiceHandler.java | 2 +- ...efaultEventSubscriptionServiceHandler.java | 2 +- .../util/EventNotificationServiceUtil.java | 2 +- .../src/main/resources/findbugs-exclude.xml | 2 +- 15 files changed, 163 insertions(+), 76 deletions(-) rename financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/{service => }/DefaultEventNotificationGenerator.java (99%) rename financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/{service => }/EventCreationService.java (99%) rename financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/{service => }/EventNotificationGenerator.java (99%) rename financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/{service => }/EventPollingService.java (99%) rename financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/{service => }/EventSubscriptionService.java (99%) diff --git a/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/conf/financial-services.xml b/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/conf/financial-services.xml index f1f1a211..bc557561 100644 --- a/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/conf/financial-services.xml +++ b/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/conf/financial-services.xml @@ -82,4 +82,31 @@ 2000 1500 + + + org.wso2.financial.services.accelerator.event.notifications.service.DefaultEventNotificationGenerator + www.wso2.com + 5 + + org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventCreationServiceHandler + org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventPollingServiceHandler + org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventSubscriptionServiceHandler + + true + {{financial_services.event.notifications.set_txn_claim_included}} + true + true + + + false + 0 0/1 0 ? * * * + 60 + 5 + 60 + EX + 600 + 20 + org.wso2.financial.services.accelerator.event.notifications.service.realtime.service.DefaultRealtimeEventNotificationRequestGenerator + + diff --git a/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/resources/conf/templates/repository/conf/financial-services.xml.j2 b/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/resources/conf/templates/repository/conf/financial-services.xml.j2 index b43c194b..92ada121 100644 --- a/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/resources/conf/templates/repository/conf/financial-services.xml.j2 +++ b/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/resources/conf/templates/repository/conf/financial-services.xml.j2 @@ -215,7 +215,7 @@ {% if financial_services.event.notifications.event_notification_generator is defined %} {{financial_services.event.notifications.event_notification_generator}} {% else %} - com.wso2.openbanking.accelerator.event.notifications.service.service.DefaultEventNotificationGenerator + org.wso2.financial.services.accelerator.event.notifications.service.DefaultEventNotificationGenerator {% endif %} {% if financial_services.event.notifications.token_issuer is defined %} {{financial_services.event.notifications.token_issuer}} @@ -231,17 +231,17 @@ {% if financial_services.event.notifications.event_creation_handler is defined %} {{financial_services.event.notifications.event_creation_handler}} {% else %} - com.wso2.openbanking.accelerator.event.notifications.service.handler.DefaultEventCreationServiceHandler + org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventCreationServiceHandler {% endif %} {% if financial_services.event.notifications.event_polling_handler is defined %} {{financial_services.event.notifications.event_polling_handler}} {% else %} - com.wso2.openbanking.accelerator.event.notifications.service.handler.DefaultEventPollingServiceHandler + org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventPollingServiceHandler {% endif %} {% if financial_services.event.notifications.event_subscription_handler is defined %} {{financial_services.event.notifications.event_subscription_handler}} {% else %} - com.wso2.openbanking.accelerator.event.notifications.service.handler.DefaultEventSubscriptionServiceHandler + org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventSubscriptionServiceHandler {% endif %} {% if financial_services.event.notifications.set_sub_claim_included is defined%} @@ -304,8 +304,8 @@ {% if financial_services.event.notifications.realtime.event_notification_request_generator is defined %} {{financial_services.event.notifications.realtime.event_notification_request_generator}} {% else %} - com.wso2.openbanking.accelerator.event.notifications.service.realtime.service.DefaultRealtimeEventNotificationRequestGenerator + org.wso2.financial.services.accelerator.event.notifications.service.realtime.service.DefaultRealtimeEventNotificationRequestGenerator {% endif %} - + diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/config/FinancialServicesConfigParser.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/config/FinancialServicesConfigParser.java index 74960e6a..f6084dbd 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/config/FinancialServicesConfigParser.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/config/FinancialServicesConfigParser.java @@ -718,7 +718,7 @@ public int getRealtimeEventNotificationCircuitBreakerOpenTimeoutInSeconds() { * * @return integer fix size to set the Thread Pool size in the real-time event notification sender */ - public int getEventNotificationThreadpoolSize() { + public int getEventNotificationThreadPoolSize() { Optional config = getConfigurationFromKeyAsString( FinancialServicesConstants.EVENT_NOTIFICATION_THREAD_POOL_SIZE); diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/test/java/org/wso2/financial/services/accelerator/common/test/FSConfigParserTests.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/test/java/org/wso2/financial/services/accelerator/common/test/FSConfigParserTests.java index 1f4cc70d..007d5a5c 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/test/java/org/wso2/financial/services/accelerator/common/test/FSConfigParserTests.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/test/java/org/wso2/financial/services/accelerator/common/test/FSConfigParserTests.java @@ -203,4 +203,103 @@ public void testGetConsentValidationConfig() { Assert.assertNotNull(FinancialServicesConfigParser.getInstance().getConsentValidationConfig()); } + + @Test(priority = 24) + public void testGetEventNotificationTokenIssuer() { + + Assert.assertNotNull(FinancialServicesConfigParser.getInstance().getEventNotificationTokenIssuer()); + } + + @Test(priority = 25) + public void testGetNumberOfSetsToReturn() { + + Assert.assertEquals(FinancialServicesConfigParser.getInstance().getNumberOfSetsToReturn(), 5); + } + + @Test(priority = 26) + public void testIsSubClaimIncluded() { + + Assert.assertFalse(FinancialServicesConfigParser.getInstance().isSubClaimIncluded()); + } + + @Test(priority = 27) + public void testIsToeClaimIncluded() { + + Assert.assertFalse(FinancialServicesConfigParser.getInstance().isToeClaimIncluded()); + } + + @Test(priority = 28) + public void testIsTxnClaimIncluded() { + + Assert.assertFalse(FinancialServicesConfigParser.getInstance().isTxnClaimIncluded()); + } + + @Test(priority = 29) + public void testIsRealtimeEventNotificationEnabled() { + + Assert.assertFalse(FinancialServicesConfigParser.getInstance().isRealtimeEventNotificationEnabled()); + } + + @Test(priority = 30) + public void testGetRealtimeEventNotificationSchedulerCronExpression() { + + Assert.assertNotNull(FinancialServicesConfigParser.getInstance() + .getRealtimeEventNotificationSchedulerCronExpression()); + } + + @Test(priority = 31) + public void testGetRealtimeEventNotificationTimeoutInSeconds() { + + Assert.assertEquals(FinancialServicesConfigParser.getInstance() + .getRealtimeEventNotificationTimeoutInSeconds(), 60); + } + + @Test(priority = 32) + public void testGetRealtimeEventNotificationMaxRetries() { + + Assert.assertEquals(FinancialServicesConfigParser.getInstance() + .getRealtimeEventNotificationMaxRetries(), 5); + } + + @Test(priority = 33) + public void testGetRealtimeEventNotificationInitialBackoffTimeInSeconds() { + + Assert.assertEquals(FinancialServicesConfigParser.getInstance() + .getRealtimeEventNotificationInitialBackoffTimeInSeconds(), 60); + } + + @Test(priority = 34) + public void testGetRealtimeEventNotificationBackoffFunction() { + + Assert.assertNotNull(FinancialServicesConfigParser.getInstance() + .getRealtimeEventNotificationBackoffFunction()); + } + + @Test(priority = 35) + public void testGetRealtimeEventNotificationCircuitBreakerOpenTimeoutInSeconds() { + + Assert.assertEquals(FinancialServicesConfigParser.getInstance() + .getRealtimeEventNotificationCircuitBreakerOpenTimeoutInSeconds(), 600); + } + @Test(priority = 36) + public void testGetEventNotificationThreadPoolSize() { + + Assert.assertEquals(FinancialServicesConfigParser.getInstance() + .getEventNotificationThreadPoolSize(), 20); + } + + @Test(priority = 37) + public void testGetEventNotificationGenerator() { + + Assert.assertNotNull(FinancialServicesConfigParser.getInstance() + .getEventNotificationGenerator()); + } + + @Test(priority = 38) + public void testGetRealtimeEventNotificationRequestGenerator() { + + Assert.assertNotNull(FinancialServicesConfigParser.getInstance() + .getRealtimeEventNotificationRequestGenerator()); + } + } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/test/resources/repository/conf/financial-services.xml b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/test/resources/repository/conf/financial-services.xml index 9cb2eeb4..62336ba7 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/test/resources/repository/conf/financial-services.xml +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/test/resources/repository/conf/financial-services.xml @@ -160,69 +160,30 @@ - - www.wso2.com - 5 + + + org.wso2.financial.services.accelerator.event.notifications.service.DefaultEventNotificationGenerator + www.wso2.com + 5 + org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventCreationServiceHandler org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventPollingServiceHandler - org.wso2.financial.services.accelerator.event.notifications.service.service.DefaultEventNotificationGenerator org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventSubscriptionServiceHandler - true - true - true + false + false + false - - - - - false - - PS256 - ES256 - - - + false - - PS256 - ES256 - - - - - - - wso2carbon - wso2carbon-sandbox - 1234 - 5678 - - - 51200 - 2000 - 2000 - - - - - - - - - - - - - - true - 0 0/1 0 ? * * * - 60 - 5 - 60 - EX - 600 - 20 - org.wso2.financial.services.accelerator.event.notifications.service.realtime.service.DefaultRealtimeEventNotificationRequestGenerator - + 0 0/1 0 ? * * * + 60 + 5 + 60 + EX + 600 + 20 + org.wso2.financial.services.accelerator.event.notifications.service.realtime.service.DefaultRealtimeEventNotificationRequestGenerator + + diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/DefaultEventNotificationGenerator.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java similarity index 99% rename from financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/DefaultEventNotificationGenerator.java rename to financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java index 6eaecc0b..f06027e9 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/DefaultEventNotificationGenerator.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.financial.services.accelerator.event.notifications.service.service; +package org.wso2.financial.services.accelerator.event.notifications.service; import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.logging.Log; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventCreationService.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventCreationService.java similarity index 99% rename from financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventCreationService.java rename to financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventCreationService.java index e4e09fab..44827a7f 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventCreationService.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventCreationService.java @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.financial.services.accelerator.event.notifications.service.service; +package org.wso2.financial.services.accelerator.event.notifications.service; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventNotificationGenerator.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventNotificationGenerator.java similarity index 99% rename from financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventNotificationGenerator.java rename to financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventNotificationGenerator.java index 93425643..6cde9dd3 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventNotificationGenerator.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventNotificationGenerator.java @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.financial.services.accelerator.event.notifications.service.service; +package org.wso2.financial.services.accelerator.event.notifications.service; import com.fasterxml.jackson.databind.JsonNode; import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventPollingService.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventPollingService.java similarity index 99% rename from financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventPollingService.java rename to financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventPollingService.java index ef24bdf3..e2513844 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventPollingService.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventPollingService.java @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.financial.services.accelerator.event.notifications.service.service; +package org.wso2.financial.services.accelerator.event.notifications.service; import com.nimbusds.jose.JOSEException; import org.apache.commons.logging.Log; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventSubscriptionService.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventSubscriptionService.java similarity index 99% rename from financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventSubscriptionService.java rename to financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventSubscriptionService.java index c28a5991..4a4f6499 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/service/EventSubscriptionService.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventSubscriptionService.java @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.financial.services.accelerator.event.notifications.service.service; +package org.wso2.financial.services.accelerator.event.notifications.service; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java index 67fa8039..b92b8f02 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java @@ -28,7 +28,7 @@ import org.wso2.financial.services.accelerator.event.notifications.service.dto.NotificationCreationDTO; import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventCreationResponse; -import org.wso2.financial.services.accelerator.event.notifications.service.service.EventCreationService; +import org.wso2.financial.services.accelerator.event.notifications.service.EventCreationService; import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; /** diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java index b5930e69..8b459419 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java @@ -31,7 +31,7 @@ import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPolling; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPollingResponse; import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationError; -import org.wso2.financial.services.accelerator.event.notifications.service.service.EventPollingService; +import org.wso2.financial.services.accelerator.event.notifications.service.EventPollingService; import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; import java.util.Locale; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java index cba1d7b6..d33d8b08 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java @@ -27,7 +27,7 @@ import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscription; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscriptionResponse; -import org.wso2.financial.services.accelerator.event.notifications.service.service.EventSubscriptionService; +import org.wso2.financial.services.accelerator.event.notifications.service.EventSubscriptionService; import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; import java.util.ArrayList; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java index c7a2788b..ee5daf00 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java @@ -35,7 +35,7 @@ import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventNotificationErrorDTO; import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; import org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventCreationServiceHandler; -import org.wso2.financial.services.accelerator.event.notifications.service.service.EventNotificationGenerator; +import org.wso2.financial.services.accelerator.event.notifications.service.EventNotificationGenerator; import java.util.Optional; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-exclude.xml b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-exclude.xml index 43ad701d..eebf9f61 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-exclude.xml +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/resources/findbugs-exclude.xml @@ -37,6 +37,6 @@ - + From edd1e17ef0785c4b33660991ff3befe9905ead15 Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Mon, 28 Oct 2024 13:34:56 +0530 Subject: [PATCH 16/23] Fixing build issue --- .../accelerator/common/util/JWTUtils.java | 2 + .../pom.xml | 145 +++++++++--------- .../DefaultEventCreationServiceHandler.java | 2 +- .../DefaultEventPollingServiceHandler.java | 2 +- ...efaultEventSubscriptionServiceHandler.java | 2 +- .../util/EventNotificationServiceUtil.java | 2 +- 6 files changed, 79 insertions(+), 76 deletions(-) diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/JWTUtils.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/JWTUtils.java index 6c37a447..234fc03f 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/JWTUtils.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/util/JWTUtils.java @@ -205,6 +205,7 @@ public static boolean isValidJWSFormat(String jwsString) { * parsable JWT * */ + @Generated(message = "Excluding from code coverage as it require external call") public static SignedJWT getSignedJWT(String jwtString) throws ParseException { if (isValidJWSFormat(jwtString)) { @@ -384,6 +385,7 @@ public static String signJWTWithDefaultKey(String body) throws Exception { * @param privateKey The private key for the JWT to be signed with * @return String signed JWT */ + @Generated(message = "Excluding from code coverage as it require external call") public static String generateJWT(String payload, Key privateKey) { if (privateKey == null || payload == null) { diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml index d1826979..1a5b0274 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml @@ -73,78 +73,79 @@ - - org.apache.maven.plugins - maven-surefire-plugin - - - src/test/resources/testng.xml - - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - - **/*Constants.class - **/*Component.class - **/*DataHolder.class - *SqlStatements.* - **/exceptions/* - **/StoreInitializer/* - - - - - default-prepare-agent - - prepare-agent - - - - default-prepare-agent-integration - - prepare-agent-integration - - - - default-report - - report - - - - default-report-integration - - report-integration - - - - default-check - - check - - - - - BUNDLE - - - INSTRUCTION - COVEREDRATIO - 0.8 - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.github.spotbugs spotbugs-maven-plugin diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java index b92b8f02..d05ad002 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java @@ -24,11 +24,11 @@ import org.wso2.financial.services.accelerator.common.exception.ConsentManagementException; import org.wso2.financial.services.accelerator.consent.mgt.dao.models.ConsentResource; import org.wso2.financial.services.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; +import org.wso2.financial.services.accelerator.event.notifications.service.EventCreationService; import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; import org.wso2.financial.services.accelerator.event.notifications.service.dto.NotificationCreationDTO; import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventCreationResponse; -import org.wso2.financial.services.accelerator.event.notifications.service.EventCreationService; import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; /** diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java index 8b459419..52118064 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java @@ -24,6 +24,7 @@ import org.json.JSONObject; import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; import org.wso2.financial.services.accelerator.common.util.Generated; +import org.wso2.financial.services.accelerator.event.notifications.service.EventPollingService; import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventPollingDTO; import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; @@ -31,7 +32,6 @@ import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPolling; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPollingResponse; import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationError; -import org.wso2.financial.services.accelerator.event.notifications.service.EventPollingService; import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; import java.util.Locale; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java index d33d8b08..22c3a93e 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java @@ -22,12 +22,12 @@ import org.apache.commons.logging.LogFactory; import org.apache.http.HttpStatus; import org.json.JSONObject; +import org.wso2.financial.services.accelerator.event.notifications.service.EventSubscriptionService; import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventSubscriptionDTO; import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscription; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscriptionResponse; -import org.wso2.financial.services.accelerator.event.notifications.service.EventSubscriptionService; import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; import java.util.ArrayList; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java index ee5daf00..87a35d18 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java @@ -31,11 +31,11 @@ import org.wso2.financial.services.accelerator.common.util.FinancialServicesUtils; import org.wso2.financial.services.accelerator.common.util.Generated; import org.wso2.financial.services.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; +import org.wso2.financial.services.accelerator.event.notifications.service.EventNotificationGenerator; import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventNotificationErrorDTO; import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; import org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventCreationServiceHandler; -import org.wso2.financial.services.accelerator.event.notifications.service.EventNotificationGenerator; import java.util.Optional; From f37a27e475c0efb32c8f2b3cf0cb4e92c9b9ebef Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Tue, 29 Oct 2024 10:00:55 +0530 Subject: [PATCH 17/23] Fix osgi issues --- .../pom.xml | 15 +++++++++++---- .../DefaultEventNotificationGenerator.java | 4 ++-- .../service/EventCreationService.java | 4 ++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml index 1a5b0274..4c3f4f99 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/pom.xml @@ -194,9 +194,17 @@ org.osgi.framework;version="${osgi.framework.imp.pkg.version.range}", org.osgi.service.component;version="${osgi.service.component.imp.pkg.version.range}", - com.wso2.openbanking.accelerator.common.*;version="${project.version}", - org.apache.commons.lang3;version="${commons-lang3.version}" - com.wso2.openbanking.accelerator.consent.mgt.service.*;version="${project.version}", + org.apache.commons.lang3;version="${commons-lang3.version}", + org.apache.commons.logging;version="${commons.logging.version}", + org.json;version="${org.json.version}", + com.fasterxml.jackson.annotation;version="${jackson.databinding.version}", + com.fasterxml.jackson.databind;version="${jackson.databinding.version}", + com.nimbusds.jose;version="${org.wso2.orbit.nimbus.version.range}", + org.wso2.carbon.identity.application.common.*;version="${carbon.identity.framework.version.range}", + org.wso2.carbon.identity.oauth2.*;version="${identity.inbound.auth.oauth.version.range}", + org.wso2.financial.services.accelerator.common.*;version="${project.version}", + org.wso2.financial.services.accelerator.consent.mgt.dao.*;version="${project.version}", + org.wso2.financial.services.accelerator.consent.mgt.service.*;version="${project.version}", !org.wso2.financial.services.accelerator.event.notifications.service.internal, @@ -205,7 +213,6 @@ javax.ws.rs-api;scope=compile;inline=false, - * <_dsannotations>* diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java index f06027e9..1a62cc22 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.registry.core.utils.UUIDGenerator; import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; import org.wso2.financial.services.accelerator.common.util.Generated; import org.wso2.financial.services.accelerator.common.util.JWTUtils; @@ -33,6 +32,7 @@ import java.time.Instant; import java.util.List; +import java.util.UUID; /** * Default Event Notification Response Generator Class. @@ -51,7 +51,7 @@ public NotificationResponse generateEventNotificationBody(Notification notificat Long currentTime = Instant.now().getEpochSecond(); //generate transaction Identifier - String transactionIdentifier = UUIDGenerator.generateUUID(); + String transactionIdentifier = UUID.randomUUID().toString(); notificationResponse.setIss(FinancialServicesConfigParser.getInstance().getEventNotificationTokenIssuer()); notificationResponse.setIat(currentTime); diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventCreationService.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventCreationService.java index 44827a7f..acefc46a 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventCreationService.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventCreationService.java @@ -21,7 +21,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONObject; -import org.wso2.carbon.registry.core.utils.UUIDGenerator; import org.wso2.financial.services.accelerator.common.util.DatabaseUtils; import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; import org.wso2.financial.services.accelerator.event.notifications.service.dao.EventNotificationDAO; @@ -34,6 +33,7 @@ import java.sql.Connection; import java.util.ArrayList; import java.util.Map; +import java.util.UUID; /** * This is the event creation service class. @@ -107,7 +107,7 @@ private ArrayList getEvents(Map notificat private Notification getNotification(NotificationCreationDTO notificationCreationDTO) { Notification notification = new Notification(); - notification.setNotificationId(UUIDGenerator.generateUUID()); + notification.setNotificationId(UUID.randomUUID().toString()); notification.setClientId(notificationCreationDTO.getClientId()); notification.setResourceId(notificationCreationDTO.getResourceId()); notification.setStatus(EventNotificationConstants.OPEN); From 14b8de919dba760433c99d30b5990fa9242f5fea Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Thu, 31 Oct 2024 16:17:27 +0530 Subject: [PATCH 18/23] Adding event notification implementation --- .../constants/EventNotificationConstants.java | 11 +- .../dto/EventNotificationErrorDTO.java | 53 ---- .../service/dto/EventSubscriptionDTO.java | 35 ++- .../FSEventNotificationException.java | 21 ++ .../DefaultEventCreationServiceHandler.java | 32 +- .../DefaultEventPollingServiceHandler.java | 86 +---- ...efaultEventSubscriptionServiceHandler.java | 293 +++++++----------- .../handler/EventCreationServiceHandler.java | 5 +- .../handler/EventPollingServiceHandler.java | 13 +- .../EventSubscriptionServiceHandler.java | 34 +- .../service/model/EventCreationResponse.java | 6 +- .../service/model/EventPollingResponse.java | 6 +- .../service/model/EventSubscription.java | 1 + .../model/EventSubscriptionResponse.java | 20 +- .../util/EventNotificationServiceUtil.java | 23 +- pom.xml | 14 +- 16 files changed, 255 insertions(+), 398 deletions(-) delete mode 100644 financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventNotificationErrorDTO.java diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/constants/EventNotificationConstants.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/constants/EventNotificationConstants.java index 94b1190c..78943682 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/constants/EventNotificationConstants.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/constants/EventNotificationConstants.java @@ -24,7 +24,7 @@ public class EventNotificationConstants { //Service level constants - public static final String X_WSO2_CLIENT_ID = "x-wso2-client_id"; + public static final String X_WSO2_CLIENT_ID = "x-wso2-client-id"; //Event Notification Status public static final String ACK = "ACK"; @@ -53,12 +53,14 @@ public class EventNotificationConstants { public static final String REQUEST = "REQUEST"; //Error Constants + public static final String ERROR_FIELD = "error"; + public static final String ERROR_DESCRIPTION_FIELD = "error_description"; public static final String INVALID_REQUEST = "invalid_request"; public static final String EVENT_NOTIFICATION_CREATION_ERROR = "Error occurred while saving event " + "notifications in the database"; public static final String MISSING_REQ_PAYLOAD = "No request payload found"; - public static final String MISSING_HEADER_PARAM_CLIENT_ID = "Missing header x-wso2-client_id"; - public static final String MISSING_HEADER_PARAM_RESOURCE_ID = "Missing header x-wso2-resource_id"; + public static final String MISSING_HEADER_PARAM_CLIENT_ID = "Missing header x-wso2-client-id"; + public static final String MISSING_HEADER_PARAM_RESOURCE_ID = "Missing header x-wso2-resource-id"; public static final String ERROR_IN_EVENT_POLLING_REQUEST = "Error in event polling request"; public static final String INVALID_CHARS_IN_HEADER_ERROR = "Invalid characters found in the request headers"; @@ -77,7 +79,8 @@ public class EventNotificationConstants { public static final String SUBSCRIPTION_ID_PARAM = "subscriptionId"; public static final String CALLBACK_URL_PARAM = "callbackUrl"; public static final String VERSION_PARAM = "version"; - public static final String EVENT_TYPE_PARAM = "eventTypes"; + public static final String EVENT_TYPES_PARAM = "eventTypes"; + public static final String EVENT_TYPE_PARAM = "eventType"; public static final String DATA_PARAM = "data"; public static final String DB_ERROR_UPDATING = "Database error while updating notification with ID : " + diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventNotificationErrorDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventNotificationErrorDTO.java deleted file mode 100644 index 280afad0..00000000 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventNotificationErrorDTO.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). - *

- * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.financial.services.accelerator.event.notifications.service.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Error model for Event Notifications. - */ -public class EventNotificationErrorDTO { - - private String errorDescription; - private String error; - - @JsonProperty("error_description") - public String getErrorDescription() { - - return errorDescription; - } - - public void setErrorDescription(String errorDescription) { - - this.errorDescription = errorDescription; - } - - @JsonProperty("error") - public String getError() { - - return error; - } - - public void setError(String error) { - - this.error = error; - } - -} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventSubscriptionDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventSubscriptionDTO.java index 64f896da..4b3b576d 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventSubscriptionDTO.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dto/EventSubscriptionDTO.java @@ -18,7 +18,7 @@ package org.wso2.financial.services.accelerator.event.notifications.service.dto; -import org.json.JSONObject; +import java.util.List; /** * Event Subscription DTO. @@ -26,7 +26,10 @@ public class EventSubscriptionDTO { private String clientId = null; private String subscriptionId = null; - private JSONObject requestData = null; + private String callbackUrl = null; + private String specVersion = null; + private List eventTypes = null; + private String requestData = null; public String getClientId() { return clientId; @@ -44,11 +47,35 @@ public void setSubscriptionId(String subscriptionId) { this.subscriptionId = subscriptionId; } - public JSONObject getRequestData() { + public String getCallbackUrl() { + return callbackUrl; + } + + public void setCallbackUrl(String callbackUrl) { + this.callbackUrl = callbackUrl; + } + + public String getSpecVersion() { + return specVersion; + } + + public void setSpecVersion(String specVersion) { + this.specVersion = specVersion; + } + + public List getEventTypes() { + return eventTypes; + } + + public void setEventTypes(List eventTypes) { + this.eventTypes = eventTypes; + } + + public String getRequestData() { return requestData; } - public void setRequestData(JSONObject requestData) { + public void setRequestData(String requestData) { this.requestData = requestData; } } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/exception/FSEventNotificationException.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/exception/FSEventNotificationException.java index ffb4ce62..8b7a5cdb 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/exception/FSEventNotificationException.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/exception/FSEventNotificationException.java @@ -25,11 +25,32 @@ */ public class FSEventNotificationException extends FinancialServicesException { + private int status; + public FSEventNotificationException(String message) { super(message); } + public FSEventNotificationException(int status, String message) { + + super(message); + this.status = status; + } + public FSEventNotificationException(int status, String message, Throwable e) { + + super(message, e); + this.status = status; + } + public FSEventNotificationException(String message, Throwable e) { super(message, e); } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java index d05ad002..3cfc755b 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventCreationServiceHandler.java @@ -20,6 +20,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpStatus; import org.json.JSONObject; import org.wso2.financial.services.accelerator.common.exception.ConsentManagementException; import org.wso2.financial.services.accelerator.consent.mgt.dao.models.ConsentResource; @@ -49,12 +50,12 @@ public void setEventCreationService(EventCreationService eventCreationService) { * @param notificationCreationDTO Notification details DTO * @return EventCreationResponse Response after event creation */ - public EventCreationResponse publishEvent(NotificationCreationDTO notificationCreationDTO) { + public EventCreationResponse publishEvent(NotificationCreationDTO notificationCreationDTO) + throws FSEventNotificationException { //validate if the resourceID is existing ConsentResource consentResource = null; ConsentCoreServiceImpl consentCoreService = EventNotificationServiceUtil.getConsentCoreServiceImpl(); - EventCreationResponse eventCreationResponse = new EventCreationResponse(); try { consentResource = consentCoreService.getConsent(notificationCreationDTO.getResourceId(), @@ -65,11 +66,10 @@ public EventCreationResponse publishEvent(NotificationCreationDTO notificationCr consentResource.getConsentID().replaceAll("[\r\n]", "")); } } catch (ConsentManagementException e) { - log.error("Consent Management Exception when validating the consent resource", e); - eventCreationResponse.setErrorResponse(String.format("A resource was not found for the resource " + - "id : '%s' in the database. ", notificationCreationDTO.getResourceId())); - eventCreationResponse.setStatus(EventNotificationConstants.BAD_REQUEST); - return eventCreationResponse; + String errorMsg = String.format("A resource was not found for the resource id : '%s' in the database. ", + notificationCreationDTO.getResourceId().replaceAll("[\r\n]", "")); + log.error(errorMsg, e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, errorMsg, e); } //validate if the clientID is existing @@ -77,12 +77,10 @@ public EventCreationResponse publishEvent(NotificationCreationDTO notificationCr EventNotificationServiceUtil.validateClientId(notificationCreationDTO.getClientId()); } catch (FSEventNotificationException e) { - log.error("Invalid client ID", e); - eventCreationResponse.setErrorResponse(String.format("A client was not found" + - " for the client id : '%s' in the database. ", - notificationCreationDTO.getClientId().replaceAll("[\r\n]", ""))); - eventCreationResponse.setStatus(EventNotificationConstants.BAD_REQUEST); - return eventCreationResponse; + String errorMsg = String.format("A client was not found" + " for the client id : '%s' in the database. ", + notificationCreationDTO.getClientId().replaceAll("[\r\n]", "")); + log.error(errorMsg, e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, errorMsg, e); } String registrationResponse = ""; @@ -90,16 +88,16 @@ public EventCreationResponse publishEvent(NotificationCreationDTO notificationCr registrationResponse = eventCreationService.publishEventNotification(notificationCreationDTO); JSONObject responseJSON = new JSONObject(); responseJSON.put(EventNotificationConstants.NOTIFICATIONS_ID, registrationResponse); + + EventCreationResponse eventCreationResponse = new EventCreationResponse(); eventCreationResponse.setStatus(EventNotificationConstants.CREATED); eventCreationResponse.setResponseBody(responseJSON); return eventCreationResponse; } catch (FSEventNotificationException e) { log.error("FS Event Notification Creation error", e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, "FS Event Notification Creation error", + e); } - - eventCreationResponse.setStatus(EventNotificationConstants.BAD_REQUEST); - eventCreationResponse.setErrorResponse("Error in event creation request payload"); - return eventCreationResponse; } } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java index 52118064..19237fb1 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventPollingServiceHandler.java @@ -20,7 +20,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.json.JSONArray; +import org.apache.http.HttpStatus; import org.json.JSONObject; import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; import org.wso2.financial.services.accelerator.common.util.Generated; @@ -31,11 +31,8 @@ import org.wso2.financial.services.accelerator.event.notifications.service.model.AggregatedPollingResponse; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPolling; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPollingResponse; -import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationError; import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; -import java.util.Locale; - /** * This is the service handler for event polling. */ @@ -55,35 +52,30 @@ public void setEventPollingService(EventPollingService eventPollingService) { * @param eventPollingDTO Event polling DTO * @return EventPollingResponse */ - public EventPollingResponse pollEvents(EventPollingDTO eventPollingDTO) { - - EventPollingResponse eventPollingResponse = new EventPollingResponse(); + public EventPollingResponse pollEvents(EventPollingDTO eventPollingDTO) throws FSEventNotificationException { //Validate clientID of the polling request try { EventNotificationServiceUtil.validateClientId(eventPollingDTO.getClientId()); } catch (FSEventNotificationException e) { - log.error("Invalid client ID", e); - eventPollingResponse.setStatus(EventNotificationConstants.BAD_REQUEST); - eventPollingResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, String.format("A client was not found" + - " for the client id : '%s' in the database.. ", eventPollingDTO.getClientId()))); - return eventPollingResponse; + String errorMessage = String.format("A client was not found for the client id : '%s' in the database. ", + eventPollingDTO.getClientId().replaceAll("[\r\n]", "")); + log.error(errorMessage, e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, errorMessage, e); } EventPolling eventPolling = mapEventPollingDtoToModel(eventPollingDTO); //Poll events try { AggregatedPollingResponse aggregatedPollingResponse = eventPollingService.pollEvents(eventPolling); + + EventPollingResponse eventPollingResponse = new EventPollingResponse(); eventPollingResponse.setStatus(aggregatedPollingResponse.getStatus()); eventPollingResponse.setResponseBody(getPollingResponseJSON(aggregatedPollingResponse)); return eventPollingResponse; } catch (FSEventNotificationException e) { log.error("OB Event Notification error" , e); - eventPollingResponse.setStatus(EventNotificationConstants.BAD_REQUEST); - eventPollingResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, e.getMessage())); - return eventPollingResponse; + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, e.getMessage(), e); } } @@ -108,66 +100,6 @@ private EventPolling mapEventPollingDtoToModel(EventPollingDTO eventPollingDTO) return eventPolling; } - /** - * This method will map the eventPollingRequest JSON to EventPollingDTO. - * @param eventPollingRequest JSON request for event polling - * @return EventPollingDTO - */ - public EventPollingDTO mapPollingRequest(JSONObject eventPollingRequest) { - - EventPollingDTO eventPollingDTO = new EventPollingDTO(); - eventPollingDTO.setClientId(eventPollingRequest.get(EventNotificationConstants.X_WSO2_CLIENT_ID).toString()); - - if (eventPollingRequest.length() == 0) { - - eventPollingDTO.setMaxEvents(FinancialServicesConfigParser.getInstance().getNumberOfSetsToReturn()); - - return eventPollingDTO; - } - - //Set acknowledged events to DTO - if (eventPollingRequest.has(EventNotificationConstants.ACK.toLowerCase(Locale.ROOT))) { - JSONArray acknowledgedEvents = (JSONArray) eventPollingRequest. - get(EventNotificationConstants.ACK.toLowerCase(Locale.ROOT)); - acknowledgedEvents.forEach((event -> { - eventPollingDTO.setAck(event.toString()); - })); - } - - //Set error events to DTO - if (eventPollingRequest.has(EventNotificationConstants.SET_ERRORS)) { - JSONObject errorEvents = (JSONObject) eventPollingRequest. - get(EventNotificationConstants.SET_ERRORS); - errorEvents.keySet().forEach(errorEvent -> { - JSONObject errorEventInformation = (JSONObject) errorEvents.get(errorEvent); - NotificationError notificationError = getNotificationError(errorEventInformation); - notificationError.setNotificationId(errorEvent); - eventPollingDTO.setErrors(errorEvent, notificationError); - }); - } - - //Set maxEvents count to return - if (eventPollingRequest.has(EventNotificationConstants.MAX_EVENTS)) { - eventPollingDTO.setMaxEvents(Integer.parseInt(eventPollingRequest. - get(EventNotificationConstants.MAX_EVENTS).toString())); - } else { - eventPollingDTO.setMaxEvents(FinancialServicesConfigParser.getInstance().getNumberOfSetsToReturn()); - } - - return eventPollingDTO; - } - - @Generated(message = "Private method tested when testing the invoked method") - private NotificationError getNotificationError(JSONObject errorEvent) { - - NotificationError notificationError = new NotificationError(); - notificationError.setErrorCode(errorEvent.get( - EventNotificationConstants.ERROR.toLowerCase(Locale.ROOT)).toString()); - notificationError.setErrorDescription( - errorEvent.get(EventNotificationConstants.DESCRIPTION).toString()); - return notificationError; - } - @Generated(message = "Private method tested when testing the invoked method") private JSONObject getPollingResponseJSON(AggregatedPollingResponse aggregatedPollingResponse) { diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java index 22c3a93e..6c2fe760 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java @@ -45,91 +45,79 @@ public void setEventSubscriptionService(EventSubscriptionService eventSubscripti this.eventSubscriptionService = eventSubscriptionService; } - /** - * This method is used to create event subscriptions. - * - * @param eventSubscriptionRequestDto Event Subscription DTO - * @return EventSubscriptionResponse Event Subscription Response - */ - public EventSubscriptionResponse createEventSubscription(EventSubscriptionDTO eventSubscriptionRequestDto) { - EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); - - EventSubscriptionResponse clientIdValidation = validateClientId(eventSubscriptionRequestDto.getClientId()); - // check whether clientIdValidation is not null, then return the error response - if (clientIdValidation != null) { - return clientIdValidation; - } - - EventSubscription eventSubscription = mapEventSubscriptionDtoToModel(eventSubscriptionRequestDto); + @Override + public EventSubscriptionResponse createEventSubscription(EventSubscriptionDTO eventSubscriptionRequestDto) + throws FSEventNotificationException { + + try { + EventNotificationServiceUtil.validateClientId(eventSubscriptionRequestDto.getClientId()); + + } catch (FSEventNotificationException e) { + String errorMsg = String.format("A client was not found" + " for the client id : '%s' in the database. ", + eventSubscriptionRequestDto.getClientId().replaceAll("[\r\n]", "")); + log.error(errorMsg, e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, errorMsg, e); + } + + try { + EventSubscription eventSubscriptionCreateResponse = eventSubscriptionService. + createEventSubscription(mapEventSubscriptionDtoToModel(eventSubscriptionRequestDto)); + + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + eventSubscriptionResponse.setResponseStatus(HttpStatus.SC_CREATED); + eventSubscriptionResponse + .setResponseBody(mapSubscriptionModelToResponseJson(eventSubscriptionCreateResponse)); + return eventSubscriptionResponse; + } catch (FSEventNotificationException e) { + log.error("Error occurred while creating event subscription", e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, e.getMessage(), e); + } + } + + @Override + public EventSubscriptionResponse getEventSubscription(String clientId, String subscriptionId) + throws FSEventNotificationException { try { - EventSubscription createEventSubscriptionResponse = eventSubscriptionService. - createEventSubscription(eventSubscription); - eventSubscriptionResponse.setStatus(HttpStatus.SC_CREATED); - eventSubscriptionResponse. - setResponseBody(mapSubscriptionModelToResponseJson(createEventSubscriptionResponse)); - return eventSubscriptionResponse; - } catch (FSEventNotificationException e) { - log.error("Error occurred while creating event subscription", e); - eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, e.getMessage())); - return eventSubscriptionResponse; - } - - } - - /** - * This method is used to retrieve a single event subscription. - * - * @param clientId Client ID of the subscription created - * @param subscriptionId Subscription ID of the subscription created - * @return EventSubscriptionResponse Event Subscription Response containing subscription - * details for the given subscription ID - */ - public EventSubscriptionResponse getEventSubscription(String clientId, String subscriptionId) { - EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + EventNotificationServiceUtil.validateClientId(clientId); - EventSubscriptionResponse clientIdValidation = validateClientId(clientId); - // check whether clientIdValidation is not null, then return the error response - if (clientIdValidation != null) { - return clientIdValidation; + } catch (FSEventNotificationException e) { + String errorMsg = String.format("A client was not found" + " for the client id : '%s' in the database. ", + clientId.replaceAll("[\r\n]", "")); + log.error(errorMsg, e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, errorMsg, e); } try { EventSubscription eventSubscription = eventSubscriptionService. getEventSubscriptionBySubscriptionId(subscriptionId); - eventSubscriptionResponse.setStatus(HttpStatus.SC_OK); + + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + eventSubscriptionResponse.setResponseStatus(HttpStatus.SC_OK); eventSubscriptionResponse.setResponseBody(mapSubscriptionModelToResponseJson(eventSubscription)); return eventSubscriptionResponse; } catch (FSEventNotificationException e) { log.error("Error occurred while retrieving event subscription", e); if (e.getMessage().equals(EventNotificationConstants.EVENT_SUBSCRIPTION_NOT_FOUND)) { - eventSubscriptionResponse.setStatus(HttpStatus.SC_BAD_REQUEST); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, e.getMessage(), e); } else { - eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + throw new FSEventNotificationException(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e); } - return eventSubscriptionResponse; } } - /** - * This method is used to retrieve all event subscriptions of a client. - * - * @param clientId Client ID - * @return EventSubscriptionResponse Event Subscription Response containing all the subscriptions - */ - public EventSubscriptionResponse getAllEventSubscriptions(String clientId) { - EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + @Override + public EventSubscriptionResponse getAllEventSubscriptions(String clientId) + throws FSEventNotificationException { + + try { + EventNotificationServiceUtil.validateClientId(clientId); - EventSubscriptionResponse clientIdValidation = validateClientId(clientId); - // check whether clientIdValidation is not null, then return the error response - if (clientIdValidation != null) { - return clientIdValidation; + } catch (FSEventNotificationException e) { + String errorMsg = String.format("A client was not found" + " for the client id : '%s' in the database. ", + clientId.replaceAll("[\r\n]", "")); + log.error(errorMsg, e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, errorMsg, e); } try { @@ -139,33 +127,30 @@ public EventSubscriptionResponse getAllEventSubscriptions(String clientId) { for (EventSubscription eventSubscription : eventSubscriptionList) { eventSubscriptionResponseList.add(mapSubscriptionModelToResponseJson(eventSubscription)); } - eventSubscriptionResponse.setStatus(HttpStatus.SC_OK); + + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + eventSubscriptionResponse.setResponseStatus(HttpStatus.SC_OK); eventSubscriptionResponse.setResponseBody(eventSubscriptionResponseList); return eventSubscriptionResponse; } catch (FSEventNotificationException e) { log.error("Error occurred while retrieving event subscriptions", e); - eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, e.getMessage())); - return eventSubscriptionResponse; + throw new FSEventNotificationException(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e); + } } - /** - * This method is used to retrieve all event subscriptions by event type. - * - * @param clientId Client ID - * @param eventType Event Type to retrieve subscriptions - * @return EventSubscriptionResponse Event Subscription Response containing subscriptions per specified - * event type - */ - public EventSubscriptionResponse getEventSubscriptionsByEventType(String clientId, String eventType) { - EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + @Override + public EventSubscriptionResponse getEventSubscriptionsByEventType(String clientId, String eventType) + throws FSEventNotificationException { + + try { + EventNotificationServiceUtil.validateClientId(clientId); - EventSubscriptionResponse clientIdValidation = validateClientId(clientId); - // check whether clientIdValidation is not null, then return the error response - if (clientIdValidation != null) { - return clientIdValidation; + } catch (FSEventNotificationException e) { + String errorMsg = String.format("A client was not found" + " for the client id : '%s' in the database. ", + clientId.replaceAll("[\r\n]", "")); + log.error(errorMsg, e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, errorMsg, e); } try { @@ -175,32 +160,32 @@ public EventSubscriptionResponse getEventSubscriptionsByEventType(String clientI for (EventSubscription eventSubscription : eventSubscriptionList) { eventSubscriptionResponseList.add(mapSubscriptionModelToResponseJson(eventSubscription)); } - eventSubscriptionResponse.setStatus(HttpStatus.SC_OK); + + + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + eventSubscriptionResponse.setResponseStatus(HttpStatus.SC_OK); eventSubscriptionResponse.setResponseBody(eventSubscriptionResponseList); return eventSubscriptionResponse; } catch (FSEventNotificationException e) { log.error("Error occurred while retrieving event subscriptions", e); - eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, e.getMessage())); - return eventSubscriptionResponse; + throw new FSEventNotificationException(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e); } } - /** - * This method is used to update an event subscription. - * - * @param eventSubscriptionUpdateRequestDto Event Subscription Update Request DTO - * @return EventSubscriptionResponse Event Subscription Response containing the updated subscription - */ - public EventSubscriptionResponse updateEventSubscription(EventSubscriptionDTO eventSubscriptionUpdateRequestDto) { + @Override + public EventSubscriptionResponse updateEventSubscription(EventSubscriptionDTO eventSubscriptionUpdateRequestDto) + throws FSEventNotificationException { + EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); - EventSubscriptionResponse clientIdValidation = validateClientId(eventSubscriptionUpdateRequestDto. - getClientId()); - // check whether clientIdValidation is not null, then return the error response - if (clientIdValidation != null) { - return clientIdValidation; + try { + EventNotificationServiceUtil.validateClientId(eventSubscriptionUpdateRequestDto.getClientId()); + + } catch (FSEventNotificationException e) { + String errorMsg = String.format("A client was not found" + " for the client id : '%s' in the database. ", + eventSubscriptionUpdateRequestDto.getClientId().replaceAll("[\r\n]", "")); + log.error(errorMsg, e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, errorMsg, e); } EventSubscription eventSubscription = mapEventSubscriptionDtoToModel(eventSubscriptionUpdateRequestDto); @@ -208,13 +193,10 @@ public EventSubscriptionResponse updateEventSubscription(EventSubscriptionDTO ev try { Boolean isUpdated = eventSubscriptionService.updateEventSubscription(eventSubscription); if (!isUpdated) { - eventSubscriptionResponse.setStatus(HttpStatus.SC_BAD_REQUEST); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, - "Event subscription not found.")); - return eventSubscriptionResponse; + log.error("Event subscription not found."); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, "Event subscription not found."); } - eventSubscriptionResponse.setStatus(HttpStatus.SC_OK); + eventSubscriptionResponse.setResponseStatus(HttpStatus.SC_OK); EventSubscription eventSubscriptionUpdateResponse = eventSubscriptionService. getEventSubscriptionBySubscriptionId(eventSubscriptionUpdateRequestDto.getSubscriptionId()); eventSubscriptionResponse. @@ -222,67 +204,38 @@ public EventSubscriptionResponse updateEventSubscription(EventSubscriptionDTO ev return eventSubscriptionResponse; } catch (FSEventNotificationException e) { log.error("Error occurred while updating event subscription", e); - eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, e.getMessage())); - return eventSubscriptionResponse; + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, e.getMessage(), e); } } - /** - * This method is used to delete an event subscription. - * - * @param clientId Client ID - * @param subscriptionId Subscription ID to be deleted - * @return EventSubscriptionResponse Event Subscription Response containing the deleted subscription - */ - public EventSubscriptionResponse deleteEventSubscription(String clientId, String subscriptionId) { - EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); + @Override + public EventSubscriptionResponse deleteEventSubscription(String clientId, String subscriptionId) + throws FSEventNotificationException { - EventSubscriptionResponse clientIdValidation = validateClientId(clientId); - // check whether clientIdValidation is not null, then return the error response - if (clientIdValidation != null) { - return clientIdValidation; + try { + EventNotificationServiceUtil.validateClientId(clientId); + + } catch (FSEventNotificationException e) { + String errorMsg = String.format("A client was not found" + " for the client id : '%s' in the database. ", + clientId.replaceAll("[\r\n]", "")); + log.error(errorMsg, e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, errorMsg, e); } + try { Boolean isDeleted = eventSubscriptionService.deleteEventSubscription(subscriptionId); if (!isDeleted) { - eventSubscriptionResponse.setStatus(HttpStatus.SC_BAD_REQUEST); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, - "Event subscription not found")); - return eventSubscriptionResponse; + log.error("Event subscription not found"); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, "Event subscription not found"); } - eventSubscriptionResponse.setStatus(HttpStatus.SC_NO_CONTENT); - return eventSubscriptionResponse; - } catch (FSEventNotificationException e) { - log.error("Error occurred while deleting event subscription", e); - eventSubscriptionResponse.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, e.getMessage())); - return eventSubscriptionResponse; - } - } - /** - * This method is used to validate the client ID. - * - * @param clientId Client ID - * @return EventSubscriptionResponse Return EventSubscriptionResponse if the client ID is - * invalid, if the client ID is valid, null will be returned. - */ - private EventSubscriptionResponse validateClientId(String clientId) { - try { - EventNotificationServiceUtil.validateClientId(clientId); - } catch (FSEventNotificationException e) { - log.error("Invalid client ID", e); EventSubscriptionResponse eventSubscriptionResponse = new EventSubscriptionResponse(); - eventSubscriptionResponse.setStatus(HttpStatus.SC_BAD_REQUEST); - eventSubscriptionResponse.setErrorResponse(EventNotificationServiceUtil.getErrorDTO( - EventNotificationConstants.INVALID_REQUEST, e.getMessage())); + eventSubscriptionResponse.setResponseStatus(HttpStatus.SC_NO_CONTENT); return eventSubscriptionResponse; + } catch (FSEventNotificationException e) { + log.error("Error occurred while deleting event subscription", e); + throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, e.getMessage(), e); } - return null; } /** @@ -296,33 +249,19 @@ private EventSubscription mapEventSubscriptionDtoToModel(EventSubscriptionDTO ev EventSubscription eventSubscription = new EventSubscription(); eventSubscription.setSubscriptionId(eventSubscriptionDTO.getSubscriptionId()); - - JSONObject payload = eventSubscriptionDTO.getRequestData(); - List eventTypes = new ArrayList<>(); - Object eventTypesObj = payload.get(EventNotificationConstants.EVENT_TYPE_PARAM); - if (eventTypesObj instanceof List) { - List eventTypesList = (List) eventTypesObj; - for (Object item : eventTypesList) { - if (item instanceof String) { - eventTypes.add((String) item); - } - } - } - eventSubscription.setEventTypes(eventTypes); - eventSubscription.setCallbackUrl(payload.get(EventNotificationConstants.CALLBACK_URL_PARAM) != null ? - payload.get(EventNotificationConstants.CALLBACK_URL_PARAM).toString() : null); - eventSubscription.setSpecVersion(payload.get(EventNotificationConstants.VERSION_PARAM) != null ? - payload.get(EventNotificationConstants.VERSION_PARAM).toString() : null); + eventSubscription.setEventTypes(eventSubscriptionDTO.getEventTypes()); + eventSubscription.setCallbackUrl(eventSubscriptionDTO.getCallbackUrl()); + eventSubscription.setSpecVersion(eventSubscriptionDTO.getSpecVersion()); eventSubscription.setClientId(eventSubscriptionDTO.getClientId()); - eventSubscription.setRequestData(payload.toString()); + eventSubscription.setRequestData(eventSubscriptionDTO.getRequestData()); return eventSubscription; } /** - * This method is used to create the response JSON object from the event subscription model. + * This method is used to create the response model from the event subscription model. * * @param eventSubscription Event Subscription Model - * @return JSONObject containing mapped subscription + * @return EventSubscriptionResponse containing mapped subscription */ public JSONObject mapSubscriptionModelToResponseJson(EventSubscription eventSubscription) { JSONObject responsePayload = new JSONObject(); @@ -338,7 +277,7 @@ public JSONObject mapSubscriptionModelToResponseJson(EventSubscription eventSubs responsePayload.put(EventNotificationConstants.VERSION_PARAM, eventSubscription.getSpecVersion()); } if (eventSubscription.getEventTypes() != null) { - responsePayload.put(EventNotificationConstants.EVENT_TYPE_PARAM, eventSubscription.getEventTypes()); + responsePayload.put(EventNotificationConstants.EVENT_TYPES_PARAM, eventSubscription.getEventTypes()); } return responsePayload; } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventCreationServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventCreationServiceHandler.java index 85a861a7..64480acc 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventCreationServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventCreationServiceHandler.java @@ -19,6 +19,7 @@ package org.wso2.financial.services.accelerator.event.notifications.service.handler; import org.wso2.financial.services.accelerator.event.notifications.service.dto.NotificationCreationDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventCreationResponse; /** @@ -32,7 +33,9 @@ public interface EventCreationServiceHandler { * method that is used to persist data into the FS_NOTIFICATION and FS_NOTIFICATION_EVENT tables. * @param notificationCreationDTO Notification details DTO * @return For successful request the API will return a JSON with the notificationID + * @throws FSEventNotificationException Exception when creating event */ - EventCreationResponse publishEvent(NotificationCreationDTO notificationCreationDTO); + EventCreationResponse publishEvent(NotificationCreationDTO notificationCreationDTO) + throws FSEventNotificationException; } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventPollingServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventPollingServiceHandler.java index 2b5fc735..a87639a1 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventPollingServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventPollingServiceHandler.java @@ -18,8 +18,8 @@ package org.wso2.financial.services.accelerator.event.notifications.service.handler; -import org.json.JSONObject; import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventPollingDTO; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventPollingResponse; /** @@ -34,15 +34,8 @@ public interface EventPollingServiceHandler { * Also, can be used to POLL for available OPEN notifications. * @param eventPollingDTO Event polling DTO * @return EventPollingResponse to the polling endpoint. + * @throws FSEventNotificationException Exception when polling events */ - EventPollingResponse pollEvents(EventPollingDTO eventPollingDTO); - - /** - * This method is used to map the eventPollingRequest to EventPollingDTO. - * @param eventPollingRequest JSON request for event polling - * @return eventPollingDTO with the request parameters. - */ - EventPollingDTO mapPollingRequest(JSONObject eventPollingRequest); + EventPollingResponse pollEvents(EventPollingDTO eventPollingDTO) throws FSEventNotificationException; } - diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventSubscriptionServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventSubscriptionServiceHandler.java index d755bd14..6a5178fa 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventSubscriptionServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/EventSubscriptionServiceHandler.java @@ -18,9 +18,8 @@ package org.wso2.financial.services.accelerator.event.notifications.service.handler; -import org.json.JSONObject; import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventSubscriptionDTO; -import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscription; +import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; import org.wso2.financial.services.accelerator.event.notifications.service.model.EventSubscriptionResponse; /** @@ -37,8 +36,10 @@ public interface EventSubscriptionServiceHandler { * * @param eventSubscriptionRequestDto The request DTO that contains the subscription details. * @return For successful request the API will return a JSON with the subscriptionId + * @throws FSEventNotificationException Exception when creating event subscription. */ - EventSubscriptionResponse createEventSubscription(EventSubscriptionDTO eventSubscriptionRequestDto); + EventSubscriptionResponse createEventSubscription(EventSubscriptionDTO eventSubscriptionRequestDto) + throws FSEventNotificationException; /** * This method is used to retrieve an event subscription by its subscription ID. @@ -46,16 +47,19 @@ public interface EventSubscriptionServiceHandler { * @param clientId The client ID of the subscription. * @param subscriptionId The subscription ID of the subscription. * @return For successful request the API will return a JSON with the retrieved Subscription. + * @throws FSEventNotificationException Exception when retrieving event subscription. */ - EventSubscriptionResponse getEventSubscription(String clientId, String subscriptionId); + EventSubscriptionResponse getEventSubscription(String clientId, String subscriptionId) + throws FSEventNotificationException; /** * This method is used to retrieve all event subscriptions of a client. * * @param clientId The client ID of the subscription. * @return For successful request the API will return a JSON with the retrieved Subscriptions. + * @throws FSEventNotificationException Exception when retrieving event subscriptions. */ - EventSubscriptionResponse getAllEventSubscriptions(String clientId); + EventSubscriptionResponse getAllEventSubscriptions(String clientId) throws FSEventNotificationException; /** * This method is used to retrieve all event subscriptions by event type. @@ -63,16 +67,20 @@ public interface EventSubscriptionServiceHandler { * @param clientId The client ID of the subscription. * @param eventType The event type that needs to be subscribed by the retrieving subscriptions. * @return For successful request the API will return a JSON with the retrieved Subscriptions. + * @throws FSEventNotificationException Exception when retrieving event subscriptions. */ - EventSubscriptionResponse getEventSubscriptionsByEventType(String clientId, String eventType); + EventSubscriptionResponse getEventSubscriptionsByEventType(String clientId, String eventType) + throws FSEventNotificationException; /** * This method is used to update an event subscription. * * @param eventSubscriptionUpdateRequestDto The request DTO that contains the updating subscription details. * @return For successful request the API will return a JSON with the updated Subscription. + * @throws FSEventNotificationException Exception when updating event subscription. */ - EventSubscriptionResponse updateEventSubscription(EventSubscriptionDTO eventSubscriptionUpdateRequestDto); + EventSubscriptionResponse updateEventSubscription(EventSubscriptionDTO eventSubscriptionUpdateRequestDto) + throws FSEventNotificationException; /** * This method is used to delete an event subscription. @@ -80,15 +88,9 @@ public interface EventSubscriptionServiceHandler { * @param clientId The client ID of the subscription. * @param subscriptionId The subscription ID of the subscription. * @return For successful request the API will an OK response. + * @throws FSEventNotificationException Exception when deleting event subscription. */ - EventSubscriptionResponse deleteEventSubscription(String clientId, String subscriptionId); - - /** - * This method is used to create the response JSON object from the event subscription model. - * - * @param eventSubscription The event subscription model. - * @return JSONObject - */ - JSONObject mapSubscriptionModelToResponseJson(EventSubscription eventSubscription); + EventSubscriptionResponse deleteEventSubscription(String clientId, String subscriptionId) + throws FSEventNotificationException; } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventCreationResponse.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventCreationResponse.java index 394b3726..ad1ccd5d 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventCreationResponse.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventCreationResponse.java @@ -27,13 +27,13 @@ public class EventCreationResponse { private String status; private JSONObject responseBody; - private String errorResponse; + private JSONObject errorResponse; - public String getErrorResponse() { + public JSONObject getErrorResponse() { return errorResponse; } - public void setErrorResponse(String errorResponse) { + public void setErrorResponse(JSONObject errorResponse) { this.errorResponse = errorResponse; } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPollingResponse.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPollingResponse.java index fdf494b2..5ab64a4a 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPollingResponse.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventPollingResponse.java @@ -27,7 +27,7 @@ public class EventPollingResponse { private String status; private JSONObject responseBody; - private Object errorResponse; + private JSONObject errorResponse; public String getStatus() { return status; @@ -45,11 +45,11 @@ public void setResponseBody(JSONObject responseBody) { this.responseBody = responseBody; } - public Object getErrorResponse() { + public JSONObject getErrorResponse() { return errorResponse; } - public void setErrorResponse(Object errorResponse) { + public void setErrorResponse(JSONObject errorResponse) { this.errorResponse = errorResponse; } } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscription.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscription.java index 9776d179..a870be05 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscription.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscription.java @@ -24,6 +24,7 @@ * This is the Event Subscription Model. */ public class EventSubscription { + private String subscriptionId = null; private String clientId = null; private String callbackUrl = null; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscriptionResponse.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscriptionResponse.java index 1809db08..e4f727a2 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscriptionResponse.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/model/EventSubscriptionResponse.java @@ -23,16 +23,16 @@ */ public class EventSubscriptionResponse { - private int status; + private int responseStatus; private Object responseBody; - private Object errorResponse; - public int getStatus() { - return status; + + public int getResponseStatus() { + return responseStatus; } - public void setStatus(int status) { - this.status = status; + public void setResponseStatus(int responseStatus) { + this.responseStatus = responseStatus; } public Object getResponseBody() { @@ -43,12 +43,4 @@ public void setResponseBody(Object responseBody) { this.responseBody = responseBody; } - public Object getErrorResponse() { - return errorResponse; - } - - public void setErrorResponse(Object errorResponse) { - this.errorResponse = errorResponse; - } - } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java index 87a35d18..94427176 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java @@ -33,9 +33,7 @@ import org.wso2.financial.services.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; import org.wso2.financial.services.accelerator.event.notifications.service.EventNotificationGenerator; import org.wso2.financial.services.accelerator.event.notifications.service.constants.EventNotificationConstants; -import org.wso2.financial.services.accelerator.event.notifications.service.dto.EventNotificationErrorDTO; import org.wso2.financial.services.accelerator.event.notifications.service.exception.FSEventNotificationException; -import org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventCreationServiceHandler; import java.util.Optional; @@ -142,26 +140,17 @@ public static String getCallbackURL(String clientID) { return "http://localhost:8080/sample-tpp-server"; } - /** - * Get the default event creation service handler. - * - * @return DefaultEventCreationServiceHandler - */ - public static DefaultEventCreationServiceHandler getDefaultEventCreationServiceHandler() { - return new DefaultEventCreationServiceHandler(); - } - /** * Method to map Event subscription Service error to API response. * * @param error Error code * @param errorDescription Error description - * @return EventNotificationErrorDTO + * @return String error response */ - public static EventNotificationErrorDTO getErrorDTO(String error, String errorDescription) { - EventNotificationErrorDTO eventNotificationErrorDTO = new EventNotificationErrorDTO(); - eventNotificationErrorDTO.setError(error); - eventNotificationErrorDTO.setErrorDescription(errorDescription); - return eventNotificationErrorDTO; + public static String getErrorDTO(String error, String errorDescription) { + JSONObject eventNotificationError = new JSONObject(); + eventNotificationError.put(EventNotificationConstants.ERROR_FIELD, error); + eventNotificationError.put(EventNotificationConstants.ERROR_DESCRIPTION_FIELD, errorDescription); + return eventNotificationError.toString(); } } diff --git a/pom.xml b/pom.xml index 8eb777e2..fbfd3a29 100644 --- a/pom.xml +++ b/pom.xml @@ -388,6 +388,11 @@ jackson-annotations ${jackson.databinding.version} + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.databinding.version} + org.springframework spring-web @@ -521,6 +526,11 @@ org.wso2.financial.services.accelerator.identity.extensions ${project.version} + + org.wso2.financial.services.accelerator + org.wso2.financial.services.accelerator.event.notifications.service + ${project.version} + org.testng @@ -595,9 +605,9 @@ 1.0.0.wso2v3 1.6.1 2.1.1 - 5.1.2.RELEASE + 5.3.39-wso2v1 2.5 - 3.3.7 + 3.5.9 1.2 1.7.21 3.0.0.v201112011016 From 043acf8befc8d57340e0b50cc55c80f9a774763f Mon Sep 17 00:00:00 2001 From: Ashirwada Date: Thu, 31 Oct 2024 21:29:09 +0530 Subject: [PATCH 19/23] Adding event notification implementation --- .../service/dao/EventSubscriptionDAOImpl.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAOImpl.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAOImpl.java index a66db38a..d050d5ac 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAOImpl.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/dao/EventSubscriptionDAOImpl.java @@ -126,9 +126,7 @@ public EventSubscription getEventSubscriptionBySubscriptionId(Connection connect eventTypes.add(eventType); } } - if (!eventTypes.isEmpty()) { - retrievedSubscription.setEventTypes(eventTypes); - } + retrievedSubscription.setEventTypes(eventTypes); } else { log.error("No event notification subscription found for the given subscription id."); throw new FSEventNotificationException( @@ -171,9 +169,7 @@ public List getEventSubscriptionsByClientId(Connection connec break; } } - if (!eventTypes.isEmpty()) { - eventSubscription.setEventTypes(eventTypes); - } + eventSubscription.setEventTypes(eventTypes); retrievedSubscriptions.add(eventSubscription); } log.debug("Retrieved the event notification subscriptions successfully."); @@ -216,9 +212,7 @@ public List getEventSubscriptionsByEventType(Connection conne break; } } - if (!eventTypes.isEmpty()) { - eventSubscription.setEventTypes(eventTypes); - } + eventSubscription.setEventTypes(eventTypes); retrievedSubscriptions.add(eventSubscription); } log.debug("Retrieved the event notification subscriptions successfully."); From 247fe15ce5007a468941c9ad2df3af6a7185c0fe Mon Sep 17 00:00:00 2001 From: Ashi1993 Date: Tue, 12 Nov 2024 10:30:13 +0530 Subject: [PATCH 20/23] fixed review comment --- .../fs-is/carbon-home/repository/conf/financial-services.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/conf/financial-services.xml b/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/conf/financial-services.xml index bc557561..4177a2d0 100644 --- a/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/conf/financial-services.xml +++ b/financial-services-accelerator/accelerators/fs-is/carbon-home/repository/conf/financial-services.xml @@ -93,7 +93,7 @@ org.wso2.financial.services.accelerator.event.notifications.service.handler.DefaultEventSubscriptionServiceHandler true - {{financial_services.event.notifications.set_txn_claim_included}} + true true true From 782acc3a382f5f5f2905b4f3d07b83526261b666 Mon Sep 17 00:00:00 2001 From: Ashi1993 Date: Wed, 13 Nov 2024 09:13:18 +0530 Subject: [PATCH 21/23] fix review comments --- .../service/DefaultEventNotificationGenerator.java | 4 +--- .../DefaultEventSubscriptionServiceHandler.java | 6 +++--- .../service/util/EventNotificationServiceUtil.java | 12 ------------ 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java index 1a62cc22..4ec4c6d4 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/DefaultEventNotificationGenerator.java @@ -28,7 +28,6 @@ import org.wso2.financial.services.accelerator.event.notifications.service.model.Notification; import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationEvent; import org.wso2.financial.services.accelerator.event.notifications.service.model.NotificationResponse; -import org.wso2.financial.services.accelerator.event.notifications.service.util.EventNotificationServiceUtil; import java.time.Instant; import java.util.List; @@ -67,9 +66,8 @@ public NotificationResponse generateEventNotificationBody(Notification notificat @Generated(message = "Excluded from tests as using a util method from a different package") public String generateEventNotification(JsonNode jsonNode) throws FSEventNotificationException { - String payload = EventNotificationServiceUtil.getCustomNotificationPayload(jsonNode); try { - return JWTUtils.signJWTWithDefaultKey(payload); + return JWTUtils.signJWTWithDefaultKey(jsonNode.toString()); } catch (Exception e) { log.error("Error while signing the JWT token", e); throw new FSEventNotificationException("Error while signing the JWT token", e); diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java index 6c2fe760..7488ec42 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/handler/DefaultEventSubscriptionServiceHandler.java @@ -70,7 +70,7 @@ public EventSubscriptionResponse createEventSubscription(EventSubscriptionDTO ev return eventSubscriptionResponse; } catch (FSEventNotificationException e) { log.error("Error occurred while creating event subscription", e); - throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, e.getMessage(), e); + throw new FSEventNotificationException(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e); } } @@ -204,7 +204,7 @@ public EventSubscriptionResponse updateEventSubscription(EventSubscriptionDTO ev return eventSubscriptionResponse; } catch (FSEventNotificationException e) { log.error("Error occurred while updating event subscription", e); - throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, e.getMessage(), e); + throw new FSEventNotificationException(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e); } } @@ -234,7 +234,7 @@ public EventSubscriptionResponse deleteEventSubscription(String clientId, String return eventSubscriptionResponse; } catch (FSEventNotificationException e) { log.error("Error occurred while deleting event subscription", e); - throw new FSEventNotificationException(HttpStatus.SC_BAD_REQUEST, e.getMessage(), e); + throw new FSEventNotificationException(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e); } } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java index 94427176..b3916f14 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/util/EventNotificationServiceUtil.java @@ -18,7 +18,6 @@ package org.wso2.financial.services.accelerator.event.notifications.service.util; -import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -72,17 +71,6 @@ public static EventNotificationGenerator getEventNotificationGenerator() { // return realtimeEventNotificationRequestGenerator; // } - /** - * Method to modify event notification payload with custom eventValues. - * - * @param jsonNode Json Node to convert - * @return String eventNotificationPayload - */ - public static String getCustomNotificationPayload(JsonNode jsonNode) { - - return jsonNode.toString(); - } - /** * Method to get event JSON from eventInformation payload string. * @param eventInformation String event Information From 9fdacbc16042a3fdf8499672b5b76a49be0c68b2 Mon Sep 17 00:00:00 2001 From: Ashi1993 Date: Tue, 3 Dec 2024 14:27:03 +0530 Subject: [PATCH 22/23] fix review comments --- .../service/internal/EventNotificationComponent.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationComponent.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationComponent.java index 547d5f15..d6124767 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationComponent.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/internal/EventNotificationComponent.java @@ -41,9 +41,7 @@ public class EventNotificationComponent { @Activate protected void activate(ComponentContext context) { - if (log.isDebugEnabled()) { - log.debug("Event Notification Service Component Activated"); - } + log.debug("Event Notification Service Component Activated"); // Check if realtime event notification enabled if (FinancialServicesConfigParser.getInstance().isRealtimeEventNotificationEnabled()) { From 4e3160f15e06259be81743872bbf3ecd1c33db7e Mon Sep 17 00:00:00 2001 From: Ashi1993 Date: Wed, 4 Dec 2024 08:40:08 +0530 Subject: [PATCH 23/23] fix review comments --- .../notifications/service/EventSubscriptionService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventSubscriptionService.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventSubscriptionService.java index 4a4f6499..0c7db316 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventSubscriptionService.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.event.notifications.service/src/main/java/org/wso2/financial/services/accelerator/event/notifications/service/EventSubscriptionService.java @@ -70,6 +70,7 @@ public EventSubscription createEventSubscription(EventSubscription eventSubscrip DatabaseUtils.rollbackTransaction(connection); throw new FSEventNotificationException(EventNotificationConstants.ERROR_STORING_EVENT_SUBSCRIPTION, e); } finally { + log.debug(EventNotificationConstants.DATABASE_CONNECTION_CLOSE_LOG_MSG); DatabaseUtils.closeConnection(connection); } } @@ -102,6 +103,7 @@ public EventSubscription getEventSubscriptionBySubscriptionId(String subscriptio DatabaseUtils.rollbackTransaction(connection); throw new FSEventNotificationException(e.getMessage(), e); } finally { + log.debug(EventNotificationConstants.DATABASE_CONNECTION_CLOSE_LOG_MSG); DatabaseUtils.closeConnection(connection); } } @@ -132,6 +134,7 @@ public List getEventSubscriptionsByClientId(String clientId) DatabaseUtils.rollbackTransaction(connection); throw new FSEventNotificationException(e.getMessage(), e); } finally { + log.debug(EventNotificationConstants.DATABASE_CONNECTION_CLOSE_LOG_MSG); DatabaseUtils.closeConnection(connection); } } @@ -163,6 +166,7 @@ public List getEventSubscriptionsByEventType(String eventType DatabaseUtils.rollbackTransaction(connection); throw new FSEventNotificationException(e.getMessage(), e); } finally { + log.debug(EventNotificationConstants.DATABASE_CONNECTION_CLOSE_LOG_MSG); DatabaseUtils.closeConnection(connection); } } @@ -223,6 +227,7 @@ public Boolean updateEventSubscription(EventSubscription eventSubscription) DatabaseUtils.rollbackTransaction(connection); throw new FSEventNotificationException(e.getMessage(), e); } finally { + log.debug(EventNotificationConstants.DATABASE_CONNECTION_CLOSE_LOG_MSG); DatabaseUtils.closeConnection(connection); } } @@ -254,6 +259,7 @@ public Boolean deleteEventSubscription(String subscriptionId) throws FSEventNoti DatabaseUtils.rollbackTransaction(connection); throw new FSEventNotificationException(e.getMessage(), e); } finally { + log.debug(EventNotificationConstants.DATABASE_CONNECTION_CLOSE_LOG_MSG); DatabaseUtils.closeConnection(connection); } }