use of com.linkedin.restli.server.RestLiServiceException in project rest.li by linkedin.
the class ErrorResponseValidationFilter method completeExceptionallyWithHttp500.
/**
* If not already completed, causes invocations of #get() method of {@link CompletableFuture} and related methods
* to throw the given exception.
*
* Converts given {@link RestLiServiceException} to HttpStatus.S_500_INTERNAL_SERVER_ERROR.
*
* @param future A {@link Future} that may be explicitly completed (setting its value and status),
* and may be used as a CompletionStage, supporting dependent functions
* and actions that trigger upon its completion.
* @param restLiServiceException The {@link RestLiServiceException} that caused the error response.
* @return {@link CompletableFuture}<{@link Void}> - future result of filter execution.
*/
private CompletableFuture<Void> completeExceptionallyWithHttp500(CompletableFuture<Void> future, RestLiServiceException restLiServiceException) {
RestLiServiceException serviceException = new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, ERROR_MESSAGE, restLiServiceException);
serviceException.setRequestId(restLiServiceException.getRequestId());
future.completeExceptionally(serviceException);
return future;
}
use of com.linkedin.restli.server.RestLiServiceException in project rest.li by linkedin.
the class ErrorResponseValidationFilter method onError.
@Override
public CompletableFuture<Void> onError(Throwable throwable, final FilterRequestContext requestContext, final FilterResponseContext responseContext) {
CompletableFuture<Void> future = new CompletableFuture<>();
if (throwable instanceof RestLiServiceException) {
RestLiServiceException restLiServiceException = (RestLiServiceException) throwable;
// do the validation only if the 'code' field is set on RestLiServiceException.
if (restLiServiceException.hasCode()) {
List<ServiceError> methodServiceErrors = requestContext.getMethodServiceErrors();
List<ServiceError> resourceServiceErrors = requestContext.getFilterResourceModel().getServiceErrors();
// nor on the method level skip the validation.
if (methodServiceErrors == null && resourceServiceErrors == null) {
// error details should not be set on RestLiServiceException object.
if (restLiServiceException.getErrorDetailsRecord() != null) {
return completeExceptionallyWithHttp500(future, restLiServiceException);
}
future.completeExceptionally(restLiServiceException);
return future;
}
Set<ServiceError> serviceErrors = new HashSet<>();
if (methodServiceErrors != null) {
serviceErrors.addAll(methodServiceErrors);
}
if (resourceServiceErrors != null) {
serviceErrors.addAll(resourceServiceErrors);
}
// An empty list of codes means that any service error code will result in a Http 500 error response.
if (serviceErrors.isEmpty()) {
return completeExceptionallyWithHttp500(future, restLiServiceException);
}
String errorCode = restLiServiceException.getCode();
Optional<ServiceError> maybeServiceError = serviceErrors.stream().filter(serviceError -> serviceError.code().equals(errorCode)).findFirst();
// convert given throwable to 500_INTERNAL_SERVER_ERROR exception.
if (!maybeServiceError.isPresent()) {
return completeExceptionallyWithHttp500(future, restLiServiceException);
}
ServiceError definedServiceError = maybeServiceError.get();
// Check that the error detail type is valid.
if (restLiServiceException.hasErrorDetails()) {
Class<?> errorResponseErrorDetailType = restLiServiceException.getErrorDetailsRecord().getClass();
Class<?> definedErrorDetailType = definedServiceError.errorDetailType();
if (!errorResponseErrorDetailType.equals(definedErrorDetailType)) {
return completeExceptionallyWithHttp500(future, restLiServiceException);
}
}
// convert given throwable to 500_INTERNAL_SERVER_ERROR exception.
if (definedServiceError.httpStatus() != restLiServiceException.getStatus()) {
return completeExceptionallyWithHttp500(future, restLiServiceException);
}
// TODO: validate error message. What if the defined message in service error has placeholders, which gets filled based on some business logic in the code.
}
}
future.completeExceptionally(throwable);
return future;
}
use of com.linkedin.restli.server.RestLiServiceException in project rest.li by linkedin.
the class RestLiValidationFilter method validateBatchCollectionResponse.
private void validateBatchCollectionResponse(RestLiDataValidator validator, List<BatchFinderResponseEnvelope.BatchFinderEntry> responses) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < responses.size(); i++) {
BatchFinderResponseEnvelope.BatchFinderEntry entry = responses.get(i);
// on error case
if (entry.isErrorResponse()) {
continue;
}
// on success case
for (int j = 0; j < entry.getElements().size(); j++) {
RecordTemplate entity = entry.getElements().get(j);
ValidationResult result = validator.validateOutput(entity);
if (!result.isValid()) {
sb.append("BatchCriteria: ").append(i + " ").append("Element: ").append(j + " ").append(result.getMessages().toString());
}
}
}
if (sb.length() > 0) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, sb.toString());
}
}
use of com.linkedin.restli.server.RestLiServiceException in project rest.li by linkedin.
the class RestLiValidationFilter method onRequest.
@Override
public CompletableFuture<Void> onRequest(final FilterRequestContext requestContext) {
// are spotted early
if (shouldValidateOnResponse(requestContext)) {
MaskTree projectionMask = requestContext.getProjectionMask();
if (projectionMask != null) {
try {
// Value class from resource model is the only source of truth for record schema.
// Schema from the record template itself should not be used.
DataSchema originalSchema = DataTemplateUtil.getSchema(requestContext.getFilterResourceModel().getValueClass());
DataSchema validatingSchema = constructValidatingSchema(requestContext, originalSchema, projectionMask.getDataMap(), _nonSchemaFieldsToAllowInProjectionMask);
// Put validating schema in scratchpad for use in onResponse
requestContext.getFilterScratchpad().put(VALIDATING_SCHEMA_KEY, validatingSchema);
} catch (InvalidProjectionException e) {
throw new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST, e.getMessage());
} catch (TemplateRuntimeException e) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, TEMPLATE_RUNTIME_EXCEPTION_MESSAGE);
}
}
}
if (!shouldValidateOnRequest(requestContext)) {
return CompletableFuture.completedFuture(null);
}
Class<?> resourceClass = requestContext.getFilterResourceModel().getResourceClass();
if (UnstructuredDataUtil.isUnstructuredDataClass(resourceClass)) {
return CompletableFuture.completedFuture(null);
}
ResourceMethod method = requestContext.getMethodType();
RestLiDataValidator validator = createRequestRestLiDataValidator(requestContext);
RestLiRequestData requestData = requestContext.getRequestData();
ValidationResult result;
switch(method) {
case CREATE:
case UPDATE:
result = validator.validateInput(requestData.getEntity());
if (!result.isValid()) {
throw constructRestLiServiceException(result.getMessages(), result.getMessages().toString());
}
break;
case PARTIAL_UPDATE:
result = validator.validateInput((PatchRequest<?>) requestData.getEntity());
if (!result.isValid()) {
throw constructRestLiServiceException(result.getMessages(), result.getMessages().toString());
}
break;
case BATCH_CREATE:
StringBuilder errorMessage = new StringBuilder();
Map<String, Collection<Message>> messages = new HashMap<>();
int index = 0;
for (RecordTemplate entity : requestData.getBatchEntities()) {
result = validator.validateInput(entity);
if (!result.isValid()) {
errorMessage.append("Index: ").append(index).append(", ").append(result.getMessages().toString());
messages.put(String.valueOf(index), result.getMessages());
}
++index;
}
if (errorMessage.length() > 0) {
throw constructRestLiServiceException(messages, errorMessage.toString());
}
break;
case BATCH_UPDATE:
case BATCH_PARTIAL_UPDATE:
ProtocolVersion protocolVersion = requestContext.getRestliProtocolVersion();
StringBuilder stringBuilder = new StringBuilder();
Map<String, Collection<Message>> errorMessages = new HashMap<>();
for (Map.Entry<?, ? extends RecordTemplate> entry : requestData.getBatchKeyEntityMap().entrySet()) {
if (method == ResourceMethod.BATCH_UPDATE) {
result = validator.validateInput(entry.getValue());
} else {
result = validator.validateInput((PatchRequest<?>) entry.getValue());
}
if (!result.isValid()) {
stringBuilder.append("Key: ").append(entry.getKey()).append(", ").append(result.getMessages().toString());
errorMessages.put(URIParamUtils.encodeKeyForBody(entry.getKey(), false, protocolVersion), result.getMessages());
}
}
if (stringBuilder.length() > 0) {
throw constructRestLiServiceException(errorMessages, stringBuilder.toString());
}
break;
default:
break;
}
return CompletableFuture.completedFuture(null);
}
use of com.linkedin.restli.server.RestLiServiceException in project rest.li by linkedin.
the class RestLiValidationFilter method validateBatchResponse.
private void validateBatchResponse(RestLiDataValidator validator, Map<?, BatchResponseEnvelope.BatchResponseEntry> batchResponseMap) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<?, ? extends BatchResponseEnvelope.BatchResponseEntry> entry : batchResponseMap.entrySet()) {
if (entry.getValue().hasException()) {
continue;
}
// The "entity" in the results map may be the raw record entity, or a wrapper containing the record entity
final RecordTemplate entity = entry.getValue().getRecord();
ValidationResult result;
if (entity instanceof UpdateEntityStatus) {
result = validator.validateOutput(((UpdateEntityStatus<? extends RecordTemplate>) entity).getEntity());
} else {
result = validator.validateOutput(entity);
}
if (!result.isValid()) {
sb.append("Key: ").append(entry.getKey()).append(", ").append(result.getMessages().toString());
}
}
if (sb.length() > 0) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, sb.toString());
}
}
Aggregations