Commit 3c40cb35295ddc4d87e5e4a7f5dbac61864369e9
- Diff rendering mode:
- inline
- side by side
spring-social-core/src/main/java/org/springframework/social/facebook/FacebookOperations.java
(30 / 14)
|   | |||
| 25 | 25 | * Implemented by {@link FacebookTemplate}. Not often used directly, but a | |
| 26 | 26 | * useful option to enhance testability, as it can easily be mocked or stubbed. | |
| 27 | 27 | * </p> | |
| 28 | * | ||
| 28 | * | ||
| 29 | 29 | * <p> | |
| 30 | 30 | * Many of the methods contained in this interface require an access token from | |
| 31 | 31 | * Facebook. When a method's description speaks of the "current user", it is | |
| 32 | 32 | * referring to the user for whom the access token has been issued. | |
| 33 | 33 | * </p> | |
| 34 | * | ||
| 34 | * | ||
| 35 | 35 | * @author Craig Walls | |
| 36 | * @author Ales Justin | ||
| 36 | 37 | */ | |
| 37 | 38 | public interface FacebookOperations { | |
| 38 | 39 | /** | |
| 39 | 40 | * Retrieves the user's Facebook profile ID. | |
| 40 | * | ||
| 41 | * | ||
| 41 | 42 | * @return the user's Facebook profile ID. | |
| 42 | 43 | */ | |
| 43 | 44 | String getProfileId(); | |
| 44 | 45 | ||
| 45 | 46 | /** | |
| 46 | 47 | * Retrieve the current user's Facebook profile information. | |
| 47 | * | ||
| 48 | * | ||
| 48 | 49 | * @return the user's profile information. | |
| 49 | 50 | */ | |
| 50 | 51 | FacebookProfile getUserProfile(); | |
| 51 | 52 | ||
| 52 | 53 | /** | |
| 53 | 54 | * Retrieve the URL to the user's Facebook profile. | |
| 54 | * | ||
| 55 | * | ||
| 55 | 56 | * @return the URL to the user's Facebook profile. | |
| 56 | 57 | */ | |
| 57 | 58 | String getProfileUrl(); | |
| 58 | 59 | ||
| 59 | 60 | /** | |
| 60 | 61 | * Get a list of the user's friends. | |
| 61 | * | ||
| 62 | * | ||
| 62 | 63 | * @return a list of <code>String</code>s where each entry is the Facebook | |
| 63 | 64 | * ID of one of the user's friends. | |
| 64 | 65 | */ | |
| … | … | ||
| 67 | 67 | ||
| 68 | 68 | /** | |
| 69 | 69 | * Posts a message to the current user's wall. | |
| 70 | * | ||
| 70 | * | ||
| 71 | * @param status | ||
| 72 | * The message to post | ||
| 73 | * @return post id | ||
| 74 | */ | ||
| 75 | String updateStatus(String status); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Posts a message to the current user's wall along with a link. | ||
| 79 | * | ||
| 71 | 80 | * @param message | |
| 72 | 81 | * The message to post | |
| 82 | * @param link | ||
| 83 | * A link to be included in the status update, can be null. | ||
| 84 | * @return post id | ||
| 73 | 85 | */ | |
| 74 | void updateStatus(String status); | ||
| 86 | String updateStatus(String message, FacebookLink link); | ||
| 75 | 87 | ||
| 76 | 88 | /** | |
| 77 | 89 | * Posts a message to the current user's wall along with a link. | |
| 78 | * | ||
| 90 | * | ||
| 79 | 91 | * @param message | |
| 80 | 92 | * The message to post | |
| 81 | 93 | * @param link | |
| 82 | * A link to be included in the status update | ||
| 94 | * A link to be included in the status update, can be null. | ||
| 95 | * @param fetchPostId | ||
| 96 | * A flag to indicate if we actually fetch post id. | ||
| 97 | * @return post id or null if fetchPostId equals false | ||
| 83 | 98 | */ | |
| 84 | void updateStatus(String message, FacebookLink link); | ||
| 99 | String updateStatus(String message, FacebookLink link, boolean fetchPostId); | ||
| 85 | 100 | ||
| 86 | 101 | /** | |
| 87 | 102 | * <p> | |
| 88 | 103 | * Low-level publish-to-Facebook method for publishing any type of object | |
| 89 | 104 | * supported by Facebook's API. | |
| 90 | 105 | * </p> | |
| 91 | * | ||
| 106 | * | ||
| 92 | 107 | * @param object | |
| 93 | 108 | * The ID of the object to publish to. | |
| 94 | 109 | * @param connection | |
| … | … | ||
| 115 | 115 | ||
| 116 | 116 | /** | |
| 117 | 117 | * Retrieves the current user's profile picture as an array of bytes. | |
| 118 | * | ||
| 118 | * | ||
| 119 | 119 | * @return the user's profile picture in bytes. | |
| 120 | 120 | */ | |
| 121 | 121 | byte[] getProfilePicture(); | |
| 122 | 122 | ||
| 123 | 123 | /** | |
| 124 | 124 | * Retrieves a user's profile picture as an array of bytes. | |
| 125 | * | ||
| 125 | * | ||
| 126 | 126 | * @param profileId | |
| 127 | 127 | * the Facebook ID of the user. | |
| 128 | 128 | * @return the user's profile picture in bytes. |
spring-social-core/src/main/java/org/springframework/social/facebook/FacebookTemplate.java
(66 / 35)
|   | |||
| 22 | 22 | ||
| 23 | 23 | import org.springframework.http.MediaType; | |
| 24 | 24 | import org.springframework.http.ResponseEntity; | |
| 25 | import org.springframework.http.client.ClientHttpRequestFactory; | ||
| 25 | 26 | import org.springframework.http.client.CommonsClientHttpRequestFactory; | |
| 26 | 27 | import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; | |
| 27 | 28 | import org.springframework.util.LinkedMultiValueMap; | |
| … | … | ||
| 32 | 32 | ||
| 33 | 33 | /** | |
| 34 | 34 | * This is the central class for interacting with Facebook. | |
| 35 | * | ||
| 35 | * | ||
| 36 | 36 | * <p> | |
| 37 | 37 | * All operations through Facebook require OAuth 2-based authentication. | |
| 38 | 38 | * Therefore, FacebookTemplate must be given an access token at construction | |
| 39 | 39 | * time. | |
| 40 | 40 | * </p> | |
| 41 | * | ||
| 41 | * | ||
| 42 | 42 | * <p> | |
| 43 | 43 | * The easiest way to get an access token is to use the XFBML | |
| 44 | 44 | * <fb:login-button> tag to require the user to signin to Facebook. Then, | |
| … | … | ||
| 47 | 47 | * {@link FacebookWebArgumentResolver} can extract the access token from the | |
| 48 | 48 | * cookie and make it available as a String argument to the controller method. | |
| 49 | 49 | * </p> | |
| 50 | * | ||
| 50 | * | ||
| 51 | 51 | * @author Craig Walls | |
| 52 | * @author Ales Justin | ||
| 52 | 53 | * @see FacebookWebArgumentResolver | |
| 53 | 54 | */ | |
| 54 | 55 | public class FacebookTemplate implements FacebookOperations { | |
| … | … | ||
| 58 | 58 | ||
| 59 | 59 | /** | |
| 60 | 60 | * Create a new instance of FacebookTemplate. | |
| 61 | * | ||
| 61 | * | ||
| 62 | 62 | * This constructor creates the FacebookTemplate using a given access token. | |
| 63 | * | ||
| 63 | * | ||
| 64 | 64 | * @param accessToken | |
| 65 | 65 | * An access token given by Facebook after a successful OAuth 2 | |
| 66 | 66 | * authentication (or through Facebook's JS library). | |
| 67 | 67 | */ | |
| 68 | 68 | public FacebookTemplate(String accessToken) { | |
| 69 | this.accessToken = accessToken; | ||
| 70 | RestTemplate restTemplate = new RestTemplate(); | ||
| 71 | // must be CommonsClientHttpRequestFactory or else the location header | ||
| 72 | // in an HTTP 302 won't be followed | ||
| 73 | restTemplate.setRequestFactory(new CommonsClientHttpRequestFactory()); | ||
| 74 | MappingJacksonHttpMessageConverter json = new MappingJacksonHttpMessageConverter(); | ||
| 75 | json.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "javascript"))); | ||
| 76 | restTemplate.getMessageConverters().add(json); | ||
| 77 | this.restOperations = restTemplate; | ||
| 69 | this(accessToken, new CommonsClientHttpRequestFactory()); | ||
| 78 | 70 | } | |
| 79 | 71 | ||
| 72 | /** | ||
| 73 | * Create a new instance of FacebookTemplate. | ||
| 74 | * | ||
| 75 | * This constructor creates the FacebookTemplate using a given access token. | ||
| 76 | * | ||
| 77 | * @param accessToken | ||
| 78 | * An access token given by Facebook after a successful OAuth 2 | ||
| 79 | * authentication (or through Facebook's JS library). | ||
| 80 | * @param requestFactory | ||
| 81 | * A request factory; can be useful in restricted environments such as GAE. | ||
| 82 | */ | ||
| 83 | public FacebookTemplate(String accessToken, ClientHttpRequestFactory requestFactory) { | ||
| 84 | this.accessToken = accessToken; | ||
| 85 | RestTemplate restTemplate = new RestTemplate(); | ||
| 86 | // must be CommonsClientHttpRequestFactory or else the location header | ||
| 87 | // in an HTTP 302 won't be followed | ||
| 88 | restTemplate.setRequestFactory(requestFactory); | ||
| 89 | MappingJacksonHttpMessageConverter json = new MappingJacksonHttpMessageConverter(); | ||
| 90 | json.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "javascript"))); | ||
| 91 | restTemplate.getMessageConverters().add(json); | ||
| 92 | this.restOperations = restTemplate; | ||
| 93 | } | ||
| 94 | |||
| 80 | 95 | public String getProfileId() { | |
| 81 | 96 | return Long.toString(getUserProfile().getId()); | |
| 82 | 97 | } | |
| … | … | ||
| 107 | 107 | ||
| 108 | 108 | public List<String> getFriendIds() { | |
| 109 | 109 | @SuppressWarnings("rawtypes") | |
| 110 | ResponseEntity<Map> response = restOperations.getForEntity(CONNECTION_URL, Map.class, CURRENT_USER, FRIENDS, | ||
| 111 | accessToken); | ||
| 110 | ResponseEntity<Map> response = restOperations.getForEntity(CONNECTION_URL, Map.class, CURRENT_USER, FRIENDS, accessToken); | ||
| 112 | 111 | ||
| 113 | 112 | @SuppressWarnings("unchecked") | |
| 114 | 113 | Map<String, List<Map<String, String>>> resultsMap = response.getBody(); | |
| 115 | 114 | List<Map<String, String>> friends = resultsMap.get("data"); | |
| 116 | |||
| 115 | |||
| 117 | 116 | List<String> friendIds = new ArrayList<String>(); | |
| 118 | 117 | for (Map<String, String> friendData : friends) { | |
| 119 | 118 | friendIds.add(friendData.get("id")); | |
| 120 | 119 | } | |
| 121 | 120 | return friendIds; | |
| 122 | 121 | } | |
| 123 | |||
| 124 | public void updateStatus(String message) { | ||
| 125 | MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>(); | ||
| 126 | map.set("message", message); | ||
| 127 | publish(CURRENT_USER, FEED, map); | ||
| 122 | |||
| 123 | public String updateStatus(String message) { | ||
| 124 | return updateStatus(message, null); | ||
| 128 | 125 | } | |
| 129 | |||
| 130 | public void updateStatus(String message, FacebookLink link) { | ||
| 126 | |||
| 127 | public String updateStatus(String message, FacebookLink link) { | ||
| 128 | return updateStatus(message, link, true); | ||
| 129 | } | ||
| 130 | |||
| 131 | public String updateStatus(String message, FacebookLink link, boolean fetchPostId) { | ||
| 131 | 132 | MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>(); | |
| 132 | map.set("link", link.getLink()); | ||
| 133 | map.set("name", link.getName()); | ||
| 134 | map.set("caption", link.getCaption()); | ||
| 135 | map.set("description", link.getDescription()); | ||
| 136 | map.set("message", message); | ||
| 133 | map.set("message", message); | ||
| 134 | if (link != null) { | ||
| 135 | map.set("link", link.getLink()); | ||
| 136 | map.set("name", link.getName()); | ||
| 137 | map.set("caption", link.getCaption()); | ||
| 138 | map.set("description", link.getDescription()); | ||
| 139 | } | ||
| 137 | 140 | publish(CURRENT_USER, FEED, map); | |
| 141 | |||
| 142 | if (fetchPostId) { | ||
| 143 | @SuppressWarnings("rawtypes") | ||
| 144 | ResponseEntity<Map> response = restOperations.getForEntity(CONNECTION_URL, Map.class, CURRENT_USER, FEED, accessToken); | ||
| 145 | @SuppressWarnings("unchecked") | ||
| 146 | Map<String, List<Map<String, String>>> resultsMap = response.getBody(); | ||
| 147 | List<Map<String, String>> posts = resultsMap.get("data"); | ||
| 148 | return posts.get(0).get("id"); // Should exist, as we just posted | ||
| 149 | } | ||
| 150 | else { | ||
| 151 | return null; | ||
| 152 | } | ||
| 138 | 153 | } | |
| 139 | |||
| 154 | |||
| 140 | 155 | public void publish(String object, String connection, MultiValueMap<String, String> data) { | |
| 141 | 156 | MultiValueMap<String, String> requestData = new LinkedMultiValueMap<String, String>(data); | |
| 142 | 157 | restOperations.postForLocation(CONNECTION_URL, requestData, object, connection, accessToken); | |
| 143 | 158 | } | |
| 144 | |||
| 159 | |||
| 145 | 160 | public byte[] getProfilePicture() { | |
| 146 | 161 | return getProfilePicture(CURRENT_USER); | |
| 147 | 162 | } | |
| 148 | 163 | ||
| 149 | 164 | public byte[] getProfilePicture(String profileId) { | |
| 150 | ResponseEntity<byte[]> imageBytes = restOperations.getForEntity(PROFILE_LARGE_PICTURE_URL, byte[].class, | ||
| 151 | profileId, accessToken); | ||
| 165 | ResponseEntity<byte[]> imageBytes = restOperations.getForEntity(PROFILE_LARGE_PICTURE_URL, byte[].class, profileId, accessToken); | ||
| 152 | 166 | return imageBytes.getBody(); | |
| 153 | 167 | } | |
| 154 | |||
| 168 | |||
| 155 | 169 | static final String PROFILE_LARGE_PICTURE_URL = "https://graph.facebook.com/{profile}/picture?type=large&access_token={accessToken}"; | |
| 156 | 170 | static final String OBJECT_URL = "https://graph.facebook.com/{objectId}"; | |
| 157 | 171 | static final String CONNECTION_URL = OBJECT_URL + "/{connection}?access_token={accessToken}"; | |
| 158 | |||
| 172 | |||
| 159 | 173 | static final String FRIENDS = "friends"; | |
| 160 | 174 | static final String FEED = "feed"; | |
| 161 | 175 | static final String CURRENT_USER = "me"; |
spring-social-core/src/test/java/org/springframework/social/facebook/FacebookTemplateTest.java
(43 / 12)
|   | |||
| 15 | 15 | */ | |
| 16 | 16 | package org.springframework.social.facebook; | |
| 17 | 17 | ||
| 18 | import static java.util.Collections.*; | ||
| 19 | import static org.junit.Assert.*; | ||
| 20 | import static org.junit.internal.matchers.IsCollectionContaining.*; | ||
| 21 | import static org.mockito.Matchers.*; | ||
| 22 | import static org.mockito.Mockito.*; | ||
| 23 | import static org.springframework.http.HttpStatus.*; | ||
| 24 | import static org.springframework.social.facebook.FacebookTemplate.*; | ||
| 25 | |||
| 26 | 18 | import java.util.ArrayList; | |
| 27 | 19 | import java.util.HashMap; | |
| 28 | 20 | import java.util.List; | |
| … | … | ||
| 26 | 26 | import org.springframework.util.LinkedMultiValueMap; | |
| 27 | 27 | import org.springframework.util.MultiValueMap; | |
| 28 | 28 | import org.springframework.web.client.RestOperations; | |
| 29 | import static java.util.Collections.singletonMap; | ||
| 30 | import static org.junit.Assert.*; | ||
| 31 | import static org.junit.internal.matchers.IsCollectionContaining.hasItem; | ||
| 32 | import static org.mockito.Matchers.eq; | ||
| 33 | import static org.mockito.Mockito.*; | ||
| 34 | import static org.springframework.http.HttpStatus.OK; | ||
| 35 | import static org.springframework.social.facebook.FacebookTemplate.*; | ||
| 29 | 36 | ||
| 30 | 37 | /** | |
| 31 | 38 | * @author Craig Walls | |
| 39 | * @author Ales Justin | ||
| 32 | 40 | */ | |
| 33 | 41 | public class FacebookTemplateTest { | |
| 34 | 42 | private static final String ACCESS_TOKEN = "someAccessToken"; | |
| … | … | ||
| 72 | 72 | ||
| 73 | 73 | @Test | |
| 74 | 74 | public void getUserProfile() { | |
| 75 | FacebookProfile fbProfile = setupRestOperationsForGettingProfile(); | ||
| 75 | setupRestOperationsForGettingProfile(); | ||
| 76 | |||
| 76 | 77 | FacebookProfile actual = facebook.getUserProfile(); | |
| 77 | 78 | assertEquals("Craig", actual.getFirstName()); | |
| 78 | 79 | assertEquals("Walls", actual.getLastName()); | |
| … | … | ||
| 120 | 120 | ||
| 121 | 121 | @Test | |
| 122 | 122 | public void updateStatus() { | |
| 123 | facebook.updateStatus("Hello Facebook!"); | ||
| 123 | Map<String, List<Map<String, String>>> resultsMap = new HashMap<String, List<Map<String, String>>>(); | ||
| 124 | List<Map<String, String>> postsList = new ArrayList<Map<String, String>>(); | ||
| 125 | postsList.add(singletonMap("id", "12345")); | ||
| 126 | postsList.add(singletonMap("id", "67890")); | ||
| 127 | resultsMap.put("data", postsList); | ||
| 124 | 128 | ||
| 129 | ResponseEntity<Map> response = new ResponseEntity<Map>(resultsMap, OK); | ||
| 130 | when(restOperations.getForEntity(eq(CONNECTION_URL), eq(Map.class), eq(CURRENT_USER), eq(FEED), | ||
| 131 | eq(ACCESS_TOKEN))).thenReturn(response); | ||
| 132 | |||
| 133 | assertEquals(facebook.updateStatus("Hello Facebook!"), "12345"); | ||
| 134 | |||
| 125 | 135 | MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>(); | |
| 126 | 136 | map.set("message", "Hello Facebook!"); | |
| 127 | 137 | verify(restOperations).postForLocation(eq(CONNECTION_URL), eq(map), eq(CURRENT_USER), eq(FEED), | |
| … | … | ||
| 140 | 140 | ||
| 141 | 141 | @Test | |
| 142 | 142 | public void updateStatus_withLink() { | |
| 143 | Map<String, List<Map<String, String>>> resultsMap = new HashMap<String, List<Map<String, String>>>(); | ||
| 144 | List<Map<String, String>> postsList = new ArrayList<Map<String, String>>(); | ||
| 145 | postsList.add(singletonMap("id", "12345")); | ||
| 146 | postsList.add(singletonMap("id", "67890")); | ||
| 147 | resultsMap.put("data", postsList); | ||
| 148 | |||
| 149 | ResponseEntity<Map> response = new ResponseEntity<Map>(resultsMap, OK); | ||
| 150 | when(restOperations.getForEntity(eq(CONNECTION_URL), eq(Map.class), eq(CURRENT_USER), eq(FEED), | ||
| 151 | eq(ACCESS_TOKEN))).thenReturn(response); | ||
| 152 | |||
| 143 | 153 | String linkUrl = "http://www.springsource.com"; | |
| 144 | 154 | String linkName = "SpringSource"; | |
| 145 | 155 | String linkCaption = "SpringSource Home Page"; | |
| 146 | 156 | String linkDescription = "SpringSource is the leader in Java application and infrastructure management."; | |
| 147 | facebook.updateStatus("Hello Facebook!", new FacebookLink(linkUrl, linkName, linkCaption, linkDescription)); | ||
| 148 | 157 | ||
| 158 | FacebookLink link = new FacebookLink(linkUrl, linkName, linkCaption, linkDescription); | ||
| 159 | assertEquals(facebook.updateStatus("Hello Facebook!", link), "12345"); | ||
| 160 | |||
| 149 | 161 | MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>(); | |
| 150 | 162 | map.set("message", "Hello Facebook!"); | |
| 151 | 163 | map.set("link", linkUrl); | |
| … | … | ||
| 168 | 168 | eq(ACCESS_TOKEN)); | |
| 169 | 169 | } | |
| 170 | 170 | ||
| 171 | @Test | ||
| 172 | public void updateStatus_withNullLink_and_noPostIdFetch() { | ||
| 173 | assertNull(facebook.updateStatus("Hello Facebook!", null, false)); | ||
| 174 | |||
| 175 | MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>(); | ||
| 176 | map.set("message", "Hello Facebook!"); | ||
| 177 | verify(restOperations).postForLocation(eq(CONNECTION_URL), eq(map), eq(CURRENT_USER), eq(FEED), eq(ACCESS_TOKEN)); | ||
| 178 | } | ||
| 179 | |||
| 171 | 180 | private FacebookProfile setupRestOperationsForGettingProfile() { | |
| 172 | 181 | FacebookProfile fbProfile = new FacebookProfile(); | |
| 173 | 182 | fbProfile.firstName = "Craig"; | |
| … | … | ||
| 188 | 188 | eq("me"), eq(ACCESS_TOKEN))).thenReturn(fbProfile); | |
| 189 | 189 | return fbProfile; | |
| 190 | 190 | } | |
| 191 | |||
| 192 | 191 | ||
| 193 | 192 | } |
Comments
Add your comment
Please log in to comment



Add a new comment:
Login or create an account to post a comment