diff --git a/spring-social-facebook/src/main/java/org/springframework/social/facebook/api/UserOperations.java b/spring-social-facebook/src/main/java/org/springframework/social/facebook/api/UserOperations.java index 90b7988a1..a20121f7d 100644 --- a/spring-social-facebook/src/main/java/org/springframework/social/facebook/api/UserOperations.java +++ b/spring-social-facebook/src/main/java/org/springframework/social/facebook/api/UserOperations.java @@ -90,4 +90,23 @@ public interface UserOperations { * @throws MissingAuthorizationException if FacebookTemplate was not created with an access token. */ List search(String query); + + /** + * Posts a short free-form message to the user's notification list + * NOTE: This will only work with an APP access token (not a user access token) + * SEE: https://developers.facebook.com/docs/app_notifications/ + * + * @param userId the Facebook user ID + * @param href The relative path/GET params of the target (for example, "index.html?gift_id=123", + * or "?gift_id=123"). Then we will construct proper target URL based on your app settings. The + * logic is that, on web, if Canvas setting exists, we always show “Canvas URL + href”. If not, + * we show nothing. In the future (not in this version), we will also use existing URL + * re-writing logic to support mobile canvas and native mobile apps. We also append some special + * tracking params (fb_source, notif_id, notif_t) to the target URL for developers to track at + * their side. One example of target URL displayed in the jewel is: + * https://apps.facebook.com/bzhang_og/?fb_source=notification¬if_id=notif_514699839_145756436&ref=notif¬if_t=app_notification + * @param template The customized text of the notification. + * @return + */ + boolean postNotifcation(String userId, String href, String template); } diff --git a/spring-social-facebook/src/main/java/org/springframework/social/facebook/api/impl/UserTemplate.java b/spring-social-facebook/src/main/java/org/springframework/social/facebook/api/impl/UserTemplate.java index d5eb7a31a..082b7448c 100644 --- a/spring-social-facebook/src/main/java/org/springframework/social/facebook/api/impl/UserTemplate.java +++ b/spring-social-facebook/src/main/java/org/springframework/social/facebook/api/impl/UserTemplate.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; import org.codehaus.jackson.JsonNode; import org.springframework.social.facebook.api.FacebookProfile; @@ -93,4 +94,17 @@ private List deserializePermissionsNodeToList(JsonNode jsonNode) { } return permissions; } + + @SuppressWarnings("unchecked") + public boolean postNotifcation(String userId, String href, String template) { + requireAuthorization(); + + MultiValueMap map = new LinkedMultiValueMap(); + map.set("href", href); + map.set("template", template); + + String uri = GraphApi.GRAPH_API_URL + userId + "/notifications"; + Map response = restTemplate.postForObject(uri, map, Map.class); + return (Boolean)response.get("success"); + } } diff --git a/spring-social-facebook/src/test/java/org/springframework/social/facebook/api/UserTemplateTest.java b/spring-social-facebook/src/test/java/org/springframework/social/facebook/api/UserTemplateTest.java index 247b066be..ccea3179b 100644 --- a/spring-social-facebook/src/test/java/org/springframework/social/facebook/api/UserTemplateTest.java +++ b/spring-social-facebook/src/test/java/org/springframework/social/facebook/api/UserTemplateTest.java @@ -25,8 +25,10 @@ import org.junit.Test; import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.social.NotAuthorizedException; +import org.springframework.social.OperationNotPermittedException; /** * @author Craig Walls @@ -206,6 +208,38 @@ public void search() { public void search_unauthorized() { unauthorizedFacebook.userOperations().search("Michael Scott"); } + + @Test + public void sendNotification(){ + mockServer.expect(requestTo("https://graph.facebook.com/123/notifications")) + .andExpect(method(POST)) + .andExpect(header("Authorization", "OAuth someAccessToken")) + .andRespond(withSuccess(jsonResource("testdata/notifications"), MediaType.APPLICATION_JSON)); + + assertTrue(facebook.userOperations().postNotifcation("123", "href", "template")); + } + + + @Test(expected = OperationNotPermittedException.class) + public void sendNotification_invalidUser(){ + mockServer.expect(requestTo("https://graph.facebook.com/123/notifications")) + .andExpect(method(POST)) + .andExpect(header("Authorization", "OAuth someAccessToken")) + .andRespond(withStatus(HttpStatus.FORBIDDEN).body(jsonResource("testdata/error-permission")).contentType(MediaType.APPLICATION_JSON)); + + facebook.userOperations().postNotifcation("123", "href", "template"); + } + + @Test(expected = NotAuthorizedException.class) + public void sendNotification_invalidAppId(){ + mockServer.expect(requestTo("https://graph.facebook.com/123/notifications")) + .andExpect(method(POST)) + .andExpect(header("Authorization", "OAuth someAccessToken")) + .andRespond(withStatus(HttpStatus.UNAUTHORIZED).body(jsonResource("testdata/error-invalid-access-token")).contentType(MediaType.APPLICATION_JSON)); + + facebook.userOperations().postNotifcation("123", "href", "template"); + } + private void assertBasicProfileData(FacebookProfile profile, boolean withMiddleName) { assertEquals("123456789", profile.getId()); diff --git a/spring-social-facebook/src/test/resources/org/springframework/social/facebook/api/testdata/error-invalid-access-token.json b/spring-social-facebook/src/test/resources/org/springframework/social/facebook/api/testdata/error-invalid-access-token.json new file mode 100644 index 000000000..315c07a5d --- /dev/null +++ b/spring-social-facebook/src/test/resources/org/springframework/social/facebook/api/testdata/error-invalid-access-token.json @@ -0,0 +1,7 @@ +{ + "error": { + "message": "Invalid OAuth access token.", + "type": "OAuthException", + "code": 190 + } +} \ No newline at end of file diff --git a/spring-social-facebook/src/test/resources/org/springframework/social/facebook/api/testdata/notifications.json b/spring-social-facebook/src/test/resources/org/springframework/social/facebook/api/testdata/notifications.json new file mode 100644 index 000000000..07d8f1ba2 --- /dev/null +++ b/spring-social-facebook/src/test/resources/org/springframework/social/facebook/api/testdata/notifications.json @@ -0,0 +1,3 @@ +{ + "success" : true +}