use of com.linkedin.restli.server.filter.FilterRequestContext in project rest.li by linkedin.
the class TestRestLiCallback method testOnSuccessWithFiltersExceptionFromSecondFilter.
@SuppressWarnings("unchecked")
@Test
public void testOnSuccessWithFiltersExceptionFromSecondFilter() throws Exception {
// App stuff.
String result = "foo";
RestLiResponseData<GetResponseEnvelope> appResponseData = ResponseDataBuilderUtil.buildGetResponseData(HttpStatus.S_200_OK, null);
// Filter stuff.
ArgumentCaptor<RestLiServiceException> exFromFilterCapture = ArgumentCaptor.forClass(RestLiServiceException.class);
final Map<String, String> headersFromFilter = Maps.newHashMap();
headersFromFilter.put("Key", "Error from filter");
RestLiServiceException exception = new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR);
RestLiResponseData<?> filterResponseData = new RestLiResponseDataImpl<>(new GetResponseEnvelope(exception), headersFromFilter, Collections.emptyList());
RestLiResponse partialFilterErrorResponse = new RestLiResponse.Builder().build();
final Exception exFromFilter = new RuntimeException("Exception From Filter");
// Common stuff.
RestException finalRestException = new RestException(new RestResponseBuilder().build());
// Setup.
when((RestLiResponseData<GetResponseEnvelope>) _responseHandler.buildRestLiResponseData(_restRequest, _routingResult, result)).thenReturn(appResponseData);
when(_restRequest.getHeaders()).thenReturn(null);
when(_responseHandler.buildExceptionResponseData(eq(_routingResult), exFromFilterCapture.capture(), anyMap(), anyList())).thenReturn(filterResponseData);
when(_responseHandler.buildPartialResponse(_routingResult, appResponseData)).thenReturn(partialFilterErrorResponse);
// Mock filter behavior.
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
FilterRequestContext requestContext = (FilterRequestContext) args[0];
FilterResponseContext responseContext = (FilterResponseContext) args[1];
RestLiResponseData<GetResponseEnvelope> responseData = (RestLiResponseData<GetResponseEnvelope>) responseContext.getResponseData();
// The second filter should be invoked with details of the exception thrown by the first
// filter. Verify incoming data.
assertEquals(responseData.getResponseEnvelope().getStatus(), HttpStatus.S_200_OK);
assertNull(responseData.getResponseEnvelope().getRecord());
assertTrue(responseData.getHeaders().isEmpty());
// Modify data.
setStatus(responseContext, HttpStatus.S_402_PAYMENT_REQUIRED);
responseData.getHeaders().put("first-filter", "success");
return CompletableFuture.completedFuture(null);
}
}).doThrow(exFromFilter).when(_filter).onResponse(eq(_filterRequestContext), any(FilterResponseContext.class));
// invoke request filters so cursor is in correct place
when(_filter.onRequest(any(FilterRequestContext.class))).thenReturn(CompletableFuture.completedFuture(null));
_twoFilterChain.onRequest(_filterRequestContext, _filterResponseContextFactory);
// Invoke.
_twoFilterRestLiCallback.onSuccess(result);
// Verify.
verify(_responseHandler).buildPartialResponse(_routingResult, appResponseData);
verify(_responseHandler).buildRestLiResponseData(_restRequest, _routingResult, result);
verify(_responseHandler).buildPartialResponse(_routingResult, appResponseData);
ArgumentCaptor<RestLiResponseException> partialRestResponseExceptionCaptor = ArgumentCaptor.forClass(RestLiResponseException.class);
verify(_callback).onError(partialRestResponseExceptionCaptor.capture());
verifyNoMoreInteractions(_responseHandler, _callback);
RestLiResponseException restLiResponseException = partialRestResponseExceptionCaptor.getValue();
assertTrue(restLiResponseException.getCause() instanceof RestLiServiceException);
RestLiServiceException restliEx1 = (RestLiServiceException) restLiResponseException.getCause();
assertNotNull(restliEx1);
assertEquals(HttpStatus.S_500_INTERNAL_SERVER_ERROR, restliEx1.getStatus());
assertEquals(exFromFilter.getMessage(), restliEx1.getMessage());
assertEquals(exFromFilter, restliEx1.getCause());
Map<String, String> expectedHeaders = buildErrorHeaders();
expectedHeaders.put("first-filter", "success");
assertNotNull(appResponseData);
assertEquals(HttpStatus.S_500_INTERNAL_SERVER_ERROR, appResponseData.getResponseEnvelope().getStatus());
assertEquals(appResponseData.getHeaders(), expectedHeaders);
assertNull(appResponseData.getResponseEnvelope().getRecord());
}
use of com.linkedin.restli.server.filter.FilterRequestContext in project rest.li by linkedin.
the class TestRestLiMethodInvocation method checkInvocation.
private void checkInvocation(Object resource, RequestContext requestContext, ResourceMethodDescriptor resourceMethodDescriptor, ResourceMethodConfig resourceMethodConfig, String httpMethod, ProtocolVersion version, String uri, String entityBody, MutablePathKeys pathkeys, final Callback<RestResponse> callback, final boolean isDebugMode, final boolean expectRoutingException, final RestLiAttachmentReader expectedRequestAttachments, final RestLiResponseAttachments expectedResponseAttachments) throws Exception {
assertNotNull(resource);
assertNotNull(resourceMethodDescriptor);
try {
EasyMock.replay(resource);
RestRequestBuilder builder = new RestRequestBuilder(new URI(uri)).setMethod(httpMethod).addHeaderValue("Accept", "application/json").setHeader(RestConstants.HEADER_RESTLI_PROTOCOL_VERSION, version.toString());
if (entityBody != null) {
builder.setEntity(entityBody.getBytes(Data.UTF_8_CHARSET));
}
if (expectedResponseAttachments != null) {
builder.addHeaderValue(RestConstants.HEADER_ACCEPT, RestConstants.HEADER_VALUE_MULTIPART_RELATED);
}
RestRequest request = builder.build();
if (isDebugMode) {
requestContext.putLocalAttr(RestLiMethodInvoker.ATTRIBUTE_PROMISE_LISTENER, new PromiseListener<Object>() {
@Override
public void onResolved(Promise<Object> promise) {
// PromiseListener is invoked with a task.
if (promise instanceof Task) {
requestContext.putLocalAttr(ATTRIBUTE_PARSEQ_TRACE, ((Task<?>) promise).getTrace());
}
}
});
}
final ServerResourceContext resourceContext = new ResourceContextImpl(pathkeys, request, requestContext);
resourceContext.setRequestAttachmentReader(expectedRequestAttachments);
if (expectedResponseAttachments != null) {
resourceContext.setResponseAttachments(expectedResponseAttachments);
}
RoutingResult routingResult = new RoutingResult(resourceContext, resourceMethodDescriptor, resourceMethodConfig);
RestLiArgumentBuilder argumentBuilder = _methodAdapterProvider.getArgumentBuilder(resourceMethodDescriptor.getType());
RestLiRequestData requestData = argumentBuilder.extractRequestData(routingResult, entityBody != null && !entityBody.isEmpty() ? DataMapUtils.readMapWithExceptions(request) : null);
FilterRequestContext filterContext = new FilterRequestContextInternalImpl(routingResult.getContext(), resourceMethodDescriptor, requestData);
final CountDownLatch latch = new CountDownLatch(1);
final CountDownLatch expectedRoutingExceptionLatch = new CountDownLatch(1);
RestLiResponseHandler restLiResponseHandler = new RestLiResponseHandler(_methodAdapterProvider, _errorResponseBuilder);
Callback<RestLiResponse> executionCallback = new Callback<RestLiResponse>() {
@Override
public void onError(Throwable e) {
if (e.getCause() != null && e.getCause().getCause() instanceof RoutingException) {
expectedRoutingExceptionLatch.countDown();
}
if (callback != null) {
callback.onError(e);
}
Assert.assertEquals(resourceContext.getRequestAttachmentReader(), expectedRequestAttachments);
Assert.assertEquals(resourceContext.getResponseAttachments(), expectedResponseAttachments);
latch.countDown();
}
@Override
public void onSuccess(final RestLiResponse result) {
if (callback != null) {
callback.onSuccess(ResponseUtils.buildResponse(routingResult, result));
}
Assert.assertEquals(resourceContext.getResponseAttachments(), expectedResponseAttachments);
latch.countDown();
}
};
FilterChainDispatcher filterChainDispatcher = new FilterChainDispatcherImpl(routingResult, _invoker, argumentBuilder);
FilterChainCallback filterChainCallback = new FilterChainCallbackImpl(routingResult, restLiResponseHandler, executionCallback, _errorResponseBuilder);
final RestLiCallback outerCallback = new RestLiCallback(filterContext, new RestLiFilterResponseContextFactory(request, routingResult, restLiResponseHandler), new RestLiFilterChain(null, filterChainDispatcher, filterChainCallback));
RestUtils.validateRequestHeadersAndUpdateResourceContext(request.getHeaders(), Collections.emptySet(), routingResult.getContext());
_invoker.invoke(requestData, routingResult, argumentBuilder, outerCallback);
try {
latch.await();
if (expectRoutingException) {
expectedRoutingExceptionLatch.await();
}
} catch (InterruptedException e) {
// Ignore
}
EasyMock.verify(resource);
Assert.assertEquals((routingResult.getContext()).getResponseMimeType(), "application/json");
} catch (RestLiSyntaxException e) {
throw new RoutingException("syntax exception", 400);
} finally {
EasyMock.reset(resource);
EasyMock.makeThreadSafe(resource, true);
}
}
use of com.linkedin.restli.server.filter.FilterRequestContext in project rest.li by linkedin.
the class TestRestLiMethodInvocation method testInvokerWithFilters.
@Test(dataProvider = "provideFilterConfig")
public void testInvokerWithFilters(final boolean throwExceptionFromFirstFilter) throws Exception {
RestLiArgumentBuilder mockArgumentBuilder = createMock(RestLiArgumentBuilder.class);
Filter mockFilter = createMock(Filter.class);
@SuppressWarnings("unchecked") Callback<Object> mockCallback = createMock(Callback.class);
FilterRequestContext mockFilterContext = createMock(FilterRequestContext.class);
RestLiRequestData requestData = new RestLiRequestDataImpl.Builder().key("Key").build();
RestLiMethodInvoker invokerWithFilters = new RestLiMethodInvoker(_resourceFactory, _engine, ErrorResponseBuilder.DEFAULT_INTERNAL_ERROR_MESSAGE);
Map<String, ResourceModel> resourceModelMap = buildResourceModels(StatusCollectionResource.class, LocationResource.class, DiscoveredItemsResource.class);
ResourceModel statusResourceModel = resourceModelMap.get("/statuses");
ResourceMethodDescriptor resourceMethodDescriptor = statusResourceModel.findMethod(ResourceMethod.GET);
final StatusCollectionResource resource = getMockResource(StatusCollectionResource.class);
RestRequestBuilder builder = new RestRequestBuilder(new URI("/statuses/1")).setMethod("GET").addHeaderValue("Accept", "application/json").setHeader(RestConstants.HEADER_RESTLI_PROTOCOL_VERSION, AllProtocolVersions.LATEST_PROTOCOL_VERSION.toString());
RestRequest request = builder.build();
RoutingResult routingResult = new RoutingResult(new ResourceContextImpl(buildPathKeys("statusID", 1L), request, new RequestContext()), resourceMethodDescriptor);
expectLastCall();
FilterChainDispatcher filterChainDispatcher = new FilterChainDispatcher() {
@Override
public void onRequestSuccess(RestLiRequestData requestData, RestLiCallback restLiCallback) {
// only invoke if filter chain's requests were successful
invokerWithFilters.invoke(requestData, routingResult, mockArgumentBuilder, restLiCallback);
}
};
FilterChainCallback filterChainCallback = new FilterChainCallback() {
@Override
public void onResponseSuccess(RestLiResponseData<?> responseData) {
// unused
}
@Override
public void onError(Throwable th, RestLiResponseData<?> responseData) {
// unused
}
};
final Exception exFromFilter = new RuntimeException("Exception from filter!");
if (throwExceptionFromFirstFilter) {
mockFilter.onRequest(eq(mockFilterContext));
expectLastCall().andThrow(exFromFilter);
mockCallback.onError(eq(exFromFilter));
} else {
expect(mockFilterContext.getRequestData()).andReturn(requestData).times(3);
mockFilter.onRequest(eq(mockFilterContext));
expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
FilterRequestContext filterContext = (FilterRequestContext) getCurrentArguments()[0];
RestLiRequestData data = filterContext.getRequestData();
// Verify incoming data.
assertEquals(data.getKey(), "Key");
// Update data.
data.setKey("Key-Filter1");
return CompletableFuture.completedFuture(null);
}
}).andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
FilterRequestContext filterContext = (FilterRequestContext) getCurrentArguments()[0];
RestLiRequestData data = filterContext.getRequestData();
// Verify incoming data.
assertEquals(data.getKey(), "Key-Filter1");
// Update data.
data.setKey("Key-Filter2");
return CompletableFuture.completedFuture(null);
}
});
Long[] argsArray = { 1L };
expect(mockArgumentBuilder.buildArguments(requestData, routingResult)).andReturn(argsArray);
expect(resource.get(eq(1L))).andReturn(null).once();
mockCallback.onSuccess(eq(null));
}
replay(resource, mockArgumentBuilder, mockFilterContext, mockFilter, mockCallback);
RestUtils.validateRequestHeadersAndUpdateResourceContext(request.getHeaders(), Collections.emptySet(), routingResult.getContext());
RestLiFilterChain filterChain = new RestLiFilterChain(Arrays.asList(mockFilter, mockFilter), filterChainDispatcher, filterChainCallback);
filterChain.onRequest(mockFilterContext, new RestLiFilterResponseContextFactory(request, routingResult, new RestLiResponseHandler(_methodAdapterProvider, _errorResponseBuilder)));
verifyRecording(mockArgumentBuilder, mockFilterContext, mockFilter);
if (throwExceptionFromFirstFilter) {
assertEquals(requestData.getKey(), "Key");
} else {
assertEquals(requestData.getKey(), "Key-Filter2");
verify(resource);
}
EasyMock.reset(resource);
EasyMock.makeThreadSafe(resource, true);
}
use of com.linkedin.restli.server.filter.FilterRequestContext in project rest.li by linkedin.
the class BaseRestLiServer method handleResourceRequest.
/**
* Handles a request by building arguments and invoking the Rest.li resource method. All the arguments are processed
* by the filters in the filter chain before invoking the resource method. The result is also processed by the
* filters after invoking the resource method.
*
* @param request The request to handle. Only the URI, method, and the headers can be accessed from this request. The
* body should have already been parse to a DataMap.
* @param callback
*/
protected final void handleResourceRequest(Request request, RoutingResult routingResult, DataMap entityDataMap, Callback<RestLiResponse> callback) {
ServerResourceContext context = routingResult.getContext();
ResourceMethodDescriptor method = routingResult.getResourceMethod();
FilterRequestContext filterContext;
RestLiArgumentBuilder argumentBuilder;
try {
argumentBuilder = lookupArgumentBuilder(method);
// Unstructured data is not available in the Rest.Li filters
RestLiRequestData requestData = argumentBuilder.extractRequestData(routingResult, entityDataMap);
filterContext = new FilterRequestContextInternalImpl(context, method, requestData);
} catch (Exception e) {
// would not trigger response filters because request filters haven't run yet
callback.onError(buildPreRoutingError(e, request));
return;
}
RestLiFilterResponseContextFactory filterResponseContextFactory = new RestLiFilterResponseContextFactory(request, routingResult, _responseHandler);
FilterChainCallback filterChainCallback = new FilterChainCallbackImpl(routingResult, _responseHandler, callback, _errorResponseBuilder);
FilterChainDispatcher filterChainDispatcher = new FilterChainDispatcherImpl(routingResult, _methodInvoker, argumentBuilder);
RestLiFilterChain filterChain = new RestLiFilterChain(_filters, filterChainDispatcher, filterChainCallback);
TimingContextUtil.beginTiming(routingResult.getContext().getRawRequestContext(), FrameworkTimingKeys.SERVER_REQUEST_RESTLI_FILTER_CHAIN.key());
filterChain.onRequest(filterContext, filterResponseContextFactory);
}
use of com.linkedin.restli.server.filter.FilterRequestContext in project rest.li by linkedin.
the class TestFilterRequestContextInternalImpl method testFilterScratchpad.
@Test
public void testFilterScratchpad() {
FilterRequestContext filterContext = new FilterRequestContextInternalImpl(context, resourceMethod, null);
Object spValue = new Object();
String spKey = UUID.randomUUID().toString();
filterContext.getFilterScratchpad().put(spKey, spValue);
assertSame(filterContext.getFilterScratchpad().get(spKey), spValue);
}
Aggregations