5ec30fac6f9e83d0ce4009627ae726c5fb872426
[facebook-errors] / src / main / java / de / juplo / facebook / errors / GraphApiException.java
1 package de.juplo.facebook.errors;
2
3
4 import com.fasterxml.jackson.core.JsonProcessingException;
5 import com.fasterxml.jackson.databind.DeserializationFeature;
6 import com.fasterxml.jackson.databind.ObjectMapper;
7 import com.fasterxml.jackson.databind.SerializationFeature;
8 import java.io.ByteArrayInputStream;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13 import org.springframework.core.io.buffer.DataBuffer;
14 import org.springframework.http.HttpHeaders;
15 import org.springframework.http.HttpStatus;
16 import org.springframework.http.ReactiveHttpInputMessage;
17 import org.springframework.web.reactive.function.BodyExtractor.Context;
18 import org.springframework.web.reactive.function.client.ClientResponse;
19
20
21
22 /**
23  * Base exception for Facebook Graph-Api exceptions.
24  * 
25  * @author Kai Moritz
26  */
27 public class GraphApiException extends RuntimeException
28 {
29   public enum Type { OAuthException, GraphMethodException }
30
31
32   final static Logger LOG = LoggerFactory.getLogger(GraphApiException.class);
33   final static ObjectMapper OBJECT_MAPPER;
34
35   public final HttpStatus status;
36   public final HttpHeaders headers;
37   public final FacebookErrorMessage error;
38
39
40   static
41   {
42     OBJECT_MAPPER = new ObjectMapper();
43     OBJECT_MAPPER.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
44     OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_FLOAT_AS_INT, false);
45     OBJECT_MAPPER.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
46   }
47
48
49
50   public static GraphApiException create(ClientResponse response)
51   {
52     return
53         response.body((ReactiveHttpInputMessage message, Context context) ->
54         {
55           DataBuffer buffer = message.getBody().blockFirst();
56           InputStream is = message.getBody().blockFirst().asInputStream();
57           return create(response.statusCode(), message.getHeaders(), is);
58         });
59   }
60
61   public static GraphApiException create(
62       HttpStatus status,
63       HttpHeaders headers,
64       InputStream in
65       )
66   {
67     try
68     {
69       return create(status, headers, OBJECT_MAPPER.readValue(in, FacebookErrorMessage.class));
70     }
71     catch (IOException | RuntimeException e)
72     {
73       return new ErrorResponseParsingErrorException(status, headers, e);
74     }
75   }
76
77   public static GraphApiException create(
78       HttpStatus status,
79       HttpHeaders headers,
80       byte[] message
81       )
82   {
83     return create(status, headers, new ByteArrayInputStream(message));
84   }
85
86   public static GraphApiException create(
87       HttpStatus status,
88       HttpHeaders headers,
89       FacebookErrorMessage error
90       )
91   {
92     // see: http://fbdevwiki.com/wiki/Error_codes
93     switch(error.code)
94     {
95       // 1..99: general errors
96       case 1:     return new UnknownErrorException(status, headers, error);
97       case 2:     return new UnexpectedErrorException(status, headers, error);
98       case 4:     return new ApplicationRequestLimitReachedException(status, headers, error);
99       case 10:    return new AuthorizationMissingException(status, headers, error);
100       case 12:    return new DeprecatedException(status, headers, error);
101       case 17:    return new AccountRequestLimitReachedException(status, headers, error);
102       case 21:    return new PageMigratedException(status, headers, error);
103       case 32:    return new PageRequestLimitReachedException(status, headers, error);
104       // 100..199: graph method errors
105       case 100:   return new UnsupportedGetRequestException(status, headers, error);
106       case 102:   return new UserAccessTokenRequiredException(status, headers, error);
107       case 104:   return new AccessTokenRequiredException(status, headers, error);
108       case 190:   return new AccessTokenExpiredException(status, headers, error);
109       // 200..299: permission errors
110       case 200:   return new ApplicationNotAuthorizedByUserException(status, headers, error);
111       case 201:
112       case 202:
113       case 203:
114       case 204:
115       case 205:
116       case 206:
117       case 207:
118       case 208:
119       case 209:
120       case 210:
121       case 211:
122       case 212:
123       case 213:
124       case 214:
125       case 215:
126       case 216:
127       case 217:
128       case 218:
129       case 219:
130       case 220:
131       case 221:
132       case 222:
133       case 223:
134       case 224:
135       case 225:
136       case 226:
137       case 227:
138       case 228:
139       case 229:
140       case 230:
141       case 231:
142       case 232:
143       case 233:
144       case 234:
145       case 235:
146       case 236:
147       case 237:
148       case 238:
149       case 239:
150       case 240:
151       case 241:
152       case 242:
153       case 243:
154       case 244:
155       case 245:
156       case 246:
157       case 247:
158       case 248:
159       case 249:
160       case 250:
161       case 251:
162       case 252:
163       case 253:
164       case 254:
165       case 255:
166       case 256:
167       case 257:
168       case 258:
169       case 259:
170       case 260:
171       case 261:
172       case 262:
173       case 263:
174       case 264:
175       case 265:
176       case 266:
177       case 267:
178       case 268:
179       case 269:
180       case 270:
181       case 271:
182       case 272:
183       case 273:
184       case 274:
185       case 275:
186       case 276:
187       case 277:
188       case 278:
189       case 279:
190       case 280:
191       case 281:
192       case 282:
193       case 283:
194       case 284:
195       case 285:
196       case 286:
197       case 287:
198       case 288:
199       case 289:
200       case 290:
201       case 291:
202       case 292:
203       case 293:
204       case 294:
205       case 295:
206       case 296:
207       case 297:
208       case 298:
209       case 299:   return new AuthorizationMissingException(status, headers, error);
210       // 200..299: permission errors
211       // 300..399: data editing errors ?
212       case 341:   return new TemporaryRateLimitExceededException(status, headers, error);
213       // 400..449: authentication error
214       // 450..499: session errors
215       // 500..599: application messaging errors ?
216       case 506:   return new MultipleConcurrentPostsException(status, headers, error);
217       // 600..699: FQL errors
218       case 613:   return new RateLimitExceededException(status, headers, error);
219       // 700..749: ref errors
220       // 750..799: application integration errors
221       // 900..949: application information errors
222       // 950..999: batch api errors
223       // 1000..1099: event api errors
224       // 1100..1199: live-message errors
225       case 1609005: return new LinkPostFailureException(status, headers, error);
226       case 2200:  return new CallbackVerificationFailedException(status, headers, error);
227
228       default:
229         GraphApiException e = new UnmappedErrorException(status, headers, error);
230         LOG.info("unmapped error: {}", e.toString());
231         return e;
232     }
233   }
234
235
236   protected GraphApiException(
237       HttpStatus status,
238       HttpHeaders headers,
239       FacebookErrorMessage error
240       )
241   {
242     super(error.message);
243     this.status = status;
244     this.headers = headers;
245     this.error = error;
246   }
247
248
249   public HttpStatus getStatus()
250   {
251     return status;
252   }
253
254   public HttpHeaders getHeaders()
255   {
256     return headers;
257   }
258
259   public Type getType()
260   {
261     return error.type == null ? null : Type.valueOf(error.type);
262   }
263
264   public Integer getCode()
265   {
266     return error.code;
267   }
268
269   public Integer getSubCode()
270   {
271     return error.subCode;
272   }
273
274   public String getUserTitle()
275   {
276     return error.userTitle;
277   }
278
279   public String getUserMessage()
280   {
281     return error.userMessage;
282   }
283
284   public String getTraceId()
285   {
286     return error.traceId;
287   }
288
289
290   @Override
291   public String toString()
292   {
293     try
294     {
295       return OBJECT_MAPPER.writeValueAsString(error);
296     }
297     catch(JsonProcessingException e)
298     {
299       // This should never happen. But in case of a mistake: be verbose!
300       LOG.error("could not convert message into JSON: {}", e);
301       return e.getMessage();
302     }
303   }
304 }