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