From: Kai Moritz Date: Tue, 7 Jun 2016 13:05:11 +0000 (+0200) Subject: Refactoring: moved all code concerned with error-handling in one package X-Git-Tag: wip-webclient~79 X-Git-Url: https://juplo.de/gitweb/?p=facebook-errors;a=commitdiff_plain;h=aa08a5cc575e5228c267686ba668cdbb3f361a90 Refactoring: moved all code concerned with error-handling in one package --- diff --git a/src/main/java/de/juplo/facebook/FacebookUtils.java b/src/main/java/de/juplo/facebook/FacebookUtils.java index b2b8261..1a76601 100644 --- a/src/main/java/de/juplo/facebook/FacebookUtils.java +++ b/src/main/java/de/juplo/facebook/FacebookUtils.java @@ -2,7 +2,7 @@ package de.juplo.facebook; import de.juplo.facebook.token.SignedRequestAwareAuthorizationCodeAccessTokenProvider; -import de.juplo.facebook.client.GraphApiErrorHandler; +import de.juplo.facebook.exceptions.GraphApiErrorHandler; import java.util.Arrays; import java.util.LinkedList; import java.util.List; diff --git a/src/main/java/de/juplo/facebook/client/GraphApiErrorHandler.java b/src/main/java/de/juplo/facebook/client/GraphApiErrorHandler.java deleted file mode 100644 index 6b1ab97..0000000 --- a/src/main/java/de/juplo/facebook/client/GraphApiErrorHandler.java +++ /dev/null @@ -1,140 +0,0 @@ -package de.juplo.facebook.client; - -import de.juplo.facebook.exceptions.GraphApiException; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.util.FileCopyUtils; -import org.springframework.web.client.ResponseErrorHandler; - - - -/** - * Error-Handler for error-messages from the Facebook Graph-API. - *

- * This error-handler handels responses withe the HTTP-status code - * {@code 400 BAD REQUEST}. It tries to extract and parse the error-message - * from the HTTP-body. Successfully extracted and parsed messages are mapped - * to a hierarchy of exceptions, that reflects the hierarchy of the error- - * codes and -types. - *

- * If the HTTP-status-code of the response is not {@code 400 BAD REQUEST} or - * the HTTP-body could not be extracted or parsed, this error-handler - * delegates the handling to its parent. - * - * @see Graph-API Documentation - * @see Inofficial Wiki For Facebook-Developers - * @author Kai Moritz - */ -public class GraphApiErrorHandler implements ResponseErrorHandler -{ - private final static Logger LOG = - LoggerFactory.getLogger(GraphApiErrorHandler.class); - - private final ResponseErrorHandler parent; - - - public GraphApiErrorHandler(ResponseErrorHandler errorHandler) - { - this.parent = errorHandler; - } - - - @Override - public boolean hasError(ClientHttpResponse response) throws IOException - { - return - HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series()) - || this.parent.hasError(response); - } - - @Override - public void handleError(final ClientHttpResponse response) throws IOException - { - if (!HttpStatus.BAD_REQUEST.equals(response.getStatusCode())) - { - // We will only handle 400 BAD REQUEST - LOG.debug("ignoring response with status-code {}.", response.getStatusCode()); - parent.handleError(response); - return; - } - - - if (response.getBody() == null) - { - // There is no body to interpret in the HTTP-message - LOG.warn("Could not convert the response into an exception, because there is no message-body."); - parent.handleError(response); - return; - } - - final byte[] body = FileCopyUtils.copyToByteArray(response.getBody()); - GraphApiException error; - - try - { - error = GraphApiException.create(body); - if (LOG.isInfoEnabled()) - LOG.info("error-response: {}", new String(body, Charset.forName("UTF-8"))); - } - catch (Exception e) - { - // The body of the HTTP-message could not be parsed. - // Let the parent error-handler try to handle the response. - - LOG.warn("Could not convert the response into an exception, because the body is unparsable: {}", body); - - // To do so, we have to wrap the original response to fill in - // the buffered body, if needed - ClientHttpResponse buffered = new ClientHttpResponse() - { - @Override - public HttpStatus getStatusCode() throws IOException - { - return response.getStatusCode(); - } - - @Override - public synchronized InputStream getBody() throws IOException - { - return new ByteArrayInputStream(body); - } - - @Override - public HttpHeaders getHeaders() - { - return response.getHeaders(); - } - - @Override - public String getStatusText() throws IOException - { - return response.getStatusText(); - } - - @Override - public void close() - { - response.close(); - } - - @Override - public int getRawStatusCode() throws IOException - { - return response.getRawStatusCode(); - } - }; - - parent.handleError(buffered); - return; - } - - throw error; - } -} diff --git a/src/main/java/de/juplo/facebook/exceptions/GraphApiErrorHandler.java b/src/main/java/de/juplo/facebook/exceptions/GraphApiErrorHandler.java new file mode 100644 index 0000000..974bf85 --- /dev/null +++ b/src/main/java/de/juplo/facebook/exceptions/GraphApiErrorHandler.java @@ -0,0 +1,140 @@ +package de.juplo.facebook.exceptions; + + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.client.ResponseErrorHandler; + + + +/** + * Error-Handler for error-messages from the Facebook Graph-API. + *

+ * This error-handler handels responses withe the HTTP-status code + * {@code 400 BAD REQUEST}. It tries to extract and parse the error-message + * from the HTTP-body. Successfully extracted and parsed messages are mapped + * to a hierarchy of exceptions, that reflects the hierarchy of the error- + * codes and -types. + *

+ * If the HTTP-status-code of the response is not {@code 400 BAD REQUEST} or + * the HTTP-body could not be extracted or parsed, this error-handler + * delegates the handling to its parent. + * + * @see Graph-API Documentation + * @see Inofficial Wiki For Facebook-Developers + * @author Kai Moritz + */ +public class GraphApiErrorHandler implements ResponseErrorHandler +{ + private final static Logger LOG = + LoggerFactory.getLogger(GraphApiErrorHandler.class); + + private final ResponseErrorHandler parent; + + + public GraphApiErrorHandler(ResponseErrorHandler errorHandler) + { + this.parent = errorHandler; + } + + + @Override + public boolean hasError(ClientHttpResponse response) throws IOException + { + return + HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series()) + || this.parent.hasError(response); + } + + @Override + public void handleError(final ClientHttpResponse response) throws IOException + { + if (!HttpStatus.BAD_REQUEST.equals(response.getStatusCode())) + { + // We will only handle 400 BAD REQUEST + LOG.debug("ignoring response with status-code {}.", response.getStatusCode()); + parent.handleError(response); + return; + } + + + if (response.getBody() == null) + { + // There is no body to interpret in the HTTP-message + LOG.warn("Could not convert the response into an exception, because there is no message-body."); + parent.handleError(response); + return; + } + + final byte[] body = FileCopyUtils.copyToByteArray(response.getBody()); + GraphApiException error; + + try + { + error = GraphApiException.create(body); + if (LOG.isInfoEnabled()) + LOG.info("error-response: {}", new String(body, Charset.forName("UTF-8"))); + } + catch (Exception e) + { + // The body of the HTTP-message could not be parsed. + // Let the parent error-handler try to handle the response. + + LOG.warn("Could not convert the response into an exception, because the body is unparsable: {}", body); + + // To do so, we have to wrap the original response to fill in + // the buffered body, if needed + ClientHttpResponse buffered = new ClientHttpResponse() + { + @Override + public HttpStatus getStatusCode() throws IOException + { + return response.getStatusCode(); + } + + @Override + public synchronized InputStream getBody() throws IOException + { + return new ByteArrayInputStream(body); + } + + @Override + public HttpHeaders getHeaders() + { + return response.getHeaders(); + } + + @Override + public String getStatusText() throws IOException + { + return response.getStatusText(); + } + + @Override + public void close() + { + response.close(); + } + + @Override + public int getRawStatusCode() throws IOException + { + return response.getRawStatusCode(); + } + }; + + parent.handleError(buffered); + return; + } + + throw error; + } +} diff --git a/src/test/java/de/juplo/facebook/client/GraphApiErrorHandlerTest.java b/src/test/java/de/juplo/facebook/client/GraphApiErrorHandlerTest.java deleted file mode 100644 index c9921fa..0000000 --- a/src/test/java/de/juplo/facebook/client/GraphApiErrorHandlerTest.java +++ /dev/null @@ -1,808 +0,0 @@ -package de.juplo.facebook.client; - -import de.juplo.facebook.exceptions.AccessTokenRequiredException; -import de.juplo.facebook.exceptions.CallbackVerificationFailedException; -import de.juplo.facebook.exceptions.UnsupportedGetRequestException; -import de.juplo.facebook.exceptions.UnexpectedErrorException; -import de.juplo.facebook.exceptions.RateExceededException; -import de.juplo.facebook.exceptions.GraphApiException; -import de.juplo.facebook.exceptions.GraphApiException.Type; -import de.juplo.facebook.exceptions.UnknownErrorException; -import de.juplo.facebook.exceptions.PageMigratedException; -import de.juplo.facebook.exceptions.UnmappedErrorException; -import java.util.Date; -import java.util.Map; -import java.util.Set; -import javax.annotation.Resource; -import static org.junit.Assert.*; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler; -import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; -import org.springframework.security.oauth2.client.resource.UserApprovalRequiredException; -import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; -import org.springframework.security.oauth2.client.token.AccessTokenProvider; -import org.springframework.security.oauth2.client.token.AccessTokenRequest; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import static org.springframework.security.oauth2.common.OAuth2AccessToken.OAUTH2_TYPE; -import org.springframework.security.oauth2.common.OAuth2RefreshToken; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.web.client.HttpClientErrorException; - - - -/** - * - * @author kai - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration( - locations = { - "classpath:/spring/test-facebook-error-handler.xml" - }) -public class GraphApiErrorHandlerTest -{ - private static final Logger log = - LoggerFactory.getLogger(GraphApiErrorHandlerTest.class); - - @Resource - private OAuth2RestTemplate clientTemplate; - - private MockClientHttpRequestFactory requestFactory; - - - @Test - public void testError1() - { - log.info("testError1"); - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"An unknown error has occurred.\",\n" + - " \"type\": \"OAuthException\",\n" + - " \"code\": 1\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(UnknownErrorException e) - { - log.debug("{}", e.toString()); - assertEquals("invalid_request", e.getOAuth2ErrorCode()); - assertEquals(new Integer(1), e.getCode()); - assertEquals("An unknown error has occurred.", e.getMessage()); - assertEquals(Type.OAuthException, e.getType()); - } - } - - @Test - public void testError2() - { - log.info("testError2"); - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"An unexpected error has occurred. Please retry your request later.\",\n" + - " \"type\": \"OAuthException\",\n" + - " \"code\": 2\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(UnexpectedErrorException e) - { - log.debug("{}", e.toString()); - assertEquals("invalid_request", e.getOAuth2ErrorCode()); - assertEquals(new Integer(2), e.getCode()); - assertEquals("An unexpected error has occurred. Please retry your request later.", e.getMessage()); - assertEquals(Type.OAuthException, e.getType()); - } - } - - @Test - public void testError21() - { - log.info("testError21"); - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"(#21) Page ID 590408587650316 was migrated to page ID 1421620791415603. Please update your API calls to the new ID\",\n" + - " \"type\": \"OAuthException\",\n" + - " \"code\": 21\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(PageMigratedException e) - { - log.debug("{}", e.toString()); - assertEquals("invalid_request", e.getOAuth2ErrorCode()); - assertEquals(new Integer(21), e.getCode()); - assertEquals("(#21) Page ID 590408587650316 was migrated to page ID 1421620791415603. Please update your API calls to the new ID", e.getMessage()); - assertEquals(Type.OAuthException, e.getType()); - } - } - - @Test - public void testError100() - { - log.info("testError100"); - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"Unsupported get request.\",\n" + - " \"type\": \"GraphMethodException\",\n" + - " \"code\": 100\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(UnsupportedGetRequestException e) - { - log.debug("{}", e.toString()); - assertEquals("invalid_request", e.getOAuth2ErrorCode()); - assertEquals(new Integer(100), e.getCode()); - assertEquals("Unsupported get request.", e.getMessage()); - assertEquals(Type.GraphMethodException, e.getType()); - } - } - - @Test - public void testError104() - { - log.info("testError104"); - - requestFactory.setBody("{\"error\":{\"message\":\"An access token is required to request this resource.\",\"type\":\"OAuthException\",\"code\":104,\"fbtrace_id\":\"E2Jjkj5++LL\"}}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(AccessTokenRequiredException e) - { - log.debug("{}", e.toString()); - assertEquals("invalid_request", e.getOAuth2ErrorCode()); - assertEquals(new Integer(104), e.getCode()); - assertEquals("An access token is required to request this resource.", e.getMessage()); - assertEquals(Type.OAuthException, e.getType()); - assertEquals("E2Jjkj5++LL", e.getTraceId()); - } - } - - @Test - public void testError613() - { - log.info("testError613"); - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"(#613) Calls to stream have exceeded the rate of 600 calls per 600 seconds.\",\n" + - " \"type\": \"OAuthException\",\n" + - " \"code\": 613\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(RateExceededException e) - { - log.debug("{}", e.toString()); - assertEquals("invalid_request", e.getOAuth2ErrorCode()); - assertEquals(new Integer(613), e.getCode()); - assertEquals("(#613) Calls to stream have exceeded the rate of 600 calls per 600 seconds.", e.getMessage()); - assertEquals(Type.OAuthException, e.getType()); - } - } - - @Test - public void testError2200() - { - requestFactory.setBody("{\"error\":{\"message\":\"(#2200) callback verification failed: \",\"type\":\"OAuthException\",\"code\":2200,\"fbtrace_id\":\"ESLjoZKvPXg\"}}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(CallbackVerificationFailedException e) - { - log.debug("{}", e.toString()); - assertEquals("invalid_request", e.getOAuth2ErrorCode()); - assertEquals(new Integer(2200), e.getCode()); - assertEquals("(#2200) callback verification failed: ", e.getMessage()); - assertEquals(Type.OAuthException, e.getType()); - assertEquals("ESLjoZKvPXg", e.getTraceId()); - } - } - - @Test - public void testUnmappedError() - { - log.info("testUnmappedError"); - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"This error does not exist.\",\n" + - " \"type\": \"NonexistentTypeException\",\n" + - " \"code\": 999999999\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(GraphApiException e) - { - log.debug("{}", e.toString()); - assertEquals("invalid_request", e.getOAuth2ErrorCode()); - assertEquals(new Integer(999999999), e.getCode()); - assertEquals("This error does not exist.", e.getMessage()); - try - { - Type type = e.getType(); - log.error("unknown type: {}", type); - fail("unmapped type was resolved by enum: " + type); - } - catch (IllegalArgumentException ee) {} - } - } - - @Test - public void testUnmappedErrors() - { - log.info("testUnmappedErrors"); - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": null,\n" + - " \"type\": \"WhateverTypeException\",\n" + - " \"code\": 999999999\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(UnmappedErrorException e) - { - log.debug("{}", e.toString()); - assertNull(e.getMessage()); - try - { - Type type = e.getType(); - log.error("unknown type: {}", type); - fail("unmapped type was resolved by enum: " + type); - } - catch (IllegalArgumentException ee) {} - assertEquals(new Integer(999999999), e.getCode()); - assertNull(e.getSubCode()); - assertNull(e.getUserTitle()); - assertNull(e.getUserMessage()); - assertNull(e.getTraceId()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"type\": \"WhateverTypeException\",\n" + - " \"code\": 999999999\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(UnmappedErrorException e) - { - log.debug("{}", e.toString()); - assertNull(e.getMessage()); - try - { - Type type = e.getType(); - log.error("unknown type: {}", type); - fail("unmapped type was resolved by enum: " + type); - } - catch (IllegalArgumentException ee) {} - assertEquals(new Integer(999999999), e.getCode()); - assertNull(e.getSubCode()); - assertNull(e.getUserTitle()); - assertNull(e.getUserMessage()); - assertNull(e.getTraceId()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"An unmapped Graph-API-Exception.\",\n" + - " \"type\": null,\n" + - " \"code\": 999999999\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(UnmappedErrorException e) - { - log.debug("{}", e.toString()); - assertEquals("An unmapped Graph-API-Exception.", e.getMessage()); - assertNull(e.getType()); - assertEquals(new Integer(999999999), e.getCode()); - assertNull(e.getSubCode()); - assertNull(e.getUserTitle()); - assertNull(e.getUserMessage()); - assertNull(e.getTraceId()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"An unmapped Graph-API-Exception.\",\n" + - " \"code\": 999999999\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(UnmappedErrorException e) - { - log.debug("{}", e.toString()); - assertEquals("An unmapped Graph-API-Exception.", e.getMessage()); - assertNull(e.getType()); - assertEquals(new Integer(999999999), e.getCode()); - assertNull(e.getSubCode()); - assertNull(e.getUserTitle()); - assertNull(e.getUserMessage()); - assertNull(e.getTraceId()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - } - - @Test - public void testInvlalidErrors() - { - log.info("testInvalidErrors"); - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"Not a Graph-Api-Exception.\",\n" + - " \"type\": \"Whatever\",\n" + - " \"code\": \"some string\"\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"Not a Graph-Api-Exception.\",\n" + - " \"type\": \"Whatever\",\n" + - " \"code\": 9.9\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"Not a Graph-Api-Exception.\",\n" + - " \"type\": \"Whatever\",\n" + - " \"code\": null\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody( - "{\n" + - " \"error\":\n" + - " {\n" + - " \"message\": \"Not a Graph-Api-Exception.\",\n" + - " \"type\": \"Whatever\"\n" + - " }\n" + - "}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody("{\"error\":{\"message\":null}}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody("{\"error\":{\"type\":null}}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody("{\"error\":{\"code\":null}}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody("{\"error\":{}}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody("{\"error\":\"some message\"}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody("{\"error\":null}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody("{\"some filed\":\"some message\"}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody("{}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - - - requestFactory.setBody(""); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(HttpClientErrorException e) - { - log.debug("{}", e.toString()); - } - catch(Exception e) - { - fail("A wrong exception was thrown: " + e.toString()); - } - } - - - @Before - public void setUp() - { - requestFactory = new MockClientHttpRequestFactory(); - requestFactory.setStatus(HttpStatus.BAD_REQUEST); - requestFactory.addHeader("Content-Type", "application/json"); - clientTemplate.setRequestFactory(requestFactory); - - clientTemplate.setErrorHandler( - new GraphApiErrorHandler( - (OAuth2ErrorHandler)clientTemplate.getErrorHandler() - ) - ); - - clientTemplate.setAccessTokenProvider(new AccessTokenProvider() - { - @Override - public OAuth2AccessToken obtainAccessToken( - OAuth2ProtectedResourceDetails details, - AccessTokenRequest parameters - ) - throws - UserRedirectRequiredException, - UserApprovalRequiredException, - AccessDeniedException - { - return new OAuth2AccessToken() { - - @Override - public Map getAdditionalInformation() - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public Set getScope() - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public OAuth2RefreshToken getRefreshToken() - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public String getTokenType() - { - return OAUTH2_TYPE; - } - - @Override - public boolean isExpired() - { - return false; - } - - @Override - public Date getExpiration() - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public int getExpiresIn() - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public String getValue() - { - return "ANY"; - } - }; - } - - @Override - public boolean supportsResource(OAuth2ProtectedResourceDetails resource) - { - return true; - } - - @Override - public OAuth2AccessToken refreshAccessToken( - OAuth2ProtectedResourceDetails resource, - OAuth2RefreshToken refreshToken, - AccessTokenRequest request - ) - throws - UserRedirectRequiredException - { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public boolean supportsRefresh(OAuth2ProtectedResourceDetails resource) - { - return false; - } - }); - } - - - static class SOME - { - } -} diff --git a/src/test/java/de/juplo/facebook/client/MockClientHttpRequestFactory.java b/src/test/java/de/juplo/facebook/client/MockClientHttpRequestFactory.java deleted file mode 100644 index 7fee109..0000000 --- a/src/test/java/de/juplo/facebook/client/MockClientHttpRequestFactory.java +++ /dev/null @@ -1,144 +0,0 @@ -package de.juplo.facebook.client; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.ClientHttpRequest; -import org.springframework.http.client.ClientHttpRequestFactory; -import org.springframework.http.client.ClientHttpResponse; - - - -/** - * - * @author kai - */ -public class MockClientHttpRequestFactory implements ClientHttpRequestFactory -{ - private static final Logger log = - LoggerFactory.getLogger(MockClientHttpRequestFactory.class); - - private HttpStatus status = HttpStatus.OK; - private HttpHeaders headers = new HttpHeaders(); - private String body = ""; - - - @Override - public ClientHttpRequest createRequest(URI uri, HttpMethod method) throws IOException - { - return new MockClientHttpRequest(uri, method); - } - - public void setStatus(HttpStatus status) - { - this.status = status; - } - - public void setHeaders(HttpHeaders headers) - { - this.headers = headers; - } - - public void addHeader(String name, String value) - { - headers.add(name, value); - } - - public void setBody(String body) - { - log.trace(body); - this.body = body; - } - - - class MockClientHttpRequest implements ClientHttpRequest - { - private final URI uri; - private final HttpMethod method; - - - public MockClientHttpRequest(URI uri, HttpMethod method) - { - this.uri = uri; - this.method = method; - } - - - @Override - public ClientHttpResponse execute() throws IOException - { - return new MockClientHttpResponse(); - } - - @Override - public HttpMethod getMethod() - { - return method; - } - - @Override - public URI getURI() - { - return uri; - } - - @Override - public HttpHeaders getHeaders() - { - return headers; - } - - @Override - public OutputStream getBody() throws IOException - { - throw new UnsupportedOperationException("Not supported yet."); - } - } - - - class MockClientHttpResponse implements ClientHttpResponse - { - @Override - public HttpStatus getStatusCode() throws IOException - { - return status; - } - - @Override - public int getRawStatusCode() throws IOException - { - return status.value(); - } - - @Override - public String getStatusText() throws IOException - { - return status.getReasonPhrase(); - } - - @Override - public void close() - { - } - - @Override - public InputStream getBody() throws IOException - { - return new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); - } - - @Override - public HttpHeaders getHeaders() - { - return headers; - } - } -} diff --git a/src/test/java/de/juplo/facebook/exceptions/GraphApiErrorHandlerTest.java b/src/test/java/de/juplo/facebook/exceptions/GraphApiErrorHandlerTest.java new file mode 100644 index 0000000..86e1dfd --- /dev/null +++ b/src/test/java/de/juplo/facebook/exceptions/GraphApiErrorHandlerTest.java @@ -0,0 +1,801 @@ +package de.juplo.facebook.exceptions; + +import de.juplo.facebook.exceptions.GraphApiException.Type; +import java.util.Date; +import java.util.Map; +import java.util.Set; +import javax.annotation.Resource; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.resource.UserApprovalRequiredException; +import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; +import org.springframework.security.oauth2.client.token.AccessTokenProvider; +import org.springframework.security.oauth2.client.token.AccessTokenRequest; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import static org.springframework.security.oauth2.common.OAuth2AccessToken.OAUTH2_TYPE; +import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.web.client.HttpClientErrorException; + + + +/** + * + * @author Kai Moritz + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration( + locations = { + "classpath:/spring/test-facebook-error-handler.xml" + }) +public class GraphApiErrorHandlerTest +{ + private static final Logger log = + LoggerFactory.getLogger(GraphApiErrorHandlerTest.class); + + @Resource + private OAuth2RestTemplate clientTemplate; + + private MockClientHttpRequestFactory requestFactory; + + + @Test + public void testError1() + { + log.info("testError1"); + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"An unknown error has occurred.\",\n" + + " \"type\": \"OAuthException\",\n" + + " \"code\": 1\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(UnknownErrorException e) + { + log.debug("{}", e.toString()); + assertEquals("invalid_request", e.getOAuth2ErrorCode()); + assertEquals(new Integer(1), e.getCode()); + assertEquals("An unknown error has occurred.", e.getMessage()); + assertEquals(Type.OAuthException, e.getType()); + } + } + + @Test + public void testError2() + { + log.info("testError2"); + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"An unexpected error has occurred. Please retry your request later.\",\n" + + " \"type\": \"OAuthException\",\n" + + " \"code\": 2\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(UnexpectedErrorException e) + { + log.debug("{}", e.toString()); + assertEquals("invalid_request", e.getOAuth2ErrorCode()); + assertEquals(new Integer(2), e.getCode()); + assertEquals("An unexpected error has occurred. Please retry your request later.", e.getMessage()); + assertEquals(Type.OAuthException, e.getType()); + } + } + + @Test + public void testError21() + { + log.info("testError21"); + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"(#21) Page ID 590408587650316 was migrated to page ID 1421620791415603. Please update your API calls to the new ID\",\n" + + " \"type\": \"OAuthException\",\n" + + " \"code\": 21\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(PageMigratedException e) + { + log.debug("{}", e.toString()); + assertEquals("invalid_request", e.getOAuth2ErrorCode()); + assertEquals(new Integer(21), e.getCode()); + assertEquals("(#21) Page ID 590408587650316 was migrated to page ID 1421620791415603. Please update your API calls to the new ID", e.getMessage()); + assertEquals(Type.OAuthException, e.getType()); + } + } + + @Test + public void testError100() + { + log.info("testError100"); + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"Unsupported get request.\",\n" + + " \"type\": \"GraphMethodException\",\n" + + " \"code\": 100\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(UnsupportedGetRequestException e) + { + log.debug("{}", e.toString()); + assertEquals("invalid_request", e.getOAuth2ErrorCode()); + assertEquals(new Integer(100), e.getCode()); + assertEquals("Unsupported get request.", e.getMessage()); + assertEquals(Type.GraphMethodException, e.getType()); + } + } + + @Test + public void testError104() + { + log.info("testError104"); + + requestFactory.setBody("{\"error\":{\"message\":\"An access token is required to request this resource.\",\"type\":\"OAuthException\",\"code\":104,\"fbtrace_id\":\"E2Jjkj5++LL\"}}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(AccessTokenRequiredException e) + { + log.debug("{}", e.toString()); + assertEquals("invalid_request", e.getOAuth2ErrorCode()); + assertEquals(new Integer(104), e.getCode()); + assertEquals("An access token is required to request this resource.", e.getMessage()); + assertEquals(Type.OAuthException, e.getType()); + assertEquals("E2Jjkj5++LL", e.getTraceId()); + } + } + + @Test + public void testError613() + { + log.info("testError613"); + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"(#613) Calls to stream have exceeded the rate of 600 calls per 600 seconds.\",\n" + + " \"type\": \"OAuthException\",\n" + + " \"code\": 613\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(RateExceededException e) + { + log.debug("{}", e.toString()); + assertEquals("invalid_request", e.getOAuth2ErrorCode()); + assertEquals(new Integer(613), e.getCode()); + assertEquals("(#613) Calls to stream have exceeded the rate of 600 calls per 600 seconds.", e.getMessage()); + assertEquals(Type.OAuthException, e.getType()); + } + } + + @Test + public void testError2200() + { + requestFactory.setBody("{\"error\":{\"message\":\"(#2200) callback verification failed: \",\"type\":\"OAuthException\",\"code\":2200,\"fbtrace_id\":\"ESLjoZKvPXg\"}}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(CallbackVerificationFailedException e) + { + log.debug("{}", e.toString()); + assertEquals("invalid_request", e.getOAuth2ErrorCode()); + assertEquals(new Integer(2200), e.getCode()); + assertEquals("(#2200) callback verification failed: ", e.getMessage()); + assertEquals(Type.OAuthException, e.getType()); + assertEquals("ESLjoZKvPXg", e.getTraceId()); + } + } + + @Test + public void testUnmappedError() + { + log.info("testUnmappedError"); + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"This error does not exist.\",\n" + + " \"type\": \"NonexistentTypeException\",\n" + + " \"code\": 999999999\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(GraphApiException e) + { + log.debug("{}", e.toString()); + assertEquals("invalid_request", e.getOAuth2ErrorCode()); + assertEquals(new Integer(999999999), e.getCode()); + assertEquals("This error does not exist.", e.getMessage()); + try + { + Type type = e.getType(); + log.error("unknown type: {}", type); + fail("unmapped type was resolved by enum: " + type); + } + catch (IllegalArgumentException ee) {} + } + } + + @Test + public void testUnmappedErrors() + { + log.info("testUnmappedErrors"); + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": null,\n" + + " \"type\": \"WhateverTypeException\",\n" + + " \"code\": 999999999\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(UnmappedErrorException e) + { + log.debug("{}", e.toString()); + assertNull(e.getMessage()); + try + { + Type type = e.getType(); + log.error("unknown type: {}", type); + fail("unmapped type was resolved by enum: " + type); + } + catch (IllegalArgumentException ee) {} + assertEquals(new Integer(999999999), e.getCode()); + assertNull(e.getSubCode()); + assertNull(e.getUserTitle()); + assertNull(e.getUserMessage()); + assertNull(e.getTraceId()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"type\": \"WhateverTypeException\",\n" + + " \"code\": 999999999\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(UnmappedErrorException e) + { + log.debug("{}", e.toString()); + assertNull(e.getMessage()); + try + { + Type type = e.getType(); + log.error("unknown type: {}", type); + fail("unmapped type was resolved by enum: " + type); + } + catch (IllegalArgumentException ee) {} + assertEquals(new Integer(999999999), e.getCode()); + assertNull(e.getSubCode()); + assertNull(e.getUserTitle()); + assertNull(e.getUserMessage()); + assertNull(e.getTraceId()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"An unmapped Graph-API-Exception.\",\n" + + " \"type\": null,\n" + + " \"code\": 999999999\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(UnmappedErrorException e) + { + log.debug("{}", e.toString()); + assertEquals("An unmapped Graph-API-Exception.", e.getMessage()); + assertNull(e.getType()); + assertEquals(new Integer(999999999), e.getCode()); + assertNull(e.getSubCode()); + assertNull(e.getUserTitle()); + assertNull(e.getUserMessage()); + assertNull(e.getTraceId()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"An unmapped Graph-API-Exception.\",\n" + + " \"code\": 999999999\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(UnmappedErrorException e) + { + log.debug("{}", e.toString()); + assertEquals("An unmapped Graph-API-Exception.", e.getMessage()); + assertNull(e.getType()); + assertEquals(new Integer(999999999), e.getCode()); + assertNull(e.getSubCode()); + assertNull(e.getUserTitle()); + assertNull(e.getUserMessage()); + assertNull(e.getTraceId()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + } + + @Test + public void testInvlalidErrors() + { + log.info("testInvalidErrors"); + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"Not a Graph-Api-Exception.\",\n" + + " \"type\": \"Whatever\",\n" + + " \"code\": \"some string\"\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"Not a Graph-Api-Exception.\",\n" + + " \"type\": \"Whatever\",\n" + + " \"code\": 9.9\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"Not a Graph-Api-Exception.\",\n" + + " \"type\": \"Whatever\",\n" + + " \"code\": null\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody( + "{\n" + + " \"error\":\n" + + " {\n" + + " \"message\": \"Not a Graph-Api-Exception.\",\n" + + " \"type\": \"Whatever\"\n" + + " }\n" + + "}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody("{\"error\":{\"message\":null}}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody("{\"error\":{\"type\":null}}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody("{\"error\":{\"code\":null}}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody("{\"error\":{}}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody("{\"error\":\"some message\"}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody("{\"error\":null}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody("{\"some filed\":\"some message\"}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody("{}"); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + + + requestFactory.setBody(""); + + try + { + clientTemplate.getForObject("ANY", SOME.class); + fail("The expected exception was not thrown"); + } + catch(HttpClientErrorException e) + { + log.debug("{}", e.toString()); + } + catch(Exception e) + { + fail("A wrong exception was thrown: " + e.toString()); + } + } + + + @Before + public void setUp() + { + requestFactory = new MockClientHttpRequestFactory(); + requestFactory.setStatus(HttpStatus.BAD_REQUEST); + requestFactory.addHeader("Content-Type", "application/json"); + clientTemplate.setRequestFactory(requestFactory); + + clientTemplate.setErrorHandler( + new GraphApiErrorHandler( + (OAuth2ErrorHandler)clientTemplate.getErrorHandler() + ) + ); + + clientTemplate.setAccessTokenProvider(new AccessTokenProvider() + { + @Override + public OAuth2AccessToken obtainAccessToken( + OAuth2ProtectedResourceDetails details, + AccessTokenRequest parameters + ) + throws + UserRedirectRequiredException, + UserApprovalRequiredException, + AccessDeniedException + { + return new OAuth2AccessToken() { + + @Override + public Map getAdditionalInformation() + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set getScope() + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public OAuth2RefreshToken getRefreshToken() + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public String getTokenType() + { + return OAUTH2_TYPE; + } + + @Override + public boolean isExpired() + { + return false; + } + + @Override + public Date getExpiration() + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int getExpiresIn() + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public String getValue() + { + return "ANY"; + } + }; + } + + @Override + public boolean supportsResource(OAuth2ProtectedResourceDetails resource) + { + return true; + } + + @Override + public OAuth2AccessToken refreshAccessToken( + OAuth2ProtectedResourceDetails resource, + OAuth2RefreshToken refreshToken, + AccessTokenRequest request + ) + throws + UserRedirectRequiredException + { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean supportsRefresh(OAuth2ProtectedResourceDetails resource) + { + return false; + } + }); + } + + + static class SOME + { + } +} diff --git a/src/test/java/de/juplo/facebook/exceptions/MockClientHttpRequestFactory.java b/src/test/java/de/juplo/facebook/exceptions/MockClientHttpRequestFactory.java new file mode 100644 index 0000000..34dc793 --- /dev/null +++ b/src/test/java/de/juplo/facebook/exceptions/MockClientHttpRequestFactory.java @@ -0,0 +1,144 @@ +package de.juplo.facebook.exceptions; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.ClientHttpResponse; + + + +/** + * + * @author kai + */ +public class MockClientHttpRequestFactory implements ClientHttpRequestFactory +{ + private static final Logger log = + LoggerFactory.getLogger(MockClientHttpRequestFactory.class); + + private HttpStatus status = HttpStatus.OK; + private HttpHeaders headers = new HttpHeaders(); + private String body = ""; + + + @Override + public ClientHttpRequest createRequest(URI uri, HttpMethod method) throws IOException + { + return new MockClientHttpRequest(uri, method); + } + + public void setStatus(HttpStatus status) + { + this.status = status; + } + + public void setHeaders(HttpHeaders headers) + { + this.headers = headers; + } + + public void addHeader(String name, String value) + { + headers.add(name, value); + } + + public void setBody(String body) + { + log.trace(body); + this.body = body; + } + + + class MockClientHttpRequest implements ClientHttpRequest + { + private final URI uri; + private final HttpMethod method; + + + public MockClientHttpRequest(URI uri, HttpMethod method) + { + this.uri = uri; + this.method = method; + } + + + @Override + public ClientHttpResponse execute() throws IOException + { + return new MockClientHttpResponse(); + } + + @Override + public HttpMethod getMethod() + { + return method; + } + + @Override + public URI getURI() + { + return uri; + } + + @Override + public HttpHeaders getHeaders() + { + return headers; + } + + @Override + public OutputStream getBody() throws IOException + { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + + class MockClientHttpResponse implements ClientHttpResponse + { + @Override + public HttpStatus getStatusCode() throws IOException + { + return status; + } + + @Override + public int getRawStatusCode() throws IOException + { + return status.value(); + } + + @Override + public String getStatusText() throws IOException + { + return status.getReasonPhrase(); + } + + @Override + public void close() + { + } + + @Override + public InputStream getBody() throws IOException + { + return new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public HttpHeaders getHeaders() + { + return headers; + } + } +}