From: Kai Moritz Date: Fri, 8 Jul 2016 16:40:46 +0000 (+0200) Subject: WIP X-Git-Url: http://juplo.de/gitweb/?a=commitdiff_plain;h=771cca83ccc455f1df1b70ab8f18c9bc082fb0ed;p=facebook-utils WIP --- diff --git a/src/main/java/de/juplo/facebook/FacebookUtils.java b/src/main/java/de/juplo/facebook/FacebookUtils.java index f6f6b37..8b39f50 100644 --- a/src/main/java/de/juplo/facebook/FacebookUtils.java +++ b/src/main/java/de/juplo/facebook/FacebookUtils.java @@ -1,7 +1,6 @@ package de.juplo.facebook; -import de.juplo.facebook.errors.GraphApiErrorHandler; import de.juplo.facebook.token.SignedRequestAwareAuthorizationCodeAccessTokenProvider; import java.util.Arrays; import java.util.LinkedList; @@ -15,7 +14,6 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler; import org.springframework.security.oauth2.client.token.AccessTokenProvider; import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; @@ -80,12 +78,6 @@ public class FacebookUtils provider.setSecret(clientSecret); chain.add(provider); template.setAccessTokenProvider(new AccessTokenProviderChain(chain)); - log.info("injecting GraphApiErrorHandler"); - template.setErrorHandler( - new GraphApiErrorHandler( - (OAuth2ErrorHandler)template.getErrorHandler() - ) - ); } return bean; diff --git a/src/main/java/de/juplo/facebook/aspects/Sanitize.java b/src/main/java/de/juplo/facebook/aspects/Sanitize.java deleted file mode 100644 index 0856a04..0000000 --- a/src/main/java/de/juplo/facebook/aspects/Sanitize.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.juplo.facebook.aspects; - - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author Kai Moritz - */ -@Target({ ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface Sanitize -{ - int length() default 255; // in accordance to @Column(length) - boolean fail() default false; -} diff --git a/src/main/java/de/juplo/facebook/aspects/SanitizeAspect.java b/src/main/java/de/juplo/facebook/aspects/SanitizeAspect.java deleted file mode 100644 index 4bdf908..0000000 --- a/src/main/java/de/juplo/facebook/aspects/SanitizeAspect.java +++ /dev/null @@ -1,93 +0,0 @@ -package de.juplo.facebook.aspects; - - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - - -/** - * @author Kai Moritz - */ -@Aspect -public class SanitizeAspect -{ - private static final Logger log = - LoggerFactory.getLogger(SanitizeAspect.class); - - - /** - * This method sanitizes the given string in all means: - * - * - * This method ensures that the output String has only - * valid XML unicode characters as specified by the - * XML 1.0 standard. For reference, please see - * the - * standard. This method will return an empty - * String if the input is null or empty. - * - * @param jp The join-point captured by AspectJ. - * @param in The String whose non-valid characters we want to remove. - * @param sanitize The annotation, the field was marked with. - * @see Invalid XML Characters: when valid UTF8 does not mean valid XML - * @see Ungültige Zeichen in Eingabefeldern abfangen - */ - @Around("set(String *) && args(in) && @annotation(sanitize)") - public void sanitize( - ProceedingJoinPoint jp, - String in, - Sanitize sanitize - ) - throws Throwable - { - if (in == null) - { - jp.proceed(new Object[] { null }); - return; - } - - in = in.trim(); - if ("".equals(in)) - { - jp.proceed(new Object[] { null }); - return; - } - - StringBuilder out = new StringBuilder(); // Used to hold the output. - char current; // Used to reference the current character. - - for (int i = 0; i < in.length(); i++) - { - current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen. - if ((current == 0x9) || - (current == 0xA) || - (current == 0xD) || - ((current >= 0x20) && (current <= 0xD7FF)) || - ((current >= 0xE000) && (current <= 0xFFFD)) || - ((current >= 0x10000) && (current <= 0x10FFFF))) - out.append(current); - } - if (out.length() > sanitize.length()) - { - log.error( - "Maximum length for attribute {} exceeded: should={}, was={}", - jp.getSignature().getName(), - sanitize.length(), - out.length() - ); - if (sanitize.fail()) - throw new RuntimeException("String is longer than " + sanitize.length()); - else - out.setLength(sanitize.length()); - } - jp.proceed(new Object[] { out.toString() }); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/AccessTokenRequiredException.java b/src/main/java/de/juplo/facebook/errors/AccessTokenRequiredException.java deleted file mode 100644 index 5b54096..0000000 --- a/src/main/java/de/juplo/facebook/errors/AccessTokenRequiredException.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.juplo.facebook.errors; - - - -/** - * 104: An access token is required to request this resource. - * @author Kai Moritz - */ -public class AccessTokenRequiredException extends OAuthException -{ - protected AccessTokenRequiredException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/CallbackVerificationFailedException.java b/src/main/java/de/juplo/facebook/errors/CallbackVerificationFailedException.java deleted file mode 100644 index 27ef936..0000000 --- a/src/main/java/de/juplo/facebook/errors/CallbackVerificationFailedException.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.juplo.facebook.errors; - - -/** - * 2200: Callback verification failed. - * @author Kai Moritz - */ -public class CallbackVerificationFailedException extends OAuthException -{ - protected CallbackVerificationFailedException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/FacebookErrorMessage.java b/src/main/java/de/juplo/facebook/errors/FacebookErrorMessage.java deleted file mode 100644 index 495c665..0000000 --- a/src/main/java/de/juplo/facebook/errors/FacebookErrorMessage.java +++ /dev/null @@ -1,42 +0,0 @@ -package de.juplo.facebook.errors; - - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonRootName; - - - -/** - * This class represents an error message from the Graph-API - * - * @see Graph-API Documentation - * @author Kai Moritz - */ -@JsonRootName("error") -@JsonPropertyOrder({ - "message", - "type", - "code", - "error_subcode", - "error_user_title", - "error_user_msg", - "fbtrace_id" - }) -public class FacebookErrorMessage -{ - @JsonProperty("message") - String message; - @JsonProperty("type") - String type; - @JsonProperty("code") - Integer code; - @JsonProperty("error_subcode") - Integer subCode; - @JsonProperty("error_user_title") - String userTitle; - @JsonProperty("error_user_msg") - String userMessage; - @JsonProperty("fbtrace_id") - String traceId; -} diff --git a/src/main/java/de/juplo/facebook/errors/GraphApiErrorHandler.java b/src/main/java/de/juplo/facebook/errors/GraphApiErrorHandler.java deleted file mode 100644 index 13a93e8..0000000 --- a/src/main/java/de/juplo/facebook/errors/GraphApiErrorHandler.java +++ /dev/null @@ -1,140 +0,0 @@ -package de.juplo.facebook.errors; - - -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/errors/GraphApiException.java b/src/main/java/de/juplo/facebook/errors/GraphApiException.java deleted file mode 100644 index a3c3904..0000000 --- a/src/main/java/de/juplo/facebook/errors/GraphApiException.java +++ /dev/null @@ -1,148 +0,0 @@ -package de.juplo.facebook.errors; - - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import java.io.IOException; -import java.io.InputStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; - - - -/** - * Base exception for Facebook Graph-Api exceptions. - * - * @author Kai Moritz - */ -public class GraphApiException extends OAuth2Exception -{ - public enum Type { OAuthException, GraphMethodException } - - - final static Logger LOG = LoggerFactory.getLogger(GraphApiException.class); - final static ObjectMapper OBJECT_MAPPER; - - private final FacebookErrorMessage error; - - - static - { - OBJECT_MAPPER = new ObjectMapper(); - OBJECT_MAPPER.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); - OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_FLOAT_AS_INT, false); - OBJECT_MAPPER.configure(SerializationFeature.WRAP_ROOT_VALUE, true); - } - - - public static GraphApiException create(InputStream in) - throws - IOException, - JsonParseException, - JsonMappingException - { - return create(OBJECT_MAPPER.readValue(in, FacebookErrorMessage.class)); - } - - public static GraphApiException create(byte[] message) - throws - IOException, - JsonParseException, - JsonMappingException - { - return create(OBJECT_MAPPER.readValue(message, FacebookErrorMessage.class)); - } - - public static GraphApiException create(FacebookErrorMessage error) - { - // see: http://fbdevwiki.com/wiki/Error_codes - switch(error.code) - { - // 1..99: general errors - case 1: return new UnknownErrorException(error); - case 2: return new UnexpectedErrorException(error); - case 21: return new PageMigratedException(error); - // 100..199: graph method errors - case 100: return new UnsupportedGetRequestException(error); - case 102: return new UserAccessTokenRequiredException(error); - case 104: return new AccessTokenRequiredException(error); - // 200..299: permission errors - // 300..399: data editing errors - // 400..449: authentication error - // 450..499: session errors - // 500..599: application messaging errors - // 600..699: FQL errors - case 613: return new RateExceededException(error); - // 700..749: ref errors - // 750..799: application integration errors - // 900..949: application information errors - // 950..999: batch api errors - // 1000..1099: event api errors - // 1100..1199: live-message errors - case 2200: return new CallbackVerificationFailedException(error); - - default: - LOG.info("unmapped error: {}", error); - return new UnmappedErrorException(error); - } - } - - - protected GraphApiException(FacebookErrorMessage error) - { - super(error.message); - this.error = error; - } - - - public Type getType() - { - return error.type == null ? null : Type.valueOf(error.type); - } - - public Integer getCode() - { - return error.code; - } - - public Integer getSubCode() - { - return error.subCode; - } - - public String getUserTitle() - { - return error.userTitle; - } - - public String getUserMessage() - { - return error.userMessage; - } - - public String getTraceId() - { - return error.traceId; - } - - - @Override - public String toString() - { - try - { - return OBJECT_MAPPER.writeValueAsString(error); - } - catch(JsonProcessingException e) - { - // This should never happen. But in case of a mistake: be verbose! - LOG.error("could not convert message into JSON: {}", e); - return e.getMessage(); - } - } -} diff --git a/src/main/java/de/juplo/facebook/errors/GraphMethodException.java b/src/main/java/de/juplo/facebook/errors/GraphMethodException.java deleted file mode 100644 index afe83cb..0000000 --- a/src/main/java/de/juplo/facebook/errors/GraphMethodException.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.juplo.facebook.errors; - - - -/** - * Baseclass for exceptions of type {@code GraphMethodException}. - * @author Kai Moritz - */ -public abstract class GraphMethodException extends GraphApiException -{ - GraphMethodException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/OAuthException.java b/src/main/java/de/juplo/facebook/errors/OAuthException.java deleted file mode 100644 index 4153dbc..0000000 --- a/src/main/java/de/juplo/facebook/errors/OAuthException.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.juplo.facebook.errors; - - - -/** - * Baseclass for exceptions of type {@code OAuthException}. - * @author Kai Moritz - */ -public class OAuthException extends GraphApiException -{ - protected OAuthException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/PageMigratedException.java b/src/main/java/de/juplo/facebook/errors/PageMigratedException.java deleted file mode 100644 index c339d83..0000000 --- a/src/main/java/de/juplo/facebook/errors/PageMigratedException.java +++ /dev/null @@ -1,45 +0,0 @@ -package de.juplo.facebook.errors; - - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - - -/** - * 21: Page ID (XXX) was migrated to page ID (YYY). - * @author Kai Moritz - */ -public class PageMigratedException extends OAuthException -{ - private final static Pattern pattern = - Pattern.compile("Page ID ([0-9]+) was migrated to page ID ([0-9]+)"); - - private final Long oldId, newId; - - - protected PageMigratedException(FacebookErrorMessage error) - { - super(error); - Matcher matcher = pattern.matcher(error.message); - if (!matcher.find()) - { - String warning = "Could not parse migration-error: " + error.message; - LOG.error(warning); - throw new RuntimeException(warning); - } - oldId = Long.parseLong(matcher.group(1)); - newId = Long.parseLong(matcher.group(2)); - } - - - public Long getOldId() - { - return oldId; - } - - public Long getNewId() - { - return newId; - } -} diff --git a/src/main/java/de/juplo/facebook/errors/RateExceededException.java b/src/main/java/de/juplo/facebook/errors/RateExceededException.java deleted file mode 100644 index e57cc45..0000000 --- a/src/main/java/de/juplo/facebook/errors/RateExceededException.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.juplo.facebook.errors; - - - -/** - * 613: Calls to stream have exceeded the rate of 600 calls per 600 seconds. - * @author Kai Moritz - */ -public class RateExceededException extends OAuthException -{ - protected RateExceededException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/UnexpectedErrorException.java b/src/main/java/de/juplo/facebook/errors/UnexpectedErrorException.java deleted file mode 100644 index 72c071b..0000000 --- a/src/main/java/de/juplo/facebook/errors/UnexpectedErrorException.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.juplo.facebook.errors; - - - -/** - * 2: An unexpected error has occurred. - * @author Kai Moritz - */ -public class UnexpectedErrorException extends OAuthException -{ - protected UnexpectedErrorException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/UnknownErrorException.java b/src/main/java/de/juplo/facebook/errors/UnknownErrorException.java deleted file mode 100644 index 7f5e8ff..0000000 --- a/src/main/java/de/juplo/facebook/errors/UnknownErrorException.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.juplo.facebook.errors; - - - -/** - * 1: An unknown error has occurred. - * @author Kai Moritz - */ -public class UnknownErrorException extends OAuthException -{ - protected UnknownErrorException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/UnmappedErrorException.java b/src/main/java/de/juplo/facebook/errors/UnmappedErrorException.java deleted file mode 100644 index 839de09..0000000 --- a/src/main/java/de/juplo/facebook/errors/UnmappedErrorException.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.juplo.facebook.errors; - - - -/** - * Marker class for error-messages, that are not mapped yet. - * Pleas help us to complete the list of possible errors and report errors, - * that are mapped to this class to info@juplo.de. Thanxs! - * - * @author Kai Moritz - */ -public class UnmappedErrorException extends GraphApiException -{ - protected UnmappedErrorException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/UnsupportedGetRequestException.java b/src/main/java/de/juplo/facebook/errors/UnsupportedGetRequestException.java deleted file mode 100644 index 4a5a9b8..0000000 --- a/src/main/java/de/juplo/facebook/errors/UnsupportedGetRequestException.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.juplo.facebook.errors; - - - -/** - * 100: Unsupported get request. - * @author Kai Moritz - */ -public class UnsupportedGetRequestException extends GraphMethodException -{ - protected UnsupportedGetRequestException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/main/java/de/juplo/facebook/errors/UserAccessTokenRequiredException.java b/src/main/java/de/juplo/facebook/errors/UserAccessTokenRequiredException.java deleted file mode 100644 index cb91950..0000000 --- a/src/main/java/de/juplo/facebook/errors/UserAccessTokenRequiredException.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.juplo.facebook.errors; - - - -/** - * A user access token is required to request this resource. - * @author Kai Moritz - */ -public class UserAccessTokenRequiredException extends OAuthException -{ - public UserAccessTokenRequiredException(FacebookErrorMessage error) - { - super(error); - } -} diff --git a/src/test/java/de/juplo/facebook/errors/FacebookErrorMessageMappingTest.java b/src/test/java/de/juplo/facebook/errors/FacebookErrorMessageMappingTest.java deleted file mode 100644 index 08fb372..0000000 --- a/src/test/java/de/juplo/facebook/errors/FacebookErrorMessageMappingTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.juplo.facebook.errors; - - -import com.fasterxml.jackson.core.JsonProcessingException; -import static de.juplo.facebook.errors.GraphApiException.OBJECT_MAPPER; -import de.juplo.facebook.errors.GraphApiException.Type; -import java.io.IOException; -import org.junit.Test; -import static org.junit.Assert.assertEquals; - - - -/** - * @author Kai Moritz - */ -public class FacebookErrorMessageMappingTest -{ - final String example = - "{" + - "\"error\":{" + - "\"message\":\"Message describing the error\"," + - "\"type\":\"OAuthException\"," + - "\"code\":190," + - "\"error_subcode\":460," + - "\"error_user_title\":\"A title\"," + - "\"error_user_msg\":\"A message\"," + - "\"fbtrace_id\":\"EJplcsCHuLu\"" + - "}" + - "}"; - - - @Test - public void testSerialize() throws JsonProcessingException - { - FacebookErrorMessage error = new FacebookErrorMessage(); - error.message = "Message describing the error"; - error.type = Type.OAuthException.name(); - error.code = 190; - error.subCode = 460; - error.userTitle = "A title"; - error.userMessage = "A message"; - error.traceId = "EJplcsCHuLu"; - - assertEquals(example, OBJECT_MAPPER.writeValueAsString(error)); - } - - @Test - public void testDeserialize() throws IOException - { - FacebookErrorMessage error = - OBJECT_MAPPER.readValue(example, FacebookErrorMessage.class); - - assertEquals("Message describing the error", error.message); - assertEquals(Type.OAuthException.name(), error.type); - assertEquals(new Integer(190), error.code); - assertEquals(new Integer(460), error.subCode); - assertEquals("A title", error.userTitle); - assertEquals("A message", error.userMessage); - assertEquals("EJplcsCHuLu", error.traceId); - } -} diff --git a/src/test/java/de/juplo/facebook/errors/GraphApiErrorHandlerTest.java b/src/test/java/de/juplo/facebook/errors/GraphApiErrorHandlerTest.java deleted file mode 100644 index 4d1e636..0000000 --- a/src/test/java/de/juplo/facebook/errors/GraphApiErrorHandlerTest.java +++ /dev/null @@ -1,825 +0,0 @@ -package de.juplo.facebook.errors; - - -import de.juplo.facebook.errors.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 testError102() - { - log.info("testError102"); - - requestFactory.setBody("{\"error\":{\"message\":\"A user access token is required to request this resource.\",\"type\":\"OAuthException\",\"code\":102,\"fbtrace_id\":\"DhdMyf23Ki7\"}}"); - - try - { - clientTemplate.getForObject("ANY", SOME.class); - fail("The expected exception was not thrown"); - } - catch(UserAccessTokenRequiredException e) - { - log.debug("{}", e.toString()); - assertEquals("invalid_request", e.getOAuth2ErrorCode()); - assertEquals(new Integer(102), e.getCode()); - assertEquals("A user access token is required to request this resource.", e.getMessage()); - assertEquals(Type.OAuthException, e.getType()); - assertEquals("DhdMyf23Ki7", e.getTraceId()); - } - } - - @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/errors/MockClientHttpRequestFactory.java b/src/test/java/de/juplo/facebook/errors/MockClientHttpRequestFactory.java deleted file mode 100644 index d5b24c3..0000000 --- a/src/test/java/de/juplo/facebook/errors/MockClientHttpRequestFactory.java +++ /dev/null @@ -1,145 +0,0 @@ -package de.juplo.facebook.errors; - - -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; - } - } -}