Search in sources :

Example 1 with ServiceError

use of com.linkedin.restli.server.errors.ServiceError in project rest.li by linkedin.

the class TestFilterRequestContextInternalImpl method testFilterRequestContextAdapter.

@Test
@SuppressWarnings("unchecked")
public void testFilterRequestContextAdapter() throws Exception {
    final String resourceName = "resourceName";
    final String resourceNamespace = "resourceNamespace";
    final ResourceMethod methodType = ResourceMethod.GET;
    final DataMap customAnnotations = new DataMap();
    customAnnotations.put("foo", "Bar");
    final ProjectionMode projectionMode = ProjectionMode.AUTOMATIC;
    final MaskTree maskTree = new MaskTree();
    final MaskTree metadataMaskTree = new MaskTree();
    final MaskTree pagingMaskTree = new MaskTree();
    final MutablePathKeys pathKeys = new PathKeysImpl();
    final Map<String, String> requestHeaders = new HashMap<>();
    requestHeaders.put("Key1", "Value1");
    final URI requestUri = new URI("foo.bar.com");
    final ProtocolVersion protoVersion = AllProtocolVersions.BASELINE_PROTOCOL_VERSION;
    final DataMap queryParams = new DataMap();
    queryParams.put("Param1", "Val1");
    final Map<String, Object> localAttrs = new HashMap<>();
    localAttrs.put("Key1", "Val1");
    final RequestContext r2RequestContext = new RequestContext();
    r2RequestContext.putLocalAttr("Key1", "Val1");
    final String finderName = UUID.randomUUID().toString();
    final String batchFinderName = UUID.randomUUID().toString();
    final String actionName = UUID.randomUUID().toString();
    final List<ServiceError> methodServiceErrors = Collections.singletonList(TestServiceError.METHOD_LEVEL_ERROR);
    final List<ServiceError> resourceServiceErrors = Collections.singletonList(TestServiceError.RESOURCE_LEVEL_ERROR);
    final List<Parameter<?>> methodParameters = Collections.singletonList(Mockito.mock(Parameter.class));
    when(resourceModel.getName()).thenReturn(resourceName);
    when(resourceModel.getNamespace()).thenReturn(resourceNamespace);
    when(filterResourceModel.getServiceErrors()).thenReturn(resourceServiceErrors);
    when(resourceMethod.getResourceModel()).thenReturn(resourceModel);
    when(resourceMethod.getMethodType()).thenReturn(methodType);
    when(resourceMethod.getFinderName()).thenReturn(finderName);
    when(resourceMethod.getBatchFinderName()).thenReturn(batchFinderName);
    when(resourceMethod.getActionName()).thenReturn(actionName);
    when(resourceMethod.getCustomAnnotationData()).thenReturn(customAnnotations);
    when(resourceMethod.getMethod()).thenReturn(null);
    when(resourceMethod.getParameters()).thenReturn(methodParameters);
    when(resourceMethod.getServiceErrors()).thenReturn(methodServiceErrors);
    when(context.getProjectionMode()).thenReturn(projectionMode);
    when(context.getProjectionMask()).thenReturn(maskTree);
    when(context.getMetadataProjectionMask()).thenReturn(metadataMaskTree);
    when(context.getPagingProjectionMask()).thenReturn(pagingMaskTree);
    when(context.getPathKeys()).thenReturn(pathKeys);
    when(context.getRequestHeaders()).thenReturn(requestHeaders);
    when(context.getRequestURI()).thenReturn(requestUri);
    when(context.getRestliProtocolVersion()).thenReturn(protoVersion);
    when(context.getParameters()).thenReturn(queryParams);
    when(context.getRawRequestContext()).thenReturn(r2RequestContext);
    FilterRequestContext filterContext = new FilterRequestContextInternalImpl(context, resourceMethod, null);
    filterContext.setProjectionMask(maskTree);
    filterContext.setMetadataProjectionMask(metadataMaskTree);
    filterContext.setPagingProjectionMask(pagingMaskTree);
    assertEquals(filterContext.getFilterResourceModel().getResourceName(), resourceName);
    assertEquals(filterContext.getFilterResourceModel().getResourceNamespace(), resourceNamespace);
    assertEquals(filterContext.getMethodType(), methodType);
    assertEquals(filterContext.getCustomAnnotations(), customAnnotations);
    assertEquals(filterContext.getProjectionMode(), projectionMode);
    assertEquals(filterContext.getProjectionMask(), maskTree);
    assertEquals(filterContext.getMetadataProjectionMask(), metadataMaskTree);
    assertEquals(filterContext.getPagingProjectionMask(), pagingMaskTree);
    assertEquals(filterContext.getPathKeys(), pathKeys);
    assertEquals(filterContext.getRequestHeaders(), requestHeaders);
    assertEquals(filterContext.getRequestURI(), requestUri);
    assertEquals(filterContext.getRestliProtocolVersion(), protoVersion);
    assertEquals(filterContext.getQueryParameters(), queryParams);
    assertEquals(filterContext.getActionName(), actionName);
    assertEquals(filterContext.getFinderName(), finderName);
    assertEquals(filterContext.getBatchFinderName(), batchFinderName);
    assertEquals(filterContext.getRequestContextLocalAttrs(), localAttrs);
    assertNull(filterContext.getMethod());
    assertEquals(filterContext.getMethodParameters(), methodParameters);
    assertNotSame(filterContext.getMethodParameters(), methodParameters);
    assertEquals(filterContext.getMethodServiceErrors(), methodServiceErrors);
    filterContext.getRequestHeaders().put("header2", "value2");
    assertEquals(requestHeaders.get("header2"), "value2");
    verify(resourceModel).getName();
    verify(resourceModel).getNamespace();
    verify(resourceMethod).getMethodType();
    verify(resourceMethod).getResourceModel();
    verify(resourceMethod).getCustomAnnotationData();
    verify(resourceMethod).getFinderName();
    verify(resourceMethod).getBatchFinderName();
    verify(resourceMethod).getActionName();
    verify(resourceMethod).getMethod();
    verify(resourceMethod, times(2)).getParameters();
    verify(resourceMethod).getServiceErrors();
    verify(context).getProjectionMode();
    verify(context).setProjectionMask(maskTree);
    verify(context).getProjectionMask();
    verify(context).setMetadataProjectionMask(metadataMaskTree);
    verify(context).getMetadataProjectionMask();
    verify(context).setPagingProjectionMask(pagingMaskTree);
    verify(context).getPagingProjectionMask();
    verify(context).getPathKeys();
    verify(context, times(2)).getRequestHeaders();
    verify(context).getRequestURI();
    verify(context).getRestliProtocolVersion();
    verify(context).getParameters();
    verify(context).getRawRequestContext();
    verify(resourceMethod).getCollectionCustomMetadataType();
    verifyNoMoreInteractions(context, resourceMethod, resourceModel);
}
Also used : TestServiceError(com.linkedin.restli.server.TestServiceError) ServiceError(com.linkedin.restli.server.errors.ServiceError) MutablePathKeys(com.linkedin.restli.internal.server.MutablePathKeys) HashMap(java.util.HashMap) PathKeysImpl(com.linkedin.restli.internal.server.PathKeysImpl) ProtocolVersion(com.linkedin.restli.common.ProtocolVersion) URI(java.net.URI) DataMap(com.linkedin.data.DataMap) MaskTree(com.linkedin.data.transform.filter.request.MaskTree) ProjectionMode(com.linkedin.restli.server.ProjectionMode) Parameter(com.linkedin.restli.internal.server.model.Parameter) FilterRequestContext(com.linkedin.restli.server.filter.FilterRequestContext) RequestContext(com.linkedin.r2.message.RequestContext) FilterRequestContext(com.linkedin.restli.server.filter.FilterRequestContext) ResourceMethod(com.linkedin.restli.common.ResourceMethod) Test(org.testng.annotations.Test) BeforeTest(org.testng.annotations.BeforeTest)

