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);
}
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;
}
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;
}
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);
}
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;
}
Aggregations