Reworked handling of error-responses from the Graph-API
authorKai Moritz <kai@juplo.de>
Sat, 7 Nov 2015 13:46:44 +0000 (14:46 +0100)
committerKai Moritz <kai@juplo.de>
Tue, 10 Nov 2015 14:51:14 +0000 (15:51 +0100)
 * The parsing of the response is done by the ObjectMapper from Jackson.
 * Unmapped exceptions are returned as such, instead of handing the handling
   to the parent.

14 files changed:
src/main/java/de/juplo/facebook/client/GraphApiErrorHandler.java
src/main/java/de/juplo/facebook/exceptions/GraphApiException.java
src/main/java/de/juplo/facebook/exceptions/GraphApiExceptionJackson1Deserializer.java [deleted file]
src/main/java/de/juplo/facebook/exceptions/GraphApiExceptionJackson2Deserializer.java [deleted file]
src/main/java/de/juplo/facebook/exceptions/GraphMethodException.java
src/main/java/de/juplo/facebook/exceptions/OAuthException.java
src/main/java/de/juplo/facebook/exceptions/PageMigratedException.java
src/main/java/de/juplo/facebook/exceptions/RateExceededException.java
src/main/java/de/juplo/facebook/exceptions/UnexpectedErrorException.java
src/main/java/de/juplo/facebook/exceptions/UnknownErrorException.java
src/main/java/de/juplo/facebook/exceptions/UnmappedErrorException.java [new file with mode: 0644]
src/main/java/de/juplo/facebook/exceptions/UnsupportedGetRequestException.java
src/test/java/de/juplo/facebook/client/GraphApiErrorHandlerTest.java
src/test/java/de/juplo/facebook/exceptions/FacebookErrorMessageMappingTest.java

index faff55e..2b73e49 100644 (file)
@@ -4,56 +4,48 @@ import de.juplo.facebook.exceptions.GraphApiException;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.List;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.client.ClientHttpResponse;
-import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.HttpMessageNotReadableException;
-import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler;
 import org.springframework.util.FileCopyUtils;
-import org.springframework.web.client.HttpMessageConverterExtractor;
-import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestTemplate;
+import org.springframework.web.client.ResponseErrorHandler;
 
 
 
 /**
+ * Error-Handler for error-messages from the Facebook Graph-API.
+ * <p>
+ * This error-handler handels responses withe the HTTP-status code
+ * {@code 4xx}. 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.
+ * <p>
+ * If the HTTP-status-code of the response is not {@code 4xx} or
+ * the HTTP-body could not be extracted or parsed, this error-handler
+ * delegates the handling to its parent.
  *
- * @author kai
+ * @see <a href="https://developers.facebook.com/docs/graph-api/using-graph-api/v2.5#errors">Graph-API Documentation</a>
+ * @see <a href="http://fbdevwiki.com/wiki/Error_codes">Inofficial Wiki For Facebook-Developers</a>
+ * @author Kai Moritz
  */
-public class GraphApiErrorHandler extends OAuth2ErrorHandler
+public class GraphApiErrorHandler implements ResponseErrorHandler
 {
-  private final OAuth2ErrorHandler errorHandler;
-  private List<HttpMessageConverter<?>> messageConverters =
-      new RestTemplate().getMessageConverters();
+  private final ResponseErrorHandler parent;
 
 
-  public GraphApiErrorHandler(OAuth2ErrorHandler errorHandler)
+  public GraphApiErrorHandler(ResponseErrorHandler errorHandler)
   {
-    super(null);
-    this.errorHandler = errorHandler;
+    this.parent = errorHandler;
   }
 
 
-       /**
-        * @param messageConverters the messageConverters to set
-        */
-  @Override
-  public void setMessageConverters(
-      List<HttpMessageConverter<?>> messageConverters
-      )
-  {
-    this.messageConverters = messageConverters;
-    errorHandler.setMessageConverters(messageConverters);
-  }
-
   @Override
   public boolean hasError(ClientHttpResponse response) throws IOException
   {
     return
         HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series())
-        || this.errorHandler.hasError(response);
+        || this.parent.hasError(response);
   }
 
   @Override
