View Javadoc
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   * @author kai
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 }