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