@@ -61,17 +53,36 @@ public class GraphApiErrorHandler extends OAuth2ErrorHandler
   {
     if (!HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series()))
     {
-      // We should only care about 400 level errors. Ex: A 500 server error shouldn't
-      // be an oauth related error.
-      errorHandler.handleError(response);
+      // Let the parent-error-handler handle all errors, that are no client
+      // errors (4xx).
+      parent.handleError(response);
+      return;
     }
-    else
+
+
+    if (response.getBody() == null)
     {
-      // Need to use buffered response because input stream may need to be consumed multiple times.
-      ClientHttpResponse bufferedResponse = new ClientHttpResponse()
-      {
-        private byte[] lazyBody;
+      // There is no body to interpret in the HTTP-message
+      parent.handleError(response);
+      return;
+    }
+
+    final byte[] body = FileCopyUtils.copyToByteArray(response.getBody());
+    GraphApiException error;
+
+    try
+    {
+      error = GraphApiException.create(body);
+    }
+    catch (Exception e)
+    {
+      // The body of the HTTP-message could not be parsed.
+      // Let the parent error-handler try to handle the response.
 
+      // 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
         {
@@ -81,16 +92,7 @@ public class GraphApiErrorHandler extends OAuth2ErrorHandler
         @Override
         public synchronized InputStream getBody() throws IOException
         {
-          if (lazyBody == null) {
-            InputStream bodyStream = response.getBody();
-            if (bodyStream != null) {
-              lazyBody = FileCopyUtils.copyToByteArray(bodyStream);
-            }
-            else {
-              lazyBody = new byte[0];
-            }
-          }
-          return new ByteArrayInputStream(lazyBody);
+          return new ByteArrayInputStream(body);
         }
 
         @Override
@@ -118,31 +120,10 @@ public class GraphApiErrorHandler extends OAuth2ErrorHandler
         }
       };
 
-
-      HttpMessageConverterExtractor<GraphApiException> extractor =
-          new HttpMessageConverterExtractor<>(
-              GraphApiException.class,
-              messageConverters
-              );
-
-      try
-      {
-        GraphApiException body = extractor.extractData(bufferedResponse);
-        if (body != null)
-        {
-          // If we can get an OAuth2Exception already from the body, it is likely
-          // to have more information than the header does, so just re-throw it
-          // here.
-          body.setHttpErrorCode(response.getRawStatusCode());
-          throw body;
-        }
-      }
-      catch (RestClientException|HttpMessageNotReadableException e)
-      {
-        // ignore
-      }
-
-      errorHandler.handleError(bufferedResponse);
+      parent.handleError(buffered);
+      return;
     }
+
+    throw error;
   }
 }
index 1055b20..bd02565 100644 (file)
@@ -3,6 +3,14 @@ package de.juplo.facebook.exceptions;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import com.fasterxml.jackson.annotation.JsonRootName;
+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.springframework.security.oauth2.common.exceptions.OAuth2Exception;
 
 /**
@@ -10,64 +18,126 @@ import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
  * 
  * @author Kai Moritz
  */
-@org.codehaus.jackson.map.annotate.JsonDeserialize(using = GraphApiExceptionJackson1Deserializer.class)
-@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = GraphApiExceptionJackson2Deserializer.class)
 public class GraphApiException extends OAuth2Exception
 {
-  private final String type;
-  private final int code;
+  final static ObjectMapper OBJECT_MAPPER;
 
-  private int httpErrorCode;
+  private final FacebookErrorMessage error;
 
 
-  public GraphApiException(String message, String type, int code)
+  static
   {
-    super(message);
-    this.type = type;
-    this.code = code;
+    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);
+      // 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
+
+      default:    return new UnmappedErrorException(error);
+    }
+  }
+
+
+  protected GraphApiException(FacebookErrorMessage error)
+  {
+    super(error.message);
+    this.error = error;
   }
 
 
   public String getType()
   {
-    return type;
+    return error.type;
   }
 