Example 2 with ServiceError

use of com.linkedin.restli.server.errors.ServiceError 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 3 with ServiceError

use of com.linkedin.restli.server.errors.ServiceError in project rest.li by linkedin.

the class RestLiAnnotationReader method buildServiceErrors.

/**
 * Builds a list of {@link ServiceError} objects given a list of codes, a mapping from code to service error, and a
 * mapping from code to parameter names. Also uses the resource class and method to construct an exception message.
 *
 * @param serviceErrorCodes list of service error codes indicating which service errors to build
 * @param serviceErrorCodeMapping mapping from service error code to service error
 * @param paramsMapping mapping from service error codes to array of parameter names
 * @param resourceClass resource class
 * @param method resource method
 * @return list of service errors
 */
private static List<ServiceError> buildServiceErrors(final List<String> serviceErrorCodes, final Map<String, ServiceError> serviceErrorCodeMapping, final Map<String, String[]> paramsMapping, final Class<?> resourceClass, final Method method) {
    final Set<String> existingServiceErrorCodes = new HashSet<>();
    final List<ServiceError> serviceErrors = new ArrayList<>(serviceErrorCodes.size());
    for (String serviceErrorCode : serviceErrorCodes) {
        // Check for duplicate service error codes
        if (existingServiceErrorCodes.contains(serviceErrorCode)) {
            throw new ResourceConfigException(String.format("Duplicate service error code '%s' used in %s", serviceErrorCode, buildExceptionLocationString(resourceClass, method)));
        }
        // Attempt to map this code to its corresponding service error
        if (serviceErrorCodeMapping.containsKey(serviceErrorCode)) {
            final ServiceError serviceError = serviceErrorCodeMapping.get(serviceErrorCode);
            // Validate that this service error doesn't use the ErrorDetails type
            final Class<? extends RecordTemplate> errorDetailType = serviceError.errorDetailType();
            if (errorDetailType != null && errorDetailType.equals(ErrorDetails.class)) {
                throw new ResourceConfigException(String.format("Class '%s' is not meant to be used as an error detail type, please use a more specific " + "model or remove from service error '%s' in %s", errorDetailType.getCanonicalName(), serviceErrorCode, buildExceptionLocationString(resourceClass, method)));
            }
            // Determine if this is a method-level service error with parameters associated with it
            final String[] parameterNames = paramsMapping.get(serviceErrorCode);
            // Depending on if there are service errors, either add it directly or wrap it with the parameter names
            serviceErrors.add(parameterNames == null ? serviceError : new ParametersServiceError(serviceError, parameterNames));
        } else {
            throw new ResourceConfigException(String.format("Unknown service error code '%s' used in %s", serviceErrorCode, buildExceptionLocationString(resourceClass, method)));
        }
        // Mark this code as seen to prevent duplicates
        existingServiceErrorCodes.add(serviceErrorCode);
    }
    return serviceErrors;
}
Also used : ParametersServiceError(com.linkedin.restli.server.errors.ParametersServiceError) ParametersServiceError(com.linkedin.restli.server.errors.ParametersServiceError) ServiceError(com.linkedin.restli.server.errors.ServiceError) ArrayList(java.util.ArrayList) ErrorDetails(com.linkedin.restli.common.ErrorDetails) ResourceConfigException(com.linkedin.restli.server.ResourceConfigException) HashSet(java.util.HashSet)

