Search in sources :

Example 26 with RestLiServiceException

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;
}
Also used : RestLiServiceException(com.linkedin.restli.server.RestLiServiceException)

Example 27 with RestLiServiceException

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;
}
Also used : ServiceError(com.linkedin.restli.server.errors.ServiceError) HashSet(java.util.HashSet) List(java.util.List) Future(java.util.concurrent.Future) HttpStatus(com.linkedin.restli.common.HttpStatus) FilterRequestContext(com.linkedin.restli.server.filter.FilterRequestContext) RestLiServiceException(com.linkedin.restli.server.RestLiServiceException) Optional(java.util.Optional) Set(java.util.Set) Filter(com.linkedin.restli.server.filter.Filter) FilterResponseContext(com.linkedin.restli.server.filter.FilterResponseContext) CompletableFuture(java.util.concurrent.CompletableFuture) ServiceError(com.linkedin.restli.server.errors.ServiceError) CompletableFuture(java.util.concurrent.CompletableFuture) RestLiServiceException(com.linkedin.restli.server.RestLiServiceException) HashSet(java.util.HashSet)

Example 28 with RestLiServiceException

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());
    }
}
Also used : RestLiServiceException(com.linkedin.restli.server.RestLiServiceException) RecordTemplate(com.linkedin.data.template.RecordTemplate) ValidationResult(com.linkedin.data.schema.validation.ValidationResult) BatchFinderResponseEnvelope(com.linkedin.restli.internal.server.response.BatchFinderResponseEnvelope)

Example 29 with RestLiServiceException

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);
}
Also used : InvalidProjectionException(com.linkedin.restli.common.util.ProjectionMaskApplier.InvalidProjectionException) HashMap(java.util.HashMap) ValidationResult(com.linkedin.data.schema.validation.ValidationResult) PatchRequest(com.linkedin.restli.common.PatchRequest) ProtocolVersion(com.linkedin.restli.common.ProtocolVersion) DataSchema(com.linkedin.data.schema.DataSchema) RestLiServiceException(com.linkedin.restli.server.RestLiServiceException) MaskTree(com.linkedin.data.transform.filter.request.MaskTree) RestLiDataValidator(com.linkedin.restli.common.validation.RestLiDataValidator) RecordTemplate(com.linkedin.data.template.RecordTemplate) TemplateRuntimeException(com.linkedin.data.template.TemplateRuntimeException) Collection(java.util.Collection) HashMap(java.util.HashMap) DataMap(com.linkedin.data.DataMap) Map(java.util.Map) ResourceMethod(com.linkedin.restli.common.ResourceMethod) RestLiRequestData(com.linkedin.restli.server.RestLiRequestData)

Example 30 with RestLiServiceException

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());
    }
}
Also used : UpdateEntityStatus(com.linkedin.restli.common.UpdateEntityStatus) RestLiServiceException(com.linkedin.restli.server.RestLiServiceException) RecordTemplate(com.linkedin.data.template.RecordTemplate) ValidationResult(com.linkedin.data.schema.validation.ValidationResult) HashMap(java.util.HashMap) DataMap(com.linkedin.data.DataMap) Map(java.util.Map) BatchResponseEnvelope(com.linkedin.restli.internal.server.response.BatchResponseEnvelope)

Aggregations

RestLiServiceException (com.linkedin.restli.server.RestLiServiceException)145 Test (org.testng.annotations.Test)55 HashMap (java.util.HashMap)39 DataMap (com.linkedin.data.DataMap)29 RecordTemplate (com.linkedin.data.template.RecordTemplate)21 Map (java.util.Map)21 RoutingException (com.linkedin.restli.server.RoutingException)20 ServerResourceContext (com.linkedin.restli.internal.server.ServerResourceContext)18 UpdateResponse (com.linkedin.restli.server.UpdateResponse)18 RoutingResult (com.linkedin.restli.internal.server.RoutingResult)17 BeforeTest (org.testng.annotations.BeforeTest)17 FilterRequestContext (com.linkedin.restli.server.filter.FilterRequestContext)16 ArrayList (java.util.ArrayList)16 RestException (com.linkedin.r2.message.rest.RestException)14 FilterResponseContext (com.linkedin.restli.server.filter.FilterResponseContext)13 RestRequest (com.linkedin.r2.message.rest.RestRequest)12 ByteString (com.linkedin.data.ByteString)11 ProtocolVersion (com.linkedin.restli.common.ProtocolVersion)11 ResourceMethodDescriptor (com.linkedin.restli.internal.server.model.ResourceMethodDescriptor)11 BatchResult (com.linkedin.restli.server.BatchResult)11