-  public int getCode()
+  public Integer getCode()
   {
-    return code;
+    return error.code;
   }
 
-  @Override
-  public int getHttpErrorCode()
+  public Integer getSubCode()
   {
-    return httpErrorCode == 0 ? super.getHttpErrorCode() : httpErrorCode;
+    return error.subCode;
   }
 
-  public void setHttpErrorCode(int httpErrorCode)
+  public String getUserTitle()
   {
-    this.httpErrorCode = httpErrorCode;
+    return error.userTitle;
   }
 
+  public String getUserMessage()
+  {
+    return error.userMessage;
+  }
+
+  public String getTraceId()
+  {
+    return error.traceId;
+  }
+
+
   @Override
   public String toString()
   {
-    StringBuilder builder = new StringBuilder();
-    builder.append("{error:{\"message\":\"");
-    builder.append(getMessage().replaceAll("\"", "\\\""));
-    builder.append("\",\"type\":");
-    builder.append(type.replaceAll("\"", "\\\""));
-    builder.append("\",\"code\":");
-    builder.append(code);
-    builder.append("}}");
-    return builder.toString();
+    try
+    {
+      return OBJECT_MAPPER.writeValueAsString(error);
+    }
+    catch(JsonProcessingException e)
+    {
+      return "Could not convert error in JSON-representation: " + e.getMessage();
+    }
   }
 
 
   /**
    * This class represents an error message from the Graph-API
    *
-   * @see https://developers.facebook.com/docs/graph-api/using-graph-api/v2.5#errors
+   * @see <a href="https://developers.facebook.com/docs/graph-api/using-graph-api/v2.5#errors">Graph-API Documentation</a>
    */
   @JsonRootName("error")
   @JsonPropertyOrder({ "message", "type", "code", "error_subcode", "error_user_title", "error_user_msg", "fbtrace_id" })
