1 package de.juplo.facebook.client;
2
3 import de.juplo.facebook.exceptions.AccessTokenRequiredException;
4 import de.juplo.facebook.exceptions.CallbackVerificationFailedException;
5 import de.juplo.facebook.exceptions.UnsupportedGetRequestException;
6 import de.juplo.facebook.exceptions.UnexpectedErrorException;
7 import de.juplo.facebook.exceptions.RateExceededException;
8 import de.juplo.facebook.exceptions.GraphApiException;
9 import de.juplo.facebook.exceptions.GraphApiException.Type;
10 import de.juplo.facebook.exceptions.UnknownErrorException;
11 import de.juplo.facebook.exceptions.PageMigratedException;
12 import de.juplo.facebook.exceptions.UnmappedErrorException;
13 import java.util.Date;
14 import java.util.Map;
15 import java.util.Set;
16 import javax.annotation.Resource;
17 import static org.junit.Assert.*;
18 import org.junit.Before;
19 import org.junit.Test;
20 import org.junit.runner.RunWith;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23 import org.springframework.http.HttpStatus;
24 import org.springframework.security.access.AccessDeniedException;
25 import org.springframework.security.oauth2.client.OAuth2RestTemplate;
26 import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler;
27 import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
28 import org.springframework.security.oauth2.client.resource.UserApprovalRequiredException;
29 import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
30 import org.springframework.security.oauth2.client.token.AccessTokenProvider;
31 import org.springframework.security.oauth2.client.token.AccessTokenRequest;
32 import org.springframework.security.oauth2.common.OAuth2AccessToken;
33 import static org.springframework.security.oauth2.common.OAuth2AccessToken.OAUTH2_TYPE;
34 import org.springframework.security.oauth2.common.OAuth2RefreshToken;
35 import org.springframework.test.context.ContextConfiguration;
36 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
37 import org.springframework.web.client.HttpClientErrorException;
38
39
40
41
42
43
44
45 @RunWith(SpringJUnit4ClassRunner.class)
46 @ContextConfiguration(
47 locations = {
48 "classpath:/spring/test-facebook-error-handler.xml"
49 })
50 public class GraphApiErrorHandlerTest
51 {
52 private static final Logger log =
53 LoggerFactory.getLogger(GraphApiErrorHandlerTest.class);
54
55 @Resource
56 private OAuth2RestTemplate clientTemplate;
57
58 private MockClientHttpRequestFactory requestFactory;
59
60
61 @Test
62 public void testError1()
63 {
64 log.info("testError1");
65
66
67 requestFactory.setBody(
68 "{\n" +
69 " \"error\":\n" +
70 " {\n" +
71 " \"message\": \"An unknown error has occurred.\",\n" +
72 " \"type\": \"OAuthException\",\n" +
73 " \"code\": 1\n" +
74 " }\n" +
75 "}");
76
77 try
78 {
79 clientTemplate.getForObject("ANY", SOME.class);
80 fail("The expected exception was not thrown");
81 }
82 catch(UnknownErrorException e)
83 {
84 log.debug("{}", e.toString());
85 assertEquals("invalid_request", e.getOAuth2ErrorCode());
86 assertEquals(new Integer(1), e.getCode());
87 assertEquals("An unknown error has occurred.", e.getMessage());
88 assertEquals(Type.OAuthException, e.getType());
89 }
90 }
91
92 @Test
93 public void testError2()
94 {
95 log.info("testError2");
96
97
98 requestFactory.setBody(
99 "{\n" +
100 " \"error\":\n" +
101 " {\n" +
102 " \"message\": \"An unexpected error has occurred. Please retry your request later.\",\n" +
103 " \"type\": \"OAuthException\",\n" +
104 " \"code\": 2\n" +
105 " }\n" +
106 "}");
107
108 try
109 {
110 clientTemplate.getForObject("ANY", SOME.class);
111 fail("The expected exception was not thrown");
112 }
113 catch(UnexpectedErrorException e)
114 {
115 log.debug("{}", e.toString());
116 assertEquals("invalid_request", e.getOAuth2ErrorCode());
117 assertEquals(new Integer(2), e.getCode());
118 assertEquals("An unexpected error has occurred. Please retry your request later.", e.getMessage());
119 assertEquals(Type.OAuthException, e.getType());
120 }
121 }
122
123 @Test
124 public void testError21()
125 {
126 log.info("testError21");
127
128
129 requestFactory.setBody(
130 "{\n" +
131 " \"error\":\n" +
132 " {\n" +
133 " \"message\": \"(#21) Page ID 590408587650316 was migrated to page ID 1421620791415603. Please update your API calls to the new ID\",\n" +
134 " \"type\": \"OAuthException\",\n" +
135 " \"code\": 21\n" +
136 " }\n" +
137 "}");
138
139 try
140 {
141 clientTemplate.getForObject("ANY", SOME.class);
142 fail("The expected exception was not thrown");
143 }
144 catch(PageMigratedException e)
145 {
146 log.debug("{}", e.toString());
147 assertEquals("invalid_request", e.getOAuth2ErrorCode());
148 assertEquals(new Integer(21), e.getCode());
149 assertEquals("(#21) Page ID 590408587650316 was migrated to page ID 1421620791415603. Please update your API calls to the new ID", e.getMessage());
150 assertEquals(Type.OAuthException, e.getType());
151 }
152 }
153
154 @Test
155 public void testError100()
156 {
157 log.info("testError100");
158
159
160 requestFactory.setBody(
161 "{\n" +
162 " \"error\":\n" +
163 " {\n" +
164 " \"message\": \"Unsupported get request.\",\n" +
165 " \"type\": \"GraphMethodException\",\n" +
166 " \"code\": 100\n" +
167 " }\n" +
168 "}");
169
170 try
171 {
172 clientTemplate.getForObject("ANY", SOME.class);
173 fail("The expected exception was not thrown");
174 }
175 catch(UnsupportedGetRequestException e)
176 {
177 log.debug("{}", e.toString());
178 assertEquals("invalid_request", e.getOAuth2ErrorCode());
179 assertEquals(new Integer(100), e.getCode());
180 assertEquals("Unsupported get request.", e.getMessage());
181 assertEquals(Type.GraphMethodException, e.getType());
182 }
183 }
184
185 @Test
186 public void testError104()
187 {
188 log.info("testError104");
189
190 requestFactory.setBody("{\"error\":{\"message\":\"An access token is required to request this resource.\",\"type\":\"OAuthException\",\"code\":104,\"fbtrace_id\":\"E2Jjkj5++LL\"}}");
191
192 try
193 {
194 clientTemplate.getForObject("ANY", SOME.class);
195 fail("The expected exception was not thrown");
196 }
197 catch(AccessTokenRequiredException e)
198 {
199 log.debug("{}", e.toString());
200 assertEquals("invalid_request", e.getOAuth2ErrorCode());
201 assertEquals(new Integer(104), e.getCode());
202 assertEquals("An access token is required to request this resource.", e.getMessage());
203 assertEquals(Type.OAuthException, e.getType());
204 assertEquals("E2Jjkj5++LL", e.getTraceId());
205 }
206 }
207
208 @Test
209 public void testError613()
210 {
211 log.info("testError613");
212
213
214 requestFactory.setBody(
215 "{\n" +
216 " \"error\":\n" +
217 " {\n" +
218 " \"message\": \"(#613) Calls to stream have exceeded the rate of 600 calls per 600 seconds.\",\n" +
219 " \"type\": \"OAuthException\",\n" +
220 " \"code\": 613\n" +
221 " }\n" +
222 "}");
223
224 try
225 {
226 clientTemplate.getForObject("ANY", SOME.class);
227 fail("The expected exception was not thrown");
228 }
229 catch(RateExceededException e)
230 {
231 log.debug("{}", e.toString());
232 assertEquals("invalid_request", e.getOAuth2ErrorCode());
233 assertEquals(new Integer(613), e.getCode());
234 assertEquals("(#613) Calls to stream have exceeded the rate of 600 calls per 600 seconds.", e.getMessage());
235 assertEquals(Type.OAuthException, e.getType());
236 }
237 }
238
239 @Test
240 public void testError2200()
241 {
242 requestFactory.setBody("{\"error\":{\"message\":\"(#2200) callback verification failed: \",\"type\":\"OAuthException\",\"code\":2200,\"fbtrace_id\":\"ESLjoZKvPXg\"}}");
243
244 try
245 {
246 clientTemplate.getForObject("ANY", SOME.class);
247 fail("The expected exception was not thrown");
248 }
249 catch(CallbackVerificationFailedException e)
250 {
251 log.debug("{}", e.toString());
252 assertEquals("invalid_request", e.getOAuth2ErrorCode());
253 assertEquals(new Integer(2200), e.getCode());
254 assertEquals("(#2200) callback verification failed: ", e.getMessage());
255 assertEquals(Type.OAuthException, e.getType());
256 assertEquals("ESLjoZKvPXg", e.getTraceId());
257 }
258 }
259
260 @Test
261 public void testUnmappedError()
262 {
263 log.info("testUnmappedError");
264
265
266 requestFactory.setBody(
267 "{\n" +
268 " \"error\":\n" +
269 " {\n" +
270 " \"message\": \"This error does not exist.\",\n" +
271 " \"type\": \"NonexistentTypeException\",\n" +
272 " \"code\": 999999999\n" +
273 " }\n" +
274 "}");
275
276 try
277 {
278 clientTemplate.getForObject("ANY", SOME.class);
279 fail("The expected exception was not thrown");
280 }
281 catch(GraphApiException e)
282 {
283 log.debug("{}", e.toString());
284 assertEquals("invalid_request", e.getOAuth2ErrorCode());
285 assertEquals(new Integer(999999999), e.getCode());
286 assertEquals("This error does not exist.", e.getMessage());
287 try
288 {
289 Type type = e.getType();
290 log.error("unknown type: {}", type);
291 fail("unmapped type was resolved by enum: " + type);
292 }
293 catch (IllegalArgumentException ee) {}
294 }
295 }
296
297 @Test
298 public void testUnmappedErrors()
299 {
300 log.info("testUnmappedErrors");
301
302
303 requestFactory.setBody(
304 "{\n" +
305 " \"error\":\n" +
306 " {\n" +
307 " \"message\": null,\n" +
308 " \"type\": \"WhateverTypeException\",\n" +
309 " \"code\": 999999999\n" +
310 " }\n" +
311 "}");
312
313 try
314 {
315 clientTemplate.getForObject("ANY", SOME.class);
316 fail("The expected exception was not thrown");
317 }
318 catch(UnmappedErrorException e)
319 {
320 log.debug("{}", e.toString());
321 assertNull(e.getMessage());
322 try
323 {
324 Type type = e.getType();
325 log.error("unknown type: {}", type);
326 fail("unmapped type was resolved by enum: " + type);
327 }
328 catch (IllegalArgumentException ee) {}
329 assertEquals(new Integer(999999999), e.getCode());
330 assertNull(e.getSubCode());
331 assertNull(e.getUserTitle());
332 assertNull(e.getUserMessage());
333 assertNull(e.getTraceId());
334 }
335 catch(Exception e)
336 {
337 fail("A wrong exception was thrown: " + e.toString());
338 }
339
340
341 requestFactory.setBody(
342 "{\n" +
343 " \"error\":\n" +
344 " {\n" +
345 " \"type\": \"WhateverTypeException\",\n" +
346 " \"code\": 999999999\n" +
347 " }\n" +
348 "}");
349
350 try
351 {
352 clientTemplate.getForObject("ANY", SOME.class);
353 fail("The expected exception was not thrown");
354 }
355 catch(UnmappedErrorException e)
356 {
357 log.debug("{}", e.toString());
358 assertNull(e.getMessage());
359 try
360 {
361 Type type = e.getType();
362 log.error("unknown type: {}", type);
363 fail("unmapped type was resolved by enum: " + type);
364 }
365 catch (IllegalArgumentException ee) {}
366 assertEquals(new Integer(999999999), e.getCode());
367 assertNull(e.getSubCode());
368 assertNull(e.getUserTitle());
369 assertNull(e.getUserMessage());
370 assertNull(e.getTraceId());
371 }
372 catch(Exception e)
373 {
374 fail("A wrong exception was thrown: " + e.toString());
375 }
376
377
378 requestFactory.setBody(
379 "{\n" +
380 " \"error\":\n" +
381 " {\n" +
382 " \"message\": \"An unmapped Graph-API-Exception.\",\n" +
383 " \"type\": null,\n" +
384 " \"code\": 999999999\n" +
385 " }\n" +
386 "}");
387
388 try
389 {
390 clientTemplate.getForObject("ANY", SOME.class);
391 fail("The expected exception was not thrown");
392 }
393 catch(UnmappedErrorException e)
394 {
395 log.debug("{}", e.toString());
396 assertEquals("An unmapped Graph-API-Exception.", e.getMessage());
397 assertNull(e.getType());
398 assertEquals(new Integer(999999999), e.getCode());
399 assertNull(e.getSubCode());
400 assertNull(e.getUserTitle());
401 assertNull(e.getUserMessage());
402 assertNull(e.getTraceId());
403 }
404 catch(Exception e)
405 {
406 fail("A wrong exception was thrown: " + e.toString());
407 }
408
409
410 requestFactory.setBody(
411 "{\n" +
412 " \"error\":\n" +
413 " {\n" +
414 " \"message\": \"An unmapped Graph-API-Exception.\",\n" +
415 " \"code\": 999999999\n" +
416 " }\n" +
417 "}");
418
419 try
420 {
421 clientTemplate.getForObject("ANY", SOME.class);
422 fail("The expected exception was not thrown");
423 }
424 catch(UnmappedErrorException e)
425 {
426 log.debug("{}", e.toString());
427 assertEquals("An unmapped Graph-API-Exception.", e.getMessage());
428 assertNull(e.getType());
429 assertEquals(new Integer(999999999), e.getCode());
430 assertNull(e.getSubCode());
431 assertNull(e.getUserTitle());
432 assertNull(e.getUserMessage());
433 assertNull(e.getTraceId());
434 }
435 catch(Exception e)
436 {
437 fail("A wrong exception was thrown: " + e.toString());
438 }
439 }
440
441 @Test
442 public void testInvlalidErrors()
443 {
444 log.info("testInvalidErrors");
445
446
447 requestFactory.setBody(
448 "{\n" +
449 " \"error\":\n" +
450 " {\n" +
451 " \"message\": \"Not a Graph-Api-Exception.\",\n" +
452 " \"type\": \"Whatever\",\n" +
453 " \"code\": \"some string\"\n" +
454 " }\n" +
455 "}");
456
457 try
458 {
459 clientTemplate.getForObject("ANY", SOME.class);
460 fail("The expected exception was not thrown");
461 }
462 catch(HttpClientErrorException e)
463 {
464 log.debug("{}", e.toString());
465 }
466 catch(Exception e)
467 {
468 fail("A wrong exception was thrown: " + e.toString());
469 }
470
471
472 requestFactory.setBody(
473 "{\n" +
474 " \"error\":\n" +
475 " {\n" +
476 " \"message\": \"Not a Graph-Api-Exception.\",\n" +
477 " \"type\": \"Whatever\",\n" +
478 " \"code\": 9.9\n" +
479 " }\n" +
480 "}");
481
482 try
483 {
484 clientTemplate.getForObject("ANY", SOME.class);
485 fail("The expected exception was not thrown");
486 }
487 catch(HttpClientErrorException e)
488 {
489 log.debug("{}", e.toString());
490 }
491 catch(Exception e)
492 {
493 fail("A wrong exception was thrown: " + e.toString());
494 }
495
496
497 requestFactory.setBody(
498 "{\n" +
499 " \"error\":\n" +
500 " {\n" +
501 " \"message\": \"Not a Graph-Api-Exception.\",\n" +
502 " \"type\": \"Whatever\",\n" +
503 " \"code\": null\n" +
504 " }\n" +
505 "}");
506
507 try
508 {
509 clientTemplate.getForObject("ANY", SOME.class);
510 fail("The expected exception was not thrown");
511 }
512 catch(HttpClientErrorException e)
513 {
514 log.debug("{}", e.toString());
515 }
516 catch(Exception e)
517 {
518 fail("A wrong exception was thrown: " + e.toString());
519 }
520
521
522 requestFactory.setBody(
523 "{\n" +
524 " \"error\":\n" +
525 " {\n" +
526 " \"message\": \"Not a Graph-Api-Exception.\",\n" +
527 " \"type\": \"Whatever\"\n" +
528 " }\n" +
529 "}");
530
531 try
532 {
533 clientTemplate.getForObject("ANY", SOME.class);
534 fail("The expected exception was not thrown");
535 }
536 catch(HttpClientErrorException e)
537 {
538 log.debug("{}", e.toString());
539 }
540 catch(Exception e)
541 {
542 fail("A wrong exception was thrown: " + e.toString());
543 }
544
545
546 requestFactory.setBody("{\"error\":{\"message\":null}}");
547
548 try
549 {
550 clientTemplate.getForObject("ANY", SOME.class);
551 fail("The expected exception was not thrown");
552 }
553 catch(HttpClientErrorException e)
554 {
555 log.debug("{}", e.toString());
556 }
557 catch(Exception e)
558 {
559 fail("A wrong exception was thrown: " + e.toString());
560 }
561
562
563 requestFactory.setBody("{\"error\":{\"type\":null}}");
564
565 try
566 {
567 clientTemplate.getForObject("ANY", SOME.class);
568 fail("The expected exception was not thrown");
569 }
570 catch(HttpClientErrorException e)
571 {
572 log.debug("{}", e.toString());
573 }
574 catch(Exception e)
575 {
576 fail("A wrong exception was thrown: " + e.toString());
577 }
578
579
580 requestFactory.setBody("{\"error\":{\"code\":null}}");
581
582 try
583 {
584 clientTemplate.getForObject("ANY", SOME.class);
585 fail("The expected exception was not thrown");
586 }
587 catch(HttpClientErrorException e)
588 {
589 log.debug("{}", e.toString());
590 }
591 catch(Exception e)
592 {
593 fail("A wrong exception was thrown: " + e.toString());
594 }
595
596
597 requestFactory.setBody("{\"error\":{}}");
598
599 try
600 {
601 clientTemplate.getForObject("ANY", SOME.class);
602 fail("The expected exception was not thrown");
603 }
604 catch(HttpClientErrorException e)
605 {
606 log.debug("{}", e.toString());
607 }
608 catch(Exception e)
609 {
610 fail("A wrong exception was thrown: " + e.toString());
611 }
612
613
614 requestFactory.setBody("{\"error\":\"some message\"}");
615
616 try
617 {
618 clientTemplate.getForObject("ANY", SOME.class);
619 fail("The expected exception was not thrown");
620 }
621 catch(HttpClientErrorException e)
622 {
623 log.debug("{}", e.toString());
624 }
625 catch(Exception e)
626 {
627 fail("A wrong exception was thrown: " + e.toString());
628 }
629
630
631 requestFactory.setBody("{\"error\":null}");
632
633 try
634 {
635 clientTemplate.getForObject("ANY", SOME.class);
636 fail("The expected exception was not thrown");
637 }
638 catch(HttpClientErrorException e)
639 {
640 log.debug("{}", e.toString());
641 }
642 catch(Exception e)
643 {
644 fail("A wrong exception was thrown: " + e.toString());
645 }
646
647
648 requestFactory.setBody("{\"some filed\":\"some message\"}");
649
650 try
651 {
652 clientTemplate.getForObject("ANY", SOME.class);
653 fail("The expected exception was not thrown");
654 }
655 catch(HttpClientErrorException e)
656 {
657 log.debug("{}", e.toString());
658 }
659 catch(Exception e)
660 {
661 fail("A wrong exception was thrown: " + e.toString());
662 }
663
664
665 requestFactory.setBody("{}");
666
667 try
668 {
669 clientTemplate.getForObject("ANY", SOME.class);
670 fail("The expected exception was not thrown");
671 }
672 catch(HttpClientErrorException e)
673 {
674 log.debug("{}", e.toString());
675 }
676 catch(Exception e)
677 {
678 fail("A wrong exception was thrown: " + e.toString());
679 }
680
681
682 requestFactory.setBody("");
683
684 try
685 {
686 clientTemplate.getForObject("ANY", SOME.class);
687 fail("The expected exception was not thrown");
688 }
689 catch(HttpClientErrorException e)
690 {
691 log.debug("{}", e.toString());
692 }
693 catch(Exception e)
694 {
695 fail("A wrong exception was thrown: " + e.toString());
696 }
697 }
698
699
700 @Before
701 public void setUp()
702 {
703 requestFactory = new MockClientHttpRequestFactory();
704 requestFactory.setStatus(HttpStatus.BAD_REQUEST);
705 requestFactory.addHeader("Content-Type", "application/json");
706 clientTemplate.setRequestFactory(requestFactory);
707
708 clientTemplate.setErrorHandler(
709 new GraphApiErrorHandler(
710 (OAuth2ErrorHandler)clientTemplate.getErrorHandler()
711 )
712 );
713
714 clientTemplate.setAccessTokenProvider(new AccessTokenProvider()
715 {
716 @Override
717 public OAuth2AccessToken obtainAccessToken(
718 OAuth2ProtectedResourceDetails details,
719 AccessTokenRequest parameters
720 )
721 throws
722 UserRedirectRequiredException,
723 UserApprovalRequiredException,
724 AccessDeniedException
725 {
726 return new OAuth2AccessToken() {
727
728 @Override
729 public Map<String, Object> getAdditionalInformation()
730 {
731 throw new UnsupportedOperationException("Not supported yet.");
732 }
733
734 @Override
735 public Set<String> getScope()
736 {
737 throw new UnsupportedOperationException("Not supported yet.");
738 }
739
740 @Override
741 public OAuth2RefreshToken getRefreshToken()
742 {
743 throw new UnsupportedOperationException("Not supported yet.");
744 }
745
746 @Override
747 public String getTokenType()
748 {
749 return OAUTH2_TYPE;
750 }
751
752 @Override
753 public boolean isExpired()
754 {
755 return false;
756 }
757
758 @Override
759 public Date getExpiration()
760 {
761 throw new UnsupportedOperationException("Not supported yet.");
762 }
763
764 @Override
765 public int getExpiresIn()
766 {
767 throw new UnsupportedOperationException("Not supported yet.");
768 }
769
770 @Override
771 public String getValue()
772 {
773 return "ANY";
774 }
775 };
776 }
777
778 @Override
779 public boolean supportsResource(OAuth2ProtectedResourceDetails resource)
780 {
781 return true;
782 }
783
784 @Override
785 public OAuth2AccessToken refreshAccessToken(
786 OAuth2ProtectedResourceDetails resource,
787 OAuth2RefreshToken refreshToken,
788 AccessTokenRequest request
789 )
790 throws
791 UserRedirectRequiredException
792 {
793 throw new UnsupportedOperationException("Not supported yet.");
794 }
795
796 @Override
797 public boolean supportsRefresh(OAuth2ProtectedResourceDetails resource)
798 {
799 return false;
800 }
801 });
802 }
803
804
805 static class SOME
806 {
807 }
808 }