Example 4 with ServiceError

use of com.linkedin.restli.server.errors.ServiceError in project rest.li by linkedin.

the class RestLiAnnotationReader method addServiceErrors.

/**
 * Reads annotations on a given resource class in order to build service errors, which are then added to
 * a given resource model.
 *
 * @param resourceModel resource model to add service errors to
 * @param resourceClass class annotated with service errors
 */
private static void addServiceErrors(final ResourceModel resourceModel, final Class<?> resourceClass) {
    final ServiceErrorDef serviceErrorDefAnnotation = resourceClass.getAnnotation(ServiceErrorDef.class);
    final ServiceErrors serviceErrorsAnnotation = resourceClass.getAnnotation(ServiceErrors.class);
    final List<ServiceError> serviceErrors = buildServiceErrors(serviceErrorDefAnnotation, serviceErrorsAnnotation, null, resourceClass, null);
    if (serviceErrors == null) {
        return;
    }
    resourceModel.setServiceErrors(serviceErrors);
}
Also used : ServiceErrorDef(com.linkedin.restli.server.annotations.ServiceErrorDef) ParametersServiceError(com.linkedin.restli.server.errors.ParametersServiceError) ServiceError(com.linkedin.restli.server.errors.ServiceError) ServiceErrors(com.linkedin.restli.server.annotations.ServiceErrors)