diff --git a/src/main/java/de/juplo/facebook/exceptions/GraphApiExceptionJackson1Deserializer.java b/src/main/java/de/juplo/facebook/exceptions/GraphApiExceptionJackson1Deserializer.java
deleted file mode 100644 (file)
index 165e7ce..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-package de.juplo.facebook.exceptions;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import org.codehaus.jackson.JsonParser;
-import org.codehaus.jackson.JsonProcessingException;
-import org.codehaus.jackson.JsonToken;
-import org.codehaus.jackson.map.DeserializationContext;
-import org.codehaus.jackson.map.JsonDeserializer;
-
-/**
- * @author Kai Moritz
- */
-public class GraphApiExceptionJackson1Deserializer
-    extends
-      JsonDeserializer<GraphApiException>
-{
-
-  @Override
-  public GraphApiException deserialize(
-      JsonParser jp,
-      DeserializationContext ctxt
-      )
-      throws
-        IOException,
-        JsonProcessingException
-  {
-    JsonToken t = jp.getCurrentToken();
-    if (t != JsonToken.START_OBJECT)
-      return null;
-
-    t = jp.nextToken();
-    if (t != JsonToken.FIELD_NAME)
-      return null;
-
-    if (!jp.getCurrentName().equals("error"))
-      return null;
-
-    t = jp.nextToken();
-    if (t != JsonToken.START_OBJECT)
-      return null;
-  
-    String message = null, type = null;
-    Integer code = null;
-
-    t = jp.nextToken();
-    Map<String, String> map = new HashMap<>();
-    for (; t == JsonToken.FIELD_NAME; t = jp.nextToken())
-    {
-      // Must point to field name
-      String fieldName = jp.getCurrentName();
-      // And then the value...
-      t = jp.nextToken();
-
-      switch (t)
-      {
-        case VALUE_STRING:
-          switch(fieldName.toLowerCase())
-          {
-            case "message":
-              message = jp.getText();
-              break;
-            case "type":
-              type = jp.getText();
-              break;
-            default:
-              return null;
-          }
-          break;
-        case VALUE_NUMBER_INT:
-          if (!fieldName.equalsIgnoreCase("code"))
-            return null;
-          code = jp.getValueAsInt();
-          break;
-        default:
-          return null;
-      }
-    }
-
-    if (message == null || type == null || code == null)
-      return null;
-
-    switch (code)
-    {
-      case 1:   return new UnknownErrorException();
-      case 2:   return new UnexpectedErrorException();
-      case 21:  return new PageMigratedException(message);
-      case 100: return new UnsupportedGetRequestException();
-      case 613: return new RateExceededException();
-      default:  return new GraphApiException(message, type, code);
-    }
-  }
-}
diff --git a/src/main/java/de/juplo/facebook/exceptions/GraphApiExceptionJackson2Deserializer.java b/src/main/java/de/juplo/facebook/exceptions/GraphApiExceptionJackson2Deserializer.java
deleted file mode 100644 (file)
index ea0e987..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-package de.juplo.facebook.exceptions;
-
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-
-/**
- * @author Kai Moritz
- *
- */
-public class GraphApiExceptionJackson2Deserializer
-    extends
-      StdDeserializer<GraphApiException>
-{
-  public GraphApiExceptionJackson2Deserializer()
-  {
-    super(GraphApiException.class);
-  }
-
-  @Override
-  public GraphApiException deserialize(
-      JsonParser jp,
-      DeserializationContext ctxt
-      )
-      throws
-        IOException,
-        JsonProcessingException
-  {
-    JsonToken t = jp.getCurrentToken();
-    if (t != JsonToken.START_OBJECT)
-      return null;
-
-    t = jp.nextToken();
-    if (t != JsonToken.FIELD_NAME)
-      return null;
-
-    if (!jp.getCurrentName().equals("error"))
-      return null;
-
-    t = jp.nextToken();
-    if (t != JsonToken.START_OBJECT)
-      return null;
-  
-    String message = null, type = null;
-    Integer code = null;
-
-    t = jp.nextToken();
-    Map<String, String> map = new HashMap<>();
-    for (; t == JsonToken.FIELD_NAME; t = jp.nextToken())
-    {
-      // Must point to field name
-      String fieldName = jp.getCurrentName();
-      // And then the value...
-      t = jp.nextToken();
-
-      switch (t)
-      {
-        case VALUE_STRING:
-          switch(fieldName.toLowerCase())
-          {
-            case "message":
-              message = jp.getText();
-              break;
-            case "type":
-              type = jp.getText();
-              break;
-            default:
-              return null;
-          }
-          break;
-        case VALUE_NUMBER_INT:
-          if (!fieldName.equalsIgnoreCase("code"))
-            return null;
-          code = jp.getValueAsInt();
-          break;
-        default:
-          return null;
-      }
-    }
-
-    if (message == null || type == null || code == null)
-      return null;
-
-    switch (code)
-    {
-      case 1:   return new UnknownErrorException();
-      case 2:   return new UnexpectedErrorException();
-      case 21:  return new PageMigratedException(message);
-      case 100: return new UnsupportedGetRequestException();
-      case 613: return new RateExceededException();
-      default:  return new GraphApiException(message, type, code);
-    }
-  }
-}
index 21c56c5..7a8afc5 100644 (file)
@@ -2,13 +2,13 @@ package de.juplo.facebook.exceptions;
 
 
 /**
- *
- * @author kai
+ * Baseclass for exceptions of type {@code GraphMethodException}.
+ * @author Kai Moritz
  */
 public abstract class GraphMethodException extends GraphApiException
 {
-  public GraphMethodException(String message, int code)
+  GraphMethodException(FacebookErrorMessage error)
   {
-    super(message, "GraphMethodException", code);
+    super(error);
   }
 }
