Skip to content

Commit

Permalink
Merge pull request wso2#6642 from rmsamitha/descriptive-errors
Browse files Browse the repository at this point in the history
Improving Error Responses for api create,update REST APIs
  • Loading branch information
rmsamitha authored Jun 28, 2024
2 parents 08ded31 + 97280d0 commit 330c82b
Show file tree
Hide file tree
Showing 14 changed files with 323 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ public enum ExceptionCodes implements ErrorHandler {
INVALID_OPERATION_POLICY_SPECIFICATION(902006, "Invalid policy specification found", 400,
"Invalid policy specification. %s", false),

INVALID_OPERATION_POLICY_PARAMETERS(902007, "Missing required parameters for policy specification", 400,
MISSING_OPERATION_POLICY_PARAMETERS(902007, "Missing required parameters for policy specification", 400,
"Required parameter(s) %s for policy specification %s are either missing or empty"),
OPERATION_POLICY_NOT_ALLOWED_IN_THE_APPLIED_FLOW(902008, "Policy is not allowed in the applied flow", 400,
"%s policy is not allowed in applied flow"),
Expand Down Expand Up @@ -545,8 +545,63 @@ public enum ExceptionCodes implements ErrorHandler {
ARTIFACT_SYNC_HTTP_REQUEST_FAILED(903009, "Error while retrieving from remote endpoint", 500, "Error while executing HTTP request to retrieve from remote endpoint"),
KEY_MANAGER_DELETE_FAILED(902013, "Key Manager Delete error", 412,"Key Manager Delete error. %s", false),
KEYS_DELETE_FAILED(902014, "Key Delete error", 412,"Keys delete error. %s", false),
DOCUMENT_NAME_ILLEGAL_CHARACTERS(902015, "Document name cannot contain illegal characters", 400, "Document name contains one or more illegal characters");

DOCUMENT_NAME_ILLEGAL_CHARACTERS(903200, "Document name cannot contain illegal characters", 400,
"Document name contains one or more illegal characters", false),

HTTP_METHOD_INVALID(903201,
"Invalid HTTP method provided for API resource", 400,
"The HTTP method '%s' provided for resource '%s' is invalid", false),

OPERATION_TYPE_INVALID(903202, "Invalid operation type provided for API operation", 400,
"The '%s' API operation type '%s' provided for operation '%s' is invalid", false),

KEYMANAGERS_VALUE_NOT_ARRAY(903203, "KeyManagers value needs to be an array", 400,
"Value of the KeyManagers config should be an array", false),

SCOPE_ALREADY_ASSIGNED_FOR_DIFFERENT_API(903204, "Invalid scopes provided for API", 400,
"Error while adding local scopes for API %s. Scope: %s already assigned locally for a different API.",
false),

UNSUPPORTED_TRANSPORT(903205, "Unsupported transport", 400,
"Unsupported transport %s provided for the API operation/resource", false),

OAS_DEFINITION_VERSION_NOT_FOUND(903206, "Invalid OAS definition", 400,
"Could not determine the OAS version as the version element of the definition is not found", false),

API_NAME_PROVIDER_ORG_EMPTY(903207, "API name, provider or organization cannot be empty", 400,
"API name, provider or organization cannot be empty. Provided values: name: %s, provider: %s, org: %s",
false),

ANONYMOUS_USER_NOT_PERMITTED(903208, "Anonymous user not permitted", 401,
"Attempt to execute privileged operation as the anonymous user", false),

GLOBAL_MEDIATION_POLICIES_NOT_FOUND(903209, "Global mediation policies not found", 404,
"Global mediation policies not found", false),

ENDPOINT_URL_NOT_PROVIDED(903210, "Endpoint url not provided", 400,
"Url is not provided for the endpoint type %s in the endpoint config", false),

OPERATION_POLICY_NAME_VERSION_INVALID(903211, "Invalid operation policy name or version", 400,
"policyName and/or policyVersion provided for the applied policy %s_%s does not match the policy " +
"specification identified by the given policyId %s",
false),

INVALID_OPERATION_POLICY_PARAMS(903212, "Invalid operation policy parameters", 400,
"Invalid value provided for the operation policy parameter %s", false),

INVALID_ENDPOINT_SECURITY_CONFIG(903213, "Invalid endpoint security configuration", 400,
"Invalid values provided for %s endpoint security configuration", false),

ENDPOINT_SECURITY_TYPE_NOT_DEFINED(903214, "Endpoint security type not defined", 400,
"Endpoint security type not defined for the %s endpoint", false),

OPERATION_OR_RESOURCE_TYPE_OR_METHOD_NOT_DEFINED(902031,
"Operation type/http method is not specified for the operation/resource", 400,
"Operation type/http method is not specified for the operation/resource: %s"),

RESOURCE_URI_TEMPLATE_NOT_DEFINED(902032, "Resource URI template value not defined", 400,
"Resource URI template value (target) not defined", false);

private final long errorCode;
private final String errorMessage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.wso2.carbon.apimgt.impl.utils.APIUtil.handleException;

/**
* This class provides the core API provider functionality. It is implemented in a very
* self-contained and 'pure' manner, without taking requirements like security into account,
Expand Down Expand Up @@ -593,8 +595,10 @@ private Set<Scope> getScopesToRegisterFromURITemplates(String apiName, String or
if (!isScopeKeyAssignedLocally(apiName, scope.getKey(), organization)) {
scopesToRegister.add(scope);
} else {
throw new APIManagementException("Error while adding local scopes for API " + apiName
+ ". Scope: " + scopeKey + " already assigned locally for a different API.");
String errMsg = "Error while adding local scopes for API " + apiName
+ ". Scope: " + scopeKey + " already assigned locally for a different API.";
APIUtil.handleException(errMsg, ExceptionCodes
.from(ExceptionCodes.SCOPE_ALREADY_ASSIGNED_FOR_DIFFERENT_API, apiName, scopeKey));
}
} else if (log.isDebugEnabled()) {
log.debug("Scope " + scopeKey + " exists as a shared scope. Skip adding as a local scope.");
Expand Down Expand Up @@ -1046,16 +1050,44 @@ private void updateEndpointSecurity(API oldApi, API api) throws APIManagementExc
(JSONObject) oldEndpointConfigJson.get(APIConstants.ENDPOINT_SECURITY);
if (endpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_PRODUCTION) != null) {
if (oldEndpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_PRODUCTION) != null) {
EndpointSecurity endpointSecurity = new ObjectMapper().convertValue(
endpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_PRODUCTION),
EndpointSecurity.class);
EndpointSecurity endpointSecurity;
if (ServiceReferenceHolder.getInstance().isDetailedErrorResponsesEnabled()) {
try {
endpointSecurity = new ObjectMapper().convertValue(
endpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_PRODUCTION),
EndpointSecurity.class);
} catch (IllegalArgumentException e) {
ErrorHandler errorHandler = ExceptionCodes.from(
ExceptionCodes.INVALID_ENDPOINT_SECURITY_CONFIG,
APIConstants.ENDPOINT_SECURITY_PRODUCTION);
throw new APIManagementException(
"Error while processing " + APIConstants.ENDPOINT_SECURITY_PRODUCTION + " endpoint security configuration related values provided for API " + api.getId()
.toString(), errorHandler);
}
} else {
endpointSecurity = new ObjectMapper().convertValue(
endpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_PRODUCTION),
EndpointSecurity.class);
}

EndpointSecurity oldEndpointSecurity = new ObjectMapper().convertValue(
oldEndpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_PRODUCTION),
EndpointSecurity.class);
if (endpointSecurity.isEnabled() && oldEndpointSecurity.isEnabled() &&
StringUtils.isBlank(endpointSecurity.getPassword())) {
endpointSecurity.setUsername(oldEndpointSecurity.getUsername());
endpointSecurity.setPassword(oldEndpointSecurity.getPassword());
if (ServiceReferenceHolder.getInstance().isDetailedErrorResponsesEnabled()) {
if (StringUtils.isBlank(endpointSecurity.getType())) {
ErrorHandler errorHandler = ExceptionCodes.from(
ExceptionCodes.ENDPOINT_SECURITY_TYPE_NOT_DEFINED,
APIConstants.ENDPOINT_SECURITY_PRODUCTION);
throw new APIManagementException("Endpoint security type is not defined " +
"for the endpoint type " + APIConstants.ENDPOINT_SECURITY_PRODUCTION,
errorHandler);
}
}

if (endpointSecurity.getType().equals(APIConstants.ENDPOINT_SECURITY_TYPE_OAUTH)) {
endpointSecurity.setUniqueIdentifier(oldEndpointSecurity.getUniqueIdentifier());
endpointSecurity.setGrantType(oldEndpointSecurity.getGrantType());
Expand All @@ -1071,16 +1103,44 @@ private void updateEndpointSecurity(API oldApi, API api) throws APIManagementExc
}
if (endpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_SANDBOX) != null) {
if (oldEndpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_SANDBOX) != null) {
EndpointSecurity endpointSecurity = new ObjectMapper()
.convertValue(endpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_SANDBOX),
EndpointSecurity endpointSecurity;
if (ServiceReferenceHolder.getInstance().isDetailedErrorResponsesEnabled()) {
try {
endpointSecurity = new ObjectMapper().convertValue(
endpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_SANDBOX),
EndpointSecurity.class);
} catch (IllegalArgumentException e) {
ErrorHandler errorHandler = ExceptionCodes.from(
ExceptionCodes.INVALID_ENDPOINT_SECURITY_CONFIG,
APIConstants.ENDPOINT_SECURITY_SANDBOX);
throw new APIManagementException(
"Error while processing " + APIConstants.ENDPOINT_SECURITY_SANDBOX +
" endpoint security configuration related values provided for API " + api.getId()
.toString(), errorHandler);
}
} else {
endpointSecurity = new ObjectMapper().convertValue(
endpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_SANDBOX),
EndpointSecurity.class);
}

EndpointSecurity oldEndpointSecurity = new ObjectMapper()
.convertValue(oldEndpointSecurityJson.get(APIConstants.ENDPOINT_SECURITY_SANDBOX),
EndpointSecurity.class);
if (endpointSecurity.isEnabled() && oldEndpointSecurity.isEnabled() &&
StringUtils.isBlank(endpointSecurity.getPassword())) {
endpointSecurity.setUsername(oldEndpointSecurity.getUsername());
endpointSecurity.setPassword(oldEndpointSecurity.getPassword());
if (ServiceReferenceHolder.getInstance().isDetailedErrorResponsesEnabled()) {
if (StringUtils.isBlank(endpointSecurity.getType())) {
ErrorHandler errorHandler = ExceptionCodes.from(
ExceptionCodes.ENDPOINT_SECURITY_TYPE_NOT_DEFINED,
APIConstants.ENDPOINT_SECURITY_SANDBOX);
throw new APIManagementException("Endpoint security type is not defined " +
"for the endpoint type " + APIConstants.ENDPOINT_SECURITY_SANDBOX,
errorHandler);
}
}
if (endpointSecurity.getType().equals(APIConstants.ENDPOINT_SECURITY_TYPE_OAUTH)) {
endpointSecurity.setUniqueIdentifier(oldEndpointSecurity.getUniqueIdentifier());
endpointSecurity.setGrantType(oldEndpointSecurity.getGrantType());
Expand Down Expand Up @@ -1638,6 +1698,16 @@ private List<OperationPolicy> validatePolicies(List<OperationPolicy> apiPolicies
if (!commonPolicyData.getSpecification().getName()
.equals(policy.getPolicyName()) || !commonPolicyData.getSpecification().getVersion()
.equals(policy.getPolicyVersion())) {
if (ServiceReferenceHolder.getInstance().isDetailedErrorResponsesEnabled()) {
String errMsg =
"policyName and/or policyVersion provided for the applied policy " + policy.getPolicyName()
+ "_" + policy.getPolicyVersion() + " does not match the policy " +
"specification identified by the given policyId " + policyId;
ErrorHandler errorHandler = ExceptionCodes.from(
ExceptionCodes.OPERATION_POLICY_NAME_VERSION_INVALID, policy.getPolicyName(),
policy.getPolicyVersion(), policyId);
throw new APIManagementException(errMsg, errorHandler);
}
throw new APIManagementException("Applied policy for uriTemplate " + policy.getPolicyName()
+ "_" + policy.getPolicyVersion() + " does not match the specification");
}
Expand Down Expand Up @@ -1720,11 +1790,34 @@ public boolean validateAppliedPolicyWithSpecification(OperationPolicySpecificati
if (appliedPolicyAttribute != null) {
if (attribute.getValidationRegex() != null) {
Pattern pattern = Pattern.compile(attribute.getValidationRegex(), Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher((String) appliedPolicyAttribute);
Matcher matcher;
if (ServiceReferenceHolder.getInstance().isDetailedErrorResponsesEnabled()) {
try {
matcher = pattern.matcher((String) appliedPolicyAttribute);
} catch (ClassCastException e) {
ErrorHandler errorHandler = ExceptionCodes.from(
ExceptionCodes.INVALID_OPERATION_POLICY_PARAMS, attribute.getName());
throw new APIManagementException(
"Value with invalid data type provided for the operation policy " +
"parameter " + attribute.getName(),
errorHandler);
}
} else {
matcher = pattern.matcher((String) appliedPolicyAttribute);
}

if (!matcher.matches()) {
throw new APIManagementException("Policy attribute " + attribute.getName()
+ " regex validation error.",
ExceptionCodes.INVALID_OPERATION_POLICY_PARAMETERS);
if (ServiceReferenceHolder.getInstance().isDetailedErrorResponsesEnabled()) {
ErrorHandler errorHandler = ExceptionCodes.from(
ExceptionCodes.INVALID_OPERATION_POLICY_PARAMS, attribute.getName());
throw new APIManagementException(
"Invalid value provided for the operation policy parameter " + attribute.getName(),
errorHandler);
} else {
throw new APIManagementException(
"Policy attribute " + attribute.getName() + " regex validation error.",
ExceptionCodes.MISSING_OPERATION_POLICY_PARAMETERS);
}
}
}
} else {
Expand Down Expand Up @@ -1850,7 +1943,9 @@ private void validateAndSetAPISecurity(APIProduct apiProduct) {
private void checkIfValidTransport(String transport) throws APIManagementException {
if (!Constants.TRANSPORT_HTTP.equalsIgnoreCase(transport) && !Constants.TRANSPORT_HTTPS.equalsIgnoreCase(transport)
&& !APIConstants.WS_PROTOCOL.equalsIgnoreCase(transport) && !APIConstants.WSS_PROTOCOL.equalsIgnoreCase(transport)) {
handleException("Unsupported Transport [" + transport + ']');
String errMsg = "Unsupported Transport [" + transport + ']';
APIUtil.handleException(errMsg, ExceptionCodes
.from(ExceptionCodes.UNSUPPORTED_TRANSPORT, transport));
}
}

Expand Down
Loading

0 comments on commit 330c82b

Please sign in to comment.