Example 5 with ServiceError

use of com.linkedin.restli.server.errors.ServiceError in project rest.li by linkedin.

the class ResourceModelEncoder method buildServiceErrors.

/**
 * Given a list of service errors, returns a service error schema array record.
 *
 * @param serviceErrors list of service errors to build, assumed to be non-null and non-empty
 * @return service error schema array
 */
private ServiceErrorSchemaArray buildServiceErrors(final List<ServiceError> serviceErrors) {
    final ServiceErrorSchemaArray serviceErrorSchemaArray = new ServiceErrorSchemaArray();
    // For each service error, build a service error schema and append it to the service error schema array
    for (ServiceError serviceError : serviceErrors) {
        ServiceErrorSchema serviceErrorSchema = new ServiceErrorSchema().setStatus(serviceError.httpStatus().getCode()).setCode(serviceError.code());
        final String message = serviceError.message();
        if (message != null) {
            serviceErrorSchema.setMessage(message);
        }
        final Class<?> errorDetailType = serviceError.errorDetailType();
        if (errorDetailType != null) {
            serviceErrorSchema.setErrorDetailType(errorDetailType.getCanonicalName());
        }
        if (serviceError instanceof ParametersServiceError) {
            final String[] parameterNames = ((ParametersServiceError) serviceError).parameterNames();
            if (parameterNames != null && parameterNames.length != 0) {
                serviceErrorSchema.setParameters(new StringArray(Arrays.asList(parameterNames)));
            }
        }
        serviceErrorSchemaArray.add(serviceErrorSchema);
    }
    return serviceErrorSchemaArray;
}
Also used : ParametersServiceError(com.linkedin.restli.server.errors.ParametersServiceError) ServiceError(com.linkedin.restli.server.errors.ServiceError) ParametersServiceError(com.linkedin.restli.server.errors.ParametersServiceError) StringArray(com.linkedin.data.template.StringArray) ServiceErrorSchema(com.linkedin.restli.restspec.ServiceErrorSchema) ServiceErrorSchemaArray(com.linkedin.restli.restspec.ServiceErrorSchemaArray)

Aggregations

ServiceError (com.linkedin.restli.server.errors.ServiceError)6 ParametersServiceError (com.linkedin.restli.server.errors.ParametersServiceError)4 ResourceConfigException (com.linkedin.restli.server.ResourceConfigException)2 ServiceErrorDef (com.linkedin.restli.server.annotations.ServiceErrorDef)2 ServiceErrors (com.linkedin.restli.server.annotations.ServiceErrors)2 FilterRequestContext (com.linkedin.restli.server.filter.FilterRequestContext)2 HashSet (java.util.HashSet)2 DataMap (com.linkedin.data.DataMap)1 StringArray (com.linkedin.data.template.StringArray)1 MaskTree (com.linkedin.data.transform.filter.request.MaskTree)1 RequestContext (com.linkedin.r2.message.RequestContext)1 ErrorDetails (com.linkedin.restli.common.ErrorDetails)1 HttpStatus (com.linkedin.restli.common.HttpStatus)1 ProtocolVersion (com.linkedin.restli.common.ProtocolVersion)1 ResourceMethod (com.linkedin.restli.common.ResourceMethod)1 MutablePathKeys (com.linkedin.restli.internal.server.MutablePathKeys)1 PathKeysImpl (com.linkedin.restli.internal.server.PathKeysImpl)1 Parameter (com.linkedin.restli.internal.server.model.Parameter)1 ServiceErrorSchema (com.linkedin.restli.restspec.ServiceErrorSchema)1 ServiceErrorSchemaArray (com.linkedin.restli.restspec.ServiceErrorSchemaArray)1