index 4537720..e8fd312 100644 (file)
@@ -1,14 +1,18 @@
 package de.juplo.facebook.exceptions;
 
 
+import de.juplo.facebook.exceptions.GraphApiException.FacebookErrorMessage;
+
+
+
 /**
- *
- * @author kai
+ * Baseclass for exceptions of type {@code OAuthException}.
+ * @author Kai Moritz
  */
-public abstract class OAuthException extends GraphApiException
+public class OAuthException extends GraphApiException
 {
-  public OAuthException(String message, int code)
+  protected OAuthException(FacebookErrorMessage error)
   {
-    super(message, "OAuthException", code);
+    super(error);
   }
 }
index d327b6d..dfa76cf 100644 (file)
@@ -7,8 +7,8 @@ import java.util.regex.Pattern;
 
 
 /**
- *
- * @author kai
+ * 21: Page ID (XXX) was migrated to page ID (YYY).
+ * @author Kai Moritz
  */
 public class PageMigratedException extends OAuthException
 {
@@ -18,12 +18,12 @@ public class PageMigratedException extends OAuthException
   private final Long oldId, newId;
 
 
-  public PageMigratedException(String message)
+  protected PageMigratedException(FacebookErrorMessage error)
   {
-    super(message, 21);
-    Matcher matcher = pattern.matcher(message);
+    super(error);
+    Matcher matcher = pattern.matcher(error.message);
     if (!matcher.find())
-      throw new RuntimeException("Could not parse migration-error: " + message);
+      throw new RuntimeException("Could not parse migration-error: " + error.message);
     oldId = Long.parseLong(matcher.group(1));
     newId = Long.parseLong(matcher.group(2));
   }
index cdda586..adc0b4a 100644 (file)
@@ -2,13 +2,13 @@ package de.juplo.facebook.exceptions;
 
 
 /**
- *
- * @author kai
+ * 613: Calls to stream have exceeded the rate of 600 calls per 600 seconds.
+ * @author Kai Moritz
  */
 public class RateExceededException extends OAuthException
 {
-  public RateExceededException()
+  protected RateExceededException(FacebookErrorMessage error)
   {
-    super("(#613) Calls to stream have exceeded the rate of 600 calls per 600 seconds.", 613);
+    super(error);
   }
 }
index 5aefff6..6d7369b 100644 (file)
@@ -1,14 +1,18 @@
 package de.juplo.facebook.exceptions;
 
 
+import de.juplo.facebook.exceptions.GraphApiException.FacebookErrorMessage;
+
+
+
 /**
- *
- * @author kai
+ * 2: An unexpected error has occurred.
+ * @author Kai Moritz
  */
 public class UnexpectedErrorException extends OAuthException
 {
-  public UnexpectedErrorException()
+  protected UnexpectedErrorException(FacebookErrorMessage error)
   {
-    super("An unexpected error has occurred. Please retry your request later.", 2);
+    super(error);
   }
 }
index 6e336f4..c281f09 100644 (file)
@@ -1,14 +1,18 @@
 package de.juplo.facebook.exceptions;
 
 
+import de.juplo.facebook.exceptions.GraphApiException.FacebookErrorMessage;
+
+
+
 /**
- *
- * @author kai
+ * 1: An unknown error has occurred.
+ * @author Kai Moritz
  */
 public class UnknownErrorException extends OAuthException
 {
-  public UnknownErrorException()
+  protected UnknownErrorException(FacebookErrorMessage error)
   {
-    super("An unknown error has occurred.", 1);
+    super(error);
   }
 }
diff --git a/src/main/java/de/juplo/facebook/exceptions/UnmappedErrorException.java b/src/main/java/de/juplo/facebook/exceptions/UnmappedErrorException.java
new file mode 100644 (file)
index 0000000..2ed9b95
--- /dev/null
@@ -0,0 +1,17 @@
+package de.juplo.facebook.exceptions;
+
+
+/**
+ * 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);
+  }
+}
index 1d5b44d..6905808 100644 (file)
@@ -2,13 +2,13 @@ package de.juplo.facebook.exceptions;
 
 
 /**
- *
- * @author kai
+ * 100: Unsupported get request.
+ * @author Kai Moritz
  */
 public class UnsupportedGetRequestException extends GraphMethodException
 {
-  public UnsupportedGetRequestException()
+  protected UnsupportedGetRequestException(FacebookErrorMessage error)
   {
-    super("Unsupported get request.", 100);
+    super(error);
   }
 }
index ddd19a6..15dfba3 100644 (file)
@@ -6,6 +6,7 @@ import de.juplo.facebook.exceptions.RateExceededException;
 import de.juplo.facebook.exceptions.GraphApiException;
 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;
@@ -79,7 +80,7 @@ public class GraphApiErrorHandlerTest
     {
       log.debug("{}", e.toString());
       assertEquals("invalid_request", e.getOAuth2ErrorCode());
-      assertEquals(1, e.getCode());
+      assertEquals(new Integer(1), e.getCode());
       assertEquals("An unknown error has occurred.", e.getMessage());
       assertEquals("OAuthException", e.getType());
     }
@@ -110,7 +111,7 @@ public class GraphApiErrorHandlerTest
     {
       log.debug("{}", e.toString());
       assertEquals("invalid_request", e.getOAuth2ErrorCode());
-      assertEquals(2, e.getCode());
+      assertEquals(new Integer(2), e.getCode());
       assertEquals("An unexpected error has occurred. Please retry your request later.", e.getMessage());
       assertEquals("OAuthException", e.getType());
     }
@@ -141,7 +142,7 @@ public class GraphApiErrorHandlerTest
     {
       log.debug("{}", e.toString());
       assertEquals("invalid_request", e.getOAuth2ErrorCode());
-      assertEquals(21, e.getCode());
+      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("OAuthException", e.getType());
     }
@@ -172,7 +173,7 @@ public class GraphApiErrorHandlerTest
     {
       log.debug("{}", e.toString());
       assertEquals("invalid_request", e.getOAuth2ErrorCode());
-      assertEquals(100, e.getCode());
+      assertEquals(new Integer(100), e.getCode());
       assertEquals("Unsupported get request.", e.getMessage());
       assertEquals("GraphMethodException", e.getType());
     }
@@ -203,7 +204,7 @@ public class GraphApiErrorHandlerTest
     {
       log.debug("{}", e.toString());
       assertEquals("invalid_request", e.getOAuth2ErrorCode());
-      assertEquals(613, e.getCode());
+      assertEquals(new Integer(613), e.getCode());
       assertEquals("(#613) Calls to stream have exceeded the rate of 600 calls per 600 seconds.", e.getMessage());
       assertEquals("OAuthException", e.getType());
     }
@@ -234,16 +235,16 @@ public class GraphApiErrorHandlerTest
     {
       log.debug("{}", e.toString());
       assertEquals("invalid_request", e.getOAuth2ErrorCode());
-      assertEquals(999999999, e.getCode());
+      assertEquals(new Integer(999999999), e.getCode());
       assertEquals("This error does not exist.", e.getMessage());
       assertEquals("NonexistentException", e.getType());
     }
   }
 
   @Test
