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