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