-  public void testInvlalidErrors()
+  public void testUnmappedErrors()
   {
-    log.info("testInvalidErrors");
+    log.info("testUnmappedErrors");
 
 
     requestFactory.setBody(
@@ -261,9 +262,16 @@ public class GraphApiErrorHandlerTest
       clientTemplate.getForObject("ANY", SOME.class);
       fail("The expected exception was not thrown");
     }
-    catch(HttpClientErrorException e)
+    catch(UnmappedErrorException e)
     {
       log.debug("{}", e.toString());
+      assertNull(e.getMessage());
+      assertEquals("Whatever", e.getType());
+      assertEquals(new Integer(999999999), e.getCode());
+      assertNull(e.getSubCode());
+      assertNull(e.getUserTitle());
+      assertNull(e.getUserMessage());
+      assertNull(e.getTraceId());
     }
     catch(Exception e)
     {
@@ -285,9 +293,16 @@ public class GraphApiErrorHandlerTest
       clientTemplate.getForObject("ANY", SOME.class);
       fail("The expected exception was not thrown");
     }
-    catch(HttpClientErrorException e)
+    catch(UnmappedErrorException e)
     {
       log.debug("{}", e.toString());
+      assertNull(e.getMessage());
+      assertEquals("Whatever", e.getType());
+      assertEquals(new Integer(999999999), e.getCode());
+      assertNull(e.getSubCode());
+      assertNull(e.getUserTitle());
+      assertNull(e.getUserMessage());
+      assertNull(e.getTraceId());
     }
     catch(Exception e)
     {
@@ -299,7 +314,7 @@ public class GraphApiErrorHandlerTest
         "{\n" +
         "  \"error\":\n" +
         "  {\n" +
-        "    \"message\": \"Not a Graph-Api-Exception.\",\n" +
+        "    \"message\": \"An unmapped Graph-API-Exception.\",\n" +
         "    \"type\": null,\n" +
         "    \"code\": 999999999\n" +
         "  }\n" +
@@ -310,9 +325,16 @@ public class GraphApiErrorHandlerTest
       clientTemplate.getForObject("ANY", SOME.class);
       fail("The expected exception was not thrown");
     }
-    catch(HttpClientErrorException e)
+    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)
     {
@@ -324,7 +346,7 @@ public class GraphApiErrorHandlerTest
         "{\n" +
         "  \"error\":\n" +
         "  {\n" +
-        "    \"message\": \"Not a Graph-Api-Exception.\",\n" +
+        "    \"message\": \"An unmapped Graph-API-Exception.\",\n" +
         "    \"code\": 999999999\n" +
         "  }\n" +
         "}");
