From f5e4f0c2d97ccda2ea7d2c94cbebe70a2f4c9ecb Mon Sep 17 00:00:00 2001 From: Nethmi Ranasinghe Date: Sat, 29 Mar 2025 12:40:31 +0530 Subject: [PATCH 1/2] Add mandatory property validation to API import flow --- .../carbon/apimgt/impl/APIProviderImpl.java | 15 +++++++ .../carbon/apimgt/impl/utils/APIUtil.java | 44 +++++++++++++++++-- .../publisher/v1/impl/ApisApiServiceImpl.java | 4 +- .../v1/impl/SettingsApiServiceImpl.java | 2 +- 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java index 877631691d59..d3274861fd24 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java @@ -3342,6 +3342,21 @@ public APIStateChangeResponse changeLifeCycleStatus(String orgId, ApiTypeWrapper boolean isApiProduct = apiTypeWrapper.isAPIProduct(); String workflowType; + if (!apiTypeWrapper.isAPIProduct()){ + // validate custom API properties + if (StringUtils.equals(action, APIConstants.LC_PUBLISH_LC_STATE)) { + org.json.simple.JSONArray customProperties = APIUtil.getCustomProperties(this.tenantDomain); + List errorProperties = APIUtil.validateMandatoryProperties(customProperties, + apiTypeWrapper.getApi().getAdditionalProperties()); + + if (!errorProperties.isEmpty()) { + String errorString = " : " + String.join(", ", errorProperties); + throw new APIManagementException(errorString, ExceptionCodes.from(ExceptionCodes + .ERROR_WHILE_UPDATING_MANDATORY_PROPERTIES)); + } + } + } + if (isApiProduct) { APIProduct apiProduct = apiTypeWrapper.getApiProduct(); providerName = apiProduct.getId().getProviderName(); diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java index e8662c256c6f..a59238820d97 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java @@ -10203,9 +10203,7 @@ public static String retrieveDefaultReservedUsername() { return defaultReservedUsername; } - public static JSONArray getCustomProperties(String userId) throws APIManagementException { - - String tenantDomain = MultitenantUtils.getTenantDomain(userId); + public static JSONArray getCustomProperties(String tenantDomain) throws APIManagementException { JSONArray customPropertyAttributes = null; JSONObject propertyConfig = getMandatoryPropertyKeysFromRegistry(tenantDomain); @@ -11604,4 +11602,44 @@ public static void validateApiWithFederatedGateway(API api) throws APIManagement + api.getGatewayType(), e); } } + + /** + * This method is used to validate the mandatory custom properties of an API + * + * @param customProperties custom properties of the API + * @param additionalPropertiesMap additional properties to validate + * @return list of erroneous property names. returns an empty array if there are no errors. + */ + public static List validateMandatoryProperties(org.json.simple.JSONArray customProperties, + JSONObject additionalPropertiesMap) { + + List errorPropertyNames = new ArrayList<>(); + + for (int i = 0; i < customProperties.size(); i++) { + JSONObject property = (JSONObject) customProperties.get(i); + String propertyName = (String) property.get(APIConstants.CustomPropertyAttributes.NAME); + boolean isRequired = (boolean) property.get(APIConstants.CustomPropertyAttributes.REQUIRED); + if (isRequired) { + String mapPropertyDisplay = (String) additionalPropertiesMap.get(propertyName + "__display"); + String mapProperty = (String) additionalPropertiesMap.get(propertyName); + + if (mapProperty == null && mapPropertyDisplay == null) { + errorPropertyNames.add(propertyName); + continue; + } + String propertyValue = ""; + String propertyValueDisplay = ""; + if (mapProperty != null) { + propertyValue = mapProperty; + } + if (mapPropertyDisplay != null) { + propertyValueDisplay = mapPropertyDisplay; + } + if (propertyValue.isEmpty() && propertyValueDisplay.isEmpty()) { + errorPropertyNames.add(propertyName); + } + } + } + return errorPropertyNames; + } } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java index 3c5e2c9c0881..9dbe6063ccd5 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java @@ -975,7 +975,7 @@ public Response updateAPI(String apiId, APIDTO body, String ifMatch, MessageCont } // validate custom properties - org.json.simple.JSONArray customProperties = APIUtil.getCustomProperties(username); + org.json.simple.JSONArray customProperties = APIUtil.getCustomProperties(organization); List errorProperties = PublisherCommonUtils.validateMandatoryProperties(customProperties, body); if (!errorProperties.isEmpty()) { String errorString = " : " + String.join(", ", errorProperties); @@ -3670,6 +3670,8 @@ public Response changeAPILifecycle(String action, String apiId, String lifecycle } else if (isAuthorizationFailure(e)) { RestApiUtil.handleAuthorizationFailure( "Authorization failure while updating the lifecycle of API " + apiId, e, log); + } else if (e.getErrorHandler().getErrorCode() == ExceptionCodes.ERROR_WHILE_UPDATING_MANDATORY_PROPERTIES.getErrorCode()) { + RestApiUtil.handleBadRequest(e.getErrorHandler().getErrorDescription() + " on API " + apiId, e, log); } else { throw e; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/SettingsApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/SettingsApiServiceImpl.java index fc66e6985d66..dfc8737053b1 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/SettingsApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/SettingsApiServiceImpl.java @@ -56,7 +56,7 @@ public Response getSettings(MessageContext messageContext){ SettingsDTO settingsDTO = settingsMappingUtil.fromSettingstoDTO(isUserAvailable, organization); settingsDTO.setScopes(getScopeList()); settingsDTO.setGatewayTypes(APIUtil.getGatewayTypes()); - settingsDTO.setCustomProperties(APIUtil.getCustomProperties(username)); + settingsDTO.setCustomProperties(APIUtil.getCustomProperties(organization)); return Response.ok().entity(settingsDTO).build(); } catch (APIManagementException | IOException e) { String errorMessage = "Error while retrieving Publisher Settings"; From 4d2406d3ce1ca4b40fdfccd8aff8dfc02fecb4ca Mon Sep 17 00:00:00 2001 From: Nethmi Ranasinghe Date: Thu, 3 Apr 2025 10:26:25 +0530 Subject: [PATCH 2/2] Resolve review comments --- .../carbon/apimgt/api/ExceptionCodes.java | 1 + .../carbon/apimgt/impl/APIProviderImpl.java | 28 +++++++++---------- .../carbon/apimgt/impl/utils/APIUtil.java | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java index ec440a079fc8..da0e21cab59a 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java @@ -122,6 +122,7 @@ public enum ExceptionCodes implements ErrorHandler { CANNOT_CREATE_API_VERSION(900362, "New API Version cannot be created from a different provider", 409, "Initial provider of an API must be preserved in all versions of that API"), INTERNAL_ERROR_WHILE_UPDATING_API(900363, "Internal Server Error occurred while updating the API", 500, "Internal Server Error. '%s'"), ERROR_WHILE_UPDATING_MANDATORY_PROPERTIES(903010, "Error while updating required properties", 400, "Error while updating required properties."), + ERROR_WHILE_VALIDATING_MANDATORY_PROPERTIES(903015, "Error while validating required properties", 400, "Error while validating required properties."), //Lifecycle related codes API_UPDATE_FORBIDDEN_PER_LC(900380, "Insufficient permission to update the API", 403, diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java index d3274861fd24..5f5ceecdeeef 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java @@ -3342,21 +3342,6 @@ public APIStateChangeResponse changeLifeCycleStatus(String orgId, ApiTypeWrapper boolean isApiProduct = apiTypeWrapper.isAPIProduct(); String workflowType; - if (!apiTypeWrapper.isAPIProduct()){ - // validate custom API properties - if (StringUtils.equals(action, APIConstants.LC_PUBLISH_LC_STATE)) { - org.json.simple.JSONArray customProperties = APIUtil.getCustomProperties(this.tenantDomain); - List errorProperties = APIUtil.validateMandatoryProperties(customProperties, - apiTypeWrapper.getApi().getAdditionalProperties()); - - if (!errorProperties.isEmpty()) { - String errorString = " : " + String.join(", ", errorProperties); - throw new APIManagementException(errorString, ExceptionCodes.from(ExceptionCodes - .ERROR_WHILE_UPDATING_MANDATORY_PROPERTIES)); - } - } - } - if (isApiProduct) { APIProduct apiProduct = apiTypeWrapper.getApiProduct(); providerName = apiProduct.getId().getProviderName(); @@ -3369,6 +3354,19 @@ public APIStateChangeResponse changeLifeCycleStatus(String orgId, ApiTypeWrapper apiOrApiProductId = apiMgtDAO.getAPIProductId(apiTypeWrapper.getApiProduct().getId()); workflowType = WorkflowConstants.WF_TYPE_AM_API_PRODUCT_STATE; } else { + // validate mandatory API properties + if (StringUtils.equals(action, APIConstants.LC_PUBLISH_LC_STATE)) { + org.json.simple.JSONArray customProperties = APIUtil.getCustomProperties(this.tenantDomain); + List errorProperties = APIUtil.validateMandatoryProperties(customProperties, + apiTypeWrapper.getApi().getAdditionalProperties()); + + if (!errorProperties.isEmpty()) { + String errorString = " : " + String.join(", ", errorProperties); + throw new APIManagementException(errorString, ExceptionCodes.from(ExceptionCodes + .ERROR_WHILE_VALIDATING_MANDATORY_PROPERTIES)); + } + } + API api = apiTypeWrapper.getApi(); providerName = api.getId().getProviderName(); apiName = api.getId().getApiName(); diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java index a59238820d97..552d0c76d43b 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java @@ -11606,7 +11606,7 @@ public static void validateApiWithFederatedGateway(API api) throws APIManagement /** * This method is used to validate the mandatory custom properties of an API * - * @param customProperties custom properties of the API + * @param customProperties custom properties of the API * @param additionalPropertiesMap additional properties to validate * @return list of erroneous property names. returns an empty array if there are no errors. */