--- /dev/null
+package de.juplo.facebook;
+
+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.http.converter.HttpMessageNotReadableException;
+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.security.oauth2.common.exceptions.OAuth2Exception;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+
+
+/**
+ *
+ * @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(1, e.getCode());
+ assertEquals("An unknown error has occurred.", e.getMessage());
+ assertEquals("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(2, e.getCode());
+ assertEquals("An unexpected error has occurred. Please retry your request later.", e.getMessage());
+ assertEquals("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(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("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(100, e.getCode());
+ assertEquals("Unsupported get request.", e.getMessage());
+ assertEquals("GraphMethodException", e.getType());
+ }
+ }
+
+ @Test
+ public void testUnmappedError()
+ {
+ log.info("testUnmappedError");
+
+
+ requestFactory.setBody(
+ "{\n" +
+ " \"error\":\n" +
+ " {\n" +
+ " \"message\": \"This error does not exist.\",\n" +
+ " \"type\": \"NonexistentException\",\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(999999999, e.getCode());
+ assertEquals("This error does not exist.", e.getMessage());
+ assertEquals("NonexistentException", e.getType());
+ }
+ }
+
+ @Test
+ public void testInvlalidErrors()
+ {
+ log.info("testInvalidErrors");
+
+
+ requestFactory.setBody(
+ "{\n" +
+ " \"error\":\n" +
+ " {\n" +
+ " \"message\": null,\n" +
+ " \"type\": \"Whatever\",\n" +
+ " \"code\": 999999999\n" +
+ " }\n" +
+ "}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody(
+ "{\n" +
+ " \"error\":\n" +
+ " {\n" +
+ " \"type\": \"Whatever\",\n" +
+ " \"code\": 999999999\n" +
+ " }\n" +
+ "}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody(
+ "{\n" +
+ " \"error\":\n" +
+ " {\n" +
+ " \"message\": \"Not a 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(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody(
+ "{\n" +
+ " \"error\":\n" +
+ " {\n" +
+ " \"message\": \"Not a Graph-Api-Exception.\",\n" +
+ " \"code\": 999999999\n" +
+ " }\n" +
+ "}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ 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(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ 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(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ 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(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ 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(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody("{\"error\":{\"message\":null}}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody("{\"error\":{\"type\":null}}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody("{\"error\":{\"code\":null}}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody("{\"error\":{}}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody("{\"error\":\"some message\"}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody("{\"error\":null}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody("{\"some filed\":\"some message\"}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody("{}");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(OAuth2Exception e)
+ {
+ log.debug("{}", e.toString());
+ assertEquals("invalid_request", e.getOAuth2ErrorCode());
+ assertFalse(e instanceof GraphApiException);
+ }
+
+
+ requestFactory.setBody("");
+
+ try
+ {
+ clientTemplate.getForObject("ANY", SOME.class);
+ fail("The expected exception was not thrown");
+ }
+ catch(HttpMessageNotReadableException e)
+ {
+ // TODO: OAuth2ErrorHandler fails, if body contains no valid JSON!
+ log.debug("{}", 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<String, Object> getAdditionalInformation()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Set<String> 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
+ {
+ }
+}