@@ -334,14 +356,27 @@ public class GraphApiErrorHandlerTest
       clientTemplate.getForObject("ANY", SOME.class);
       fail("The expected exception was not thrown");
     }
-    catch(HttpClientErrorException e)
+    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(
index cd1a4c8..cb25662 100644 (file)
@@ -1,12 +1,9 @@
 package de.juplo.facebook.exceptions;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
 import de.juplo.facebook.exceptions.GraphApiException.FacebookErrorMessage;
+import static de.juplo.facebook.exceptions.GraphApiException.OBJECT_MAPPER;
 import java.io.IOException;
-import org.junit.Before;
 import org.junit.Test;
 import static org.junit.Assert.assertEquals;
 
@@ -28,17 +25,6 @@ public class FacebookErrorMessageMappingTest
           "}" +
       "}";
 
-  ObjectMapper mapper;
-
-
-  @Before
-  public void setUp()
-  {
-    mapper = new ObjectMapper();
-    mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
-    mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
-  }
-
 
   @Test
   public void testSerialize() throws JsonProcessingException
@@ -52,14 +38,14 @@ public class FacebookErrorMessageMappingTest
     error.userMessage = "A message";
     error.traceId = "EJplcsCHuLu";
 
-    assertEquals(example, mapper.writeValueAsString(error));
+    assertEquals(example, OBJECT_MAPPER.writeValueAsString(error));
   }
 
   @Test
   public void testDeserialize() throws IOException
   {
     FacebookErrorMessage error =
-        mapper.readValue(example, FacebookErrorMessage.class);
+        OBJECT_MAPPER.readValue(example, FacebookErrorMessage.class);
 
     assertEquals("Message describing the error", error.message);
     assertEquals("OAuthException", error.type);