Skip to content
Browse files

added response envelope and other migrations to all operations

  • Loading branch information...
1 parent ba09f99 commit 1d6452724d66c75bffae2a6173c20eaf02b80cd5 @arikg committed Sep 2, 2012
Showing with 630 additions and 129 deletions.
  1. +51 −17 src/main/java/org/springframework/social/appdotnet/api/PostsOperations.java
  2. +4 −41 src/main/java/org/springframework/social/appdotnet/api/UsersOperations.java
  3. +43 −0 src/main/java/org/springframework/social/appdotnet/api/data/ADNPaging.java
  4. +25 −0 src/main/java/org/springframework/social/appdotnet/api/data/ADNResponse.java
  5. +67 −0 src/main/java/org/springframework/social/appdotnet/api/data/ADNResponseMeta.java
  6. +36 −0 src/main/java/org/springframework/social/appdotnet/api/data/post/ADNPosts.java
  7. +0 −11 src/main/java/org/springframework/social/appdotnet/api/data/post/ADNPostsList.java
  8. +37 −0 src/main/java/org/springframework/social/appdotnet/api/data/user/ADNUsers.java
  9. +0 −11 src/main/java/org/springframework/social/appdotnet/api/data/user/ADNUsersList.java
  10. +18 −2 src/main/java/org/springframework/social/appdotnet/api/impl/AbstractAppdotnetOperations.java
  11. +25 −0 src/main/java/org/springframework/social/appdotnet/api/impl/AppdotnetRequestInterceptor.java
  12. +7 −1 src/main/java/org/springframework/social/appdotnet/api/impl/AppdotnetTemplate.java
  13. +12 −0 src/main/java/org/springframework/social/appdotnet/api/impl/PostResponse.java
  14. +14 −0 src/main/java/org/springframework/social/appdotnet/api/impl/PostsResponse.java
  15. +49 −15 src/main/java/org/springframework/social/appdotnet/api/impl/PostsTemplate.java
  16. +12 −0 src/main/java/org/springframework/social/appdotnet/api/impl/TokenResponse.java
  17. +2 −1 src/main/java/org/springframework/social/appdotnet/api/impl/TokensTemplate.java
  18. +12 −0 src/main/java/org/springframework/social/appdotnet/api/impl/UserResponse.java
  19. +14 −0 src/main/java/org/springframework/social/appdotnet/api/impl/UsersResponse.java
  20. +16 −29 src/main/java/org/springframework/social/appdotnet/api/impl/UsersTemplate.java
  21. +98 −0 src/main/java/org/springframework/social/appdotnet/api/impl/error/AppdotnetErrorHandler.java
  22. +38 −0 src/main/java/org/springframework/social/appdotnet/api/impl/error/AppdotnetErrorMeta.java
  23. +16 −0 src/main/java/org/springframework/social/appdotnet/api/impl/error/AppdotnetErrorResponse.java
  24. +1 −1 src/main/java/org/springframework/social/appdotnet/api/impl/json/ADNLinkMixin.java
  25. +33 −0 src/test/java/org/springframework/social/appdotnet/connect/AppdotnetAdapterTest.java
View
68 src/main/java/org/springframework/social/appdotnet/api/PostsOperations.java
@@ -1,8 +1,8 @@
package org.springframework.social.appdotnet.api;
import org.springframework.social.appdotnet.api.data.post.ADNPost;
+import org.springframework.social.appdotnet.api.data.post.ADNPosts;
-import java.util.List;
import java.util.Map;
/**
@@ -39,60 +39,94 @@
/**
* Retrieve the latest posts from the user personal stream (this includes only posts from users he is following)
*
- * @return List of Post objects
+ * @return A Posts object containing a list of posts and extra data
*/
- public List<ADNPost> getPersonalStream();
+ public ADNPosts getPersonalStream();
/**
* Retrieve the latest posts from the user personal stream (this includes only posts from users he is following)
*
* @param extraParams extra parameters to modify the query, best used with {@link GeneralParametersBuilder}
- * @return List of Post objects
+ * @return A Posts object containing a list of posts and extra data
*/
- public List<ADNPost> getPersonalStream(Map<String, String> extraParams);
+ public ADNPosts getPersonalStream(Map<String, String> extraParams);
/**
* Retrieve the latest posts from the global stream
*
- * @return List of Post objects
+ * @return A Posts object containing a list of posts and extra data
*/
- public List<ADNPost> getGlobalStream();
+ public ADNPosts getGlobalStream();
/**
* Retrieve the latest posts from the global stream
*
* @param extraParams extra parameters to modify the query, best used with {@link GeneralParametersBuilder}
- * @return List of Post objects
+ * @return A Posts object containing a list of posts and extra data
*/
- public List<ADNPost> getGlobalStream(Map<String, String> extraParams);
+ public ADNPosts getGlobalStream(Map<String, String> extraParams);
/**
* Retrieve the latest posts that contain the specified hashtag
*
- * @return List of Post objects
+ * @return A Posts object containing a list of posts and extra data
*/
- public List<ADNPost> getHashtagStream(String hashtag);
+ public ADNPosts getHashtagStream(String hashtag);
/**
* Retrieve the latest posts that contain the specified hashtag
*
* @param extraParams extra parameters to modify the query, best used with {@link GeneralParametersBuilder}
- * @return List of Post objects
+ * @return A Posts object containing a list of posts and extra data
*/
- public List<ADNPost> getHashtagStream(String hashtag, Map<String, String> extraParams);
+ public ADNPosts getHashtagStream(String hashtag, Map<String, String> extraParams);
/**
* Retrieve all posts that are in the stream of replies to a specified post
*
- * @return List of Post objects
+ * @return A Posts object containing a list of posts and extra data
*/
- public List<ADNPost> getPostReplies(String id);
+ public ADNPosts getPostReplies(String id);
/**
* Retrieve all posts that are in the stream of replies to a specified post
*
* @param extraParams extra parameters to modify the query, best used with {@link GeneralParametersBuilder}
- * @return List of Post objects
+ * @return A Posts object containing a list of posts and extra data
*/
- public List<ADNPost> getPostReplies(String id, Map<String, String> extraParams);
+ public ADNPosts getPostReplies(String id, Map<String, String> extraParams);
+
+ /**
+ * Retrieve all posts created by the specified user
+ *
+ * @param userId App.net user ID of the user whose posts you wish to retrieve
+ * @return A Posts object containing a list of posts and extra data
+ */
+ public ADNPosts getUserPosts(String userId);
+
+ /**
+ * Retrieve all posts created by the specified user
+ *
+ * @param userId App.net user ID of the user whose posts you wish to retrieve
+ * @param extraParams extra parameters to modify the query, best used with {@link GeneralParametersBuilder}
+ * @return A Posts object containing a list of posts and extra data
+ */
+ public ADNPosts getUserPosts(String userId, Map<String, String> extraParams);
+
+ /**
+ * Retrieve all posts that the specified user was mentioned in
+ *
+ * @param userId App.net user ID of the user whose mentions you wish to retrieve
+ * @return A Posts object containing a list of posts and extra data
+ */
+ public ADNPosts getUserMentions(String userId);
+
+ /**
+ * Retrieve all posts that the specified user was mentioned in
+ *
+ * @param userId userId App.net user ID of the user whose mentions you wish to retrieve
+ * @param extraParams extra parameters to modify the query, best used with {@link GeneralParametersBuilder}
+ * @return A Posts object containing a list of posts and extra data
+ */
+ public ADNPosts getUserMentions(String userId, Map<String, String> extraParams);
}
View
45 src/main/java/org/springframework/social/appdotnet/api/UsersOperations.java
@@ -1,10 +1,7 @@
package org.springframework.social.appdotnet.api;
-import org.springframework.social.appdotnet.api.data.post.ADNPost;
import org.springframework.social.appdotnet.api.data.user.ADNUser;
-
-import java.util.List;
-import java.util.Map;
+import org.springframework.social.appdotnet.api.data.user.ADNUsers;
/**
* Interface defining the operations for retrieving information about App.net users.
@@ -48,15 +45,15 @@
* @param id App.net id of the user you wish to get followers of
* @return a List of {@link ADNUser} objects representing all the user's followers
*/
- public List<ADNUser> getFollowers(String id);
+ public ADNUsers getFollowers(String id);
/**
* Get all the users that the specified user is following
*
* @param id App.net id of the user you wish to get a following list of
* @return a List of {@link ADNUser} objects representing all the users that the specified user is following
*/
- public List<ADNUser> getFollowing(String id);
+ public ADNUsers getFollowing(String id);
/**
* Mute the user who's ID was specified
@@ -79,40 +76,6 @@
*
* @return a List of {@link ADNUser} objects representing all the users that the logged in user muted
*/
- public List<ADNUser> getMutedUsers();
-
- /**
- * Retrieve all posts created by the specified user
- *
- * @param userId App.net user ID of the user whose posts you wish to retrieve
- * @return a List of {@link ADNPost} objects all created by the user specified (without the user in the)
- */
- public List<ADNPost> getPosts(String userId);
-
- /**
- * Retrieve all posts created by the specified user
- *
- * @param userId App.net user ID of the user whose posts you wish to retrieve
- * @param extraParams extra parameters to modify the query, best used with {@link GeneralParametersBuilder}
- * @return a List of {@link ADNPost} objects all created by the user specified (without the user in the)
- */
- public List<ADNPost> getPosts(String userId, Map<String, String> extraParams);
-
- /**
- * Retrieve all posts that the specified user was mentioned in
- *
- * @param userId App.net user ID of the user whose mentions you wish to retrieve
- * @return a List of {@link ADNPost} objects in which the user is mentioned
- */
- public List<ADNPost> getMentions(String userId);
-
- /**
- * Retrieve all posts that the specified user was mentioned in
- *
- * @param userId userId App.net user ID of the user whose mentions you wish to retrieve
- * @param extraParams extra parameters to modify the query, best used with {@link GeneralParametersBuilder}
- * @return a List of {@link ADNPost} objects in which the user is mentioned
- */
- public List<ADNPost> getMentions(String userId, Map<String, String> extraParams);
+ public ADNUsers getMutedUsers();
}
View
43 src/main/java/org/springframework/social/appdotnet/api/data/ADNPaging.java
@@ -0,0 +1,43 @@
+package org.springframework.social.appdotnet.api.data;
+
+/**
+ * @author Arik Galansky
+ */
+public class ADNPaging {
+ private String minId;
+ private String maxId;
+ private Boolean more;
+
+ public ADNPaging() {
+ }
+
+ public ADNPaging(String minId, String maxId, Boolean more) {
+ this.minId = minId;
+ this.maxId = maxId;
+ this.more = more;
+ }
+
+ public String getMinId() {
+ return minId;
+ }
+
+ public void setMinId(String minId) {
+ this.minId = minId;
+ }
+
+ public String getMaxId() {
+ return maxId;
+ }
+
+ public void setMaxId(String maxId) {
+ this.maxId = maxId;
+ }
+
+ public Boolean hasMore() {
+ return more;
+ }
+
+ public void setMore(Boolean more) {
+ this.more = more;
+ }
+}
View
25 src/main/java/org/springframework/social/appdotnet/api/data/ADNResponse.java
@@ -0,0 +1,25 @@
+package org.springframework.social.appdotnet.api.data;
+
+/**
+ * @author Arik Galansky
+ */
+public class ADNResponse<T> {
+ private T data;
+ private ADNResponseMeta meta;
+
+ public T getData() {
+ return data;
+ }
+
+ public void setData(T data) {
+ this.data = data;
+ }
+
+ public ADNResponseMeta getMeta() {
+ return meta;
+ }
+
+ public void setMeta(ADNResponseMeta meta) {
+ this.meta = meta;
+ }
+}
View
67 src/main/java/org/springframework/social/appdotnet/api/data/ADNResponseMeta.java
@@ -0,0 +1,67 @@
+package org.springframework.social.appdotnet.api.data;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+
+/**
+ * @author Arik Galansky
+ */
+// TODO Arikg: remove setters
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ADNResponseMeta {
+ private String max_id;
+ private String min_id;
+ private Boolean more;
+ private String code;
+ private String error_message;
+ private String error_slug;
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getError_message() {
+ return error_message;
+ }
+
+ public void setError_message(String error_message) {
+ this.error_message = error_message;
+ }
+
+ public String getError_slug() {
+ return error_slug;
+ }
+
+ public void setError_slug(String error_slug) {
+ this.error_slug = error_slug;
+ }
+
+ public String getMax_id() {
+ return max_id;
+ }
+
+ public void setMax_id(String max_id) {
+ this.max_id = max_id;
+ }
+
+ public String getMin_id() {
+ return min_id;
+ }
+
+ public void setMin_id(String min_id) {
+ this.min_id = min_id;
+ }
+
+ public Boolean getMore() {
+ return more;
+ }
+
+ public void setMore(Boolean more) {
+ this.more = more;
+ }
+
+
+}
View
36 src/main/java/org/springframework/social/appdotnet/api/data/post/ADNPosts.java
@@ -0,0 +1,36 @@
+package org.springframework.social.appdotnet.api.data.post;
+
+import org.springframework.social.appdotnet.api.data.ADNPaging;
+
+import java.util.List;
+
+/**
+ * A class extending a generic list of posts with extra paging data
+ *
+ * @author Arik Galansky
+ */
+public class ADNPosts {
+ private List<ADNPost> posts;
+ private ADNPaging paging;
+
+ public ADNPosts(List<ADNPost> posts, ADNPaging paging) {
+ this.posts = posts;
+ this.paging = paging;
+ }
+
+ public List<ADNPost> getPosts() {
+ return posts;
+ }
+
+ public void setPosts(List<ADNPost> posts) {
+ this.posts = posts;
+ }
+
+ public ADNPaging getPaging() {
+ return paging;
+ }
+
+ public void setPaging(ADNPaging paging) {
+ this.paging = paging;
+ }
+}
View
11 src/main/java/org/springframework/social/appdotnet/api/data/post/ADNPostsList.java
@@ -1,11 +0,0 @@
-package org.springframework.social.appdotnet.api.data.post;
-
-import java.util.ArrayList;
-
-/**
- * A class extending a generic list of posts to use when a Class object is needed
- *
- * @author Arik Galansky
- */
-public class ADNPostsList extends ArrayList<ADNPost> {
-}
View
37 src/main/java/org/springframework/social/appdotnet/api/data/user/ADNUsers.java
@@ -0,0 +1,37 @@
+package org.springframework.social.appdotnet.api.data.user;
+
+import org.springframework.social.appdotnet.api.data.ADNPaging;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class extending a generic list of users with extra paging data
+ *
+ * @author Arik Galansky
+ */
+public class ADNUsers extends ArrayList<ADNUser> {
+ private List<ADNUser> users;
+ private ADNPaging paging;
+
+ public ADNUsers(List<ADNUser> users, ADNPaging paging) {
+ this.users = users;
+ this.paging = paging;
+ }
+
+ public List<ADNUser> getUsers() {
+ return users;
+ }
+
+ public void setUsers(List<ADNUser> users) {
+ this.users = users;
+ }
+
+ public ADNPaging getPaging() {
+ return paging;
+ }
+
+ public void setPaging(ADNPaging paging) {
+ this.paging = paging;
+ }
+}
View
11 src/main/java/org/springframework/social/appdotnet/api/data/user/ADNUsersList.java
@@ -1,11 +0,0 @@
-package org.springframework.social.appdotnet.api.data.user;
-
-import java.util.ArrayList;
-
-/**
- * A class extending a generic list of users to use when A Class object is needed
- *
- * @author Arik Galansky
- */
-public class ADNUsersList extends ArrayList<ADNUser> {
-}
View
20 src/main/java/org/springframework/social/appdotnet/api/impl/AbstractAppdotnetOperations.java
@@ -1,5 +1,7 @@
package org.springframework.social.appdotnet.api.impl;
+import org.springframework.social.appdotnet.api.data.ADNPaging;
+import org.springframework.social.appdotnet.api.data.ADNResponseMeta;
import org.springframework.social.support.URIBuilder;
import org.springframework.web.client.RestTemplate;
@@ -16,16 +18,23 @@
protected final RestTemplate restTemplate;
protected final String accessToken;
private final String baseUrl;
+ private final String defaultResource;
public AbstractAppdotnetOperations(String accessToken, RestTemplate restTemplate, String name, String version) {
this.restTemplate = restTemplate;
this.accessToken = accessToken;
this.baseUrl = new StringBuilder().append("https://alpha-api.app.net/stream/")
- .append(version).append("/").append(name).append("/").toString();
+ .append(version).append("/").toString();
+ this.defaultResource = name;
}
protected String buildUri(String uri, Map<String, String> params) {
- URIBuilder builder = URIBuilder.fromUri(baseUrl + uri);
+ return buildUri(defaultResource, uri, params);
+ }
+
+ protected String buildUri(String resource, String uri, Map<String, String> params) {
+ String url = new StringBuilder().append(baseUrl).append(resource).append("/").append(uri).toString();
+ URIBuilder builder = URIBuilder.fromUri(url);
if (accessToken != null) {
builder = builder.queryParam("access_token", accessToken);
}
@@ -37,6 +46,13 @@ protected String buildUri(String uri, Map<String, String> params) {
return builder.build().toString();
}
+ protected ADNPaging createPaging(ADNResponseMeta meta) {
+ if (meta != null) {
+ return new ADNPaging(meta.getMin_id(), meta.getMax_id(), meta.getMore());
+ }
+ return null;
+ }
+
protected String buildUri(String uri) {
return buildUri(uri, null);
}
View
25 src/main/java/org/springframework/social/appdotnet/api/impl/AppdotnetRequestInterceptor.java
@@ -0,0 +1,25 @@
+package org.springframework.social.appdotnet.api.impl;
+
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.http.client.support.HttpRequestWrapper;
+
+import java.io.IOException;
+
+/**
+ * Request interceptor class used to make the request suitable for app.net interaction by setting appropriate headers.
+ *
+ * @author Arik Galansky
+ */
+class AppdotnetRequestInterceptor implements ClientHttpRequestInterceptor {
+ @Override
+ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
+ HttpRequestWrapper requestWrapper = new HttpRequestWrapper(request);
+ requestWrapper.getHeaders().set("X-ADN-Migration-Overrides",
+ "response_envelope=1&disable_min_max_id=1&follow_pagination=1");
+
+ return execution.execute(requestWrapper, body);
+ }
+}
View
8 src/main/java/org/springframework/social/appdotnet/api/impl/AppdotnetTemplate.java
@@ -1,15 +1,19 @@
package org.springframework.social.appdotnet.api.impl;
import org.codehaus.jackson.map.ObjectMapper;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.social.appdotnet.api.Appdotnet;
import org.springframework.social.appdotnet.api.PostsOperations;
import org.springframework.social.appdotnet.api.TokensOperations;
import org.springframework.social.appdotnet.api.UsersOperations;
+import org.springframework.social.appdotnet.api.impl.error.AppdotnetErrorHandler;
import org.springframework.social.appdotnet.api.impl.json.AppdotnetJacksonModule;
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
import org.springframework.web.client.RestTemplate;
+import static java.util.Collections.singletonList;
+
/**
* This is the central class for interacting with App.net.
* From it you can get the sub-api classes and execute operation on the app.net api.
@@ -49,7 +53,9 @@ protected MappingJacksonHttpMessageConverter getJsonMessageConverter() {
@Override
protected void configureRestTemplate(RestTemplate restTemplate) {
- // TODO Arikg: set error handler
+ restTemplate.setErrorHandler(new AppdotnetErrorHandler());
+ restTemplate.setInterceptors(singletonList((ClientHttpRequestInterceptor) new AppdotnetRequestInterceptor()));
+
}
@Override
View
12 src/main/java/org/springframework/social/appdotnet/api/impl/PostResponse.java
@@ -0,0 +1,12 @@
+package org.springframework.social.appdotnet.api.impl;
+
+import org.springframework.social.appdotnet.api.data.ADNResponse;
+import org.springframework.social.appdotnet.api.data.post.ADNPost;
+
+/**
+ * Empty implementation used to get the Class object to fix type erasure
+ *
+ * @author Arik Galansky
+ */
+class PostResponse extends ADNResponse<ADNPost> {
+}
View
14 src/main/java/org/springframework/social/appdotnet/api/impl/PostsResponse.java
@@ -0,0 +1,14 @@
+package org.springframework.social.appdotnet.api.impl;
+
+import org.springframework.social.appdotnet.api.data.ADNResponse;
+import org.springframework.social.appdotnet.api.data.post.ADNPost;
+
+import java.util.List;
+
+/**
+ * Empty implementation used to get the Class object to fix type erasure
+ *
+ * @author Arik Galansky
+ */
+class PostsResponse extends ADNResponse<List<ADNPost>> {
+}
View
64 src/main/java/org/springframework/social/appdotnet/api/impl/PostsTemplate.java
@@ -1,8 +1,9 @@
package org.springframework.social.appdotnet.api.impl;
import org.springframework.social.appdotnet.api.PostsOperations;
+import org.springframework.social.appdotnet.api.data.ADNResponse;
import org.springframework.social.appdotnet.api.data.post.ADNPost;
-import org.springframework.social.appdotnet.api.data.post.ADNPostsList;
+import org.springframework.social.appdotnet.api.data.post.ADNPosts;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
@@ -23,14 +24,14 @@ public PostsTemplate(String accessToken, RestTemplate restTemplate) {
@Override
public ADNPost get(String id) {
- return restTemplate.getForObject(buildUri(id), ADNPost.class);
+ return restTemplate.getForObject(buildUri(id), PostResponse.class).getData();
}
@Override
public ADNPost create(String text) {
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("text", text);
- return restTemplate.postForObject(buildUri(), map, ADNPost.class);
+ return restTemplate.postForObject(buildUri(), map, PostResponse.class).getData();
}
@Override
@@ -39,42 +40,75 @@ public void delete(String id) {
}
@Override
- public List<ADNPost> getPersonalStream(Map<String, String> extraParams) {
- return restTemplate.getForObject(buildUri("stream", extraParams), ADNPostsList.class);
+ public ADNPosts getPersonalStream(Map<String, String> extraParams) {
+ return getPosts(extraParams, "stream", null);
}
@Override
- public List<ADNPost> getPersonalStream() {
+ public ADNPosts getPersonalStream() {
return getPersonalStream(null);
}
@Override
- public List<ADNPost> getGlobalStream(Map<String, String> extraParams) {
- return restTemplate.getForObject(buildUri("stream/global", extraParams), ADNPostsList.class);
+ public ADNPosts getGlobalStream(Map<String, String> extraParams) {
+ return getPosts(extraParams, "stream/global", null);
}
@Override
- public List<ADNPost> getGlobalStream() {
+ public ADNPosts getGlobalStream() {
return getGlobalStream(null);
}
@Override
- public List<ADNPost> getHashtagStream(String hashtag, Map<String, String> extraParams) {
- return restTemplate.getForObject(buildUri("tag/" + hashtag, extraParams), ADNPostsList.class);
+ public ADNPosts getHashtagStream(String hashtag, Map<String, String> extraParams) {
+ return getPosts(extraParams, "tag/" + hashtag, null);
}
@Override
- public List<ADNPost> getHashtagStream(String hashtag) {
+ public ADNPosts getHashtagStream(String hashtag) {
return getHashtagStream(hashtag, null);
}
@Override
- public List<ADNPost> getPostReplies(String id, Map<String, String> extraParams) {
- return restTemplate.getForObject(buildUri(id + "/replies", extraParams), ADNPostsList.class);
+ public ADNPosts getPostReplies(String id, Map<String, String> extraParams) {
+ return getPosts(extraParams, id + "/replies", null);
}
@Override
- public List<ADNPost> getPostReplies(String id) {
+ public ADNPosts getPostReplies(String id) {
return getPostReplies(id, null);
}
+
+ @Override
+ public ADNPosts getUserPosts(String userId, Map<String, String> extraParams) {
+ return getPosts(extraParams, userId + "/posts", "users");
+ }
+
+ @Override
+ public ADNPosts getUserPosts(String userId) {
+ return getUserPosts(userId, null);
+ }
+
+ @Override
+ public ADNPosts getUserMentions(String userId, Map<String, String> extraParams) {
+ return getPosts(extraParams, userId + "/mentions", "users");
+ }
+
+ @Override
+ public ADNPosts getUserMentions(String userId) {
+ return getUserMentions(userId, null);
+ }
+
+ // private
+
+ private ADNPosts getPosts(Map<String, String> extraParams, String action, String resource) {
+ String url;
+ if (resource == null) {
+ url = buildUri(action, extraParams);
+ } else {
+ url = buildUri(resource, action, extraParams);
+ }
+ ADNResponse<List<ADNPost>> response = restTemplate.getForObject(url, PostsResponse.class);
+ return new ADNPosts(response.getData(), createPaging(response.getMeta()));
+ }
}
View
12 src/main/java/org/springframework/social/appdotnet/api/impl/TokenResponse.java
@@ -0,0 +1,12 @@
+package org.springframework.social.appdotnet.api.impl;
+
+import org.springframework.social.appdotnet.api.data.ADNResponse;
+import org.springframework.social.appdotnet.api.data.ADNToken;
+
+/**
+ * Empty implementation used to get the Class object to fix type erasure
+ *
+ * @author Arik Galansky
+ */
+class TokenResponse extends ADNResponse<ADNToken> {
+}
View
3 src/main/java/org/springframework/social/appdotnet/api/impl/TokensTemplate.java
@@ -10,12 +10,13 @@
* @author Arik Galansky
*/
class TokensTemplate extends AbstractAppdotnetOperations implements TokensOperations {
+
public TokensTemplate(String accessToken, RestTemplate restTemplate) {
super(accessToken, restTemplate, "token", VERSION_0);
}
@Override
public ADNToken getToken() {
- return restTemplate.getForObject(buildUri(), ADNToken.class);
+ return restTemplate.getForObject(buildUri(), TokenResponse.class).getData();
}
}
View
12 src/main/java/org/springframework/social/appdotnet/api/impl/UserResponse.java
@@ -0,0 +1,12 @@
+package org.springframework.social.appdotnet.api.impl;
+
+import org.springframework.social.appdotnet.api.data.ADNResponse;
+import org.springframework.social.appdotnet.api.data.user.ADNUser;
+
+/**
+ * Empty implementation used to get the Class object to fix type erasure
+ *
+ * @author Arik Galansky
+ */
+class UserResponse extends ADNResponse<ADNUser> {
+}
View
14 src/main/java/org/springframework/social/appdotnet/api/impl/UsersResponse.java
@@ -0,0 +1,14 @@
+package org.springframework.social.appdotnet.api.impl;
+
+import org.springframework.social.appdotnet.api.data.ADNResponse;
+import org.springframework.social.appdotnet.api.data.user.ADNUser;
+
+import java.util.List;
+
+/**
+ * Empty implementation used to get the Class object to fix type erasure
+ *
+ * @author Arik Galansky
+ */
+class UsersResponse extends ADNResponse<List<ADNUser>> {
+}
View
45 src/main/java/org/springframework/social/appdotnet/api/impl/UsersTemplate.java
@@ -1,10 +1,9 @@
package org.springframework.social.appdotnet.api.impl;
import org.springframework.social.appdotnet.api.UsersOperations;
-import org.springframework.social.appdotnet.api.data.post.ADNPost;
-import org.springframework.social.appdotnet.api.data.post.ADNPostsList;
+import org.springframework.social.appdotnet.api.data.ADNResponse;
import org.springframework.social.appdotnet.api.data.user.ADNUser;
-import org.springframework.social.appdotnet.api.data.user.ADNUsersList;
+import org.springframework.social.appdotnet.api.data.user.ADNUsers;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@@ -23,7 +22,7 @@ public UsersTemplate(String accessToken, RestTemplate restTemplate) {
@Override
public ADNUser get(String id) {
- return restTemplate.getForObject(buildUri(id), ADNUser.class);
+ return restTemplate.getForObject(buildUri(id), UserResponse.class).getData();
}
@Override
@@ -33,7 +32,7 @@ public ADNUser getUserProfile() {
@Override
public ADNUser follow(String id) {
- return restTemplate.postForObject(buildUri(id + "follow"), null, ADNUser.class);
+ return restTemplate.getForObject(buildUri(id + "follow"), UserResponse.class).getData();
}
@Override
@@ -42,18 +41,19 @@ public void unfollow(String id) {
}
@Override
- public List<ADNUser> getFollowers(String id) {
- return restTemplate.getForObject(buildUri(id + "followers"), ADNUsersList.class);
+ public ADNUsers getFollowers(String id) {
+ return getUsers(null, id + "followers");
}
@Override
- public List<ADNUser> getFollowing(String id) {
- return restTemplate.getForObject(buildUri(id + "following"), ADNUsersList.class);
+ public ADNUsers getFollowing(String id) {
+ return getUsers(null, id + "following");
}
@Override
public ADNUser mute(String id) {
- return restTemplate.postForObject(buildUri(id + "mute"), null, ADNUser.class);
+ return restTemplate.postForObject(buildUri(id + "mute"), null, UserResponse.class).getData();
+
}
@Override
@@ -62,27 +62,14 @@ public void unmute(String id) {
}
@Override
- public List<ADNUser> getMutedUsers() {
- return restTemplate.getForObject(buildUri("me/following"), ADNUsersList.class);
- }
-
- @Override
- public List<ADNPost> getPosts(String userId, Map<String, String> extraParams) {
- return restTemplate.getForObject(buildUri(userId + "/posts", extraParams), ADNPostsList.class);
- }
-
- @Override
- public List<ADNPost> getPosts(String userId) {
- return getPosts(userId, null);
+ public ADNUsers getMutedUsers() {
+ return getUsers(null, "me/muted");
}
- @Override
- public List<ADNPost> getMentions(String userId, Map<String, String> extraParams) {
- return restTemplate.getForObject(buildUri(userId + "/mentions", extraParams), ADNPostsList.class);
- }
+ // private
- @Override
- public List<ADNPost> getMentions(String userId) {
- return getMentions(userId, null);
+ private ADNUsers getUsers(Map<String, String> extraParams, String action) {
+ ADNResponse<List<ADNUser>> response = restTemplate.getForObject(buildUri(action, extraParams), UsersResponse.class);
+ return new ADNUsers(response.getData(), createPaging(response.getMeta()));
}
}
View
98 src/main/java/org/springframework/social/appdotnet/api/impl/error/AppdotnetErrorHandler.java
@@ -0,0 +1,98 @@
+package org.springframework.social.appdotnet.api.impl.error;
+
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.social.*;
+import org.springframework.web.client.DefaultResponseErrorHandler;
+
+import java.io.IOException;
+
+import static org.springframework.http.HttpStatus.Series.CLIENT_ERROR;
+import static org.springframework.http.HttpStatus.Series.SERVER_ERROR;
+
+/**
+ * Subclass of {@link DefaultResponseErrorHandler} that handles errors from Appdotnet API,
+ * interpreting them into appropriate exceptions.
+ *
+ * @author Arik Galansky
+ */
+public class AppdotnetErrorHandler extends DefaultResponseErrorHandler {
+
+
+ @Override
+ public void handleError(ClientHttpResponse response) throws IOException {
+ HttpStatus statusCode = response.getStatusCode();
+ if (statusCode.series() == SERVER_ERROR) {
+ handleServerErrors(statusCode);
+ } else if (statusCode.series() == CLIENT_ERROR) {
+ handleClientErrors(response);
+ }
+
+ // if not otherwise handled, do default handling and wrap with UncategorizedApiException
+ try {
+ super.handleError(response);
+ } catch (Exception e) {
+ throw new UncategorizedApiException("Error consuming App.net REST API", e);
+ }
+ }
+
+ private void handleClientErrors(ClientHttpResponse response) throws IOException {
+ HttpStatus statusCode = response.getStatusCode();
+ AppdotnetErrorMeta errorMeta = extractErrorDetailsFromResponse(response);
+ if (errorMeta == null) {
+ return; // unexpected error body, can't be handled here
+ }
+
+ if (statusCode == HttpStatus.NOT_FOUND) {
+ throw new ResourceNotFoundException(constructErrorMessage(errorMeta));
+ }
+ if ("invalid-token".equals(errorMeta.getError_slug())) {
+ throw new InvalidAuthorizationException(constructErrorMessage(errorMeta));
+ }
+ if ("not-authorized".equals(errorMeta.getError_slug())) {
+ throw new NotAuthorizedException(constructErrorMessage(errorMeta));
+ }
+ if ("token-expired".equals(errorMeta.getError_slug())) {
+ throw new ExpiredAuthorizationException();
+ }
+ if ("code-used".equals(errorMeta.getError_slug())) {
+ throw new InvalidAuthorizationException(constructErrorMessage(errorMeta));
+ }
+ if ("redirect-uri-required".equals(errorMeta.getError_slug())) {
+ throw new InvalidAuthorizationException(constructErrorMessage(errorMeta));
+ }
+ }
+
+ private String constructErrorMessage(AppdotnetErrorMeta errorMeta) {
+ StringBuilder sb = new StringBuilder();
+ if (errorMeta.getError_slug() != null) {
+ sb.append(errorMeta.getError_slug()).append(": ");
+ }
+ if (errorMeta.getError_message() != null) {
+ sb.append(errorMeta.getError_message());
+ }
+ return sb.toString();
+ }
+
+ private void handleServerErrors(HttpStatus statusCode) throws IOException {
+ if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR) {
+ throw new InternalServerErrorException("Something is broken at App.net");
+ } else if (statusCode == HttpStatus.BAD_GATEWAY) {
+ throw new ServerDownException("App.net is down or is being upgraded.");
+ } else if (statusCode == HttpStatus.SERVICE_UNAVAILABLE) {
+ throw new ServerOverloadedException("App.net is overloaded with requests. Try again later.");
+ }
+ }
+
+ private AppdotnetErrorMeta extractErrorDetailsFromResponse(ClientHttpResponse response) throws IOException {
+ ObjectMapper mapper = new ObjectMapper(new JsonFactory());
+ AppdotnetErrorResponse errorResponse = mapper.readValue(response.getBody(), AppdotnetErrorResponse.class);
+ if (errorResponse != null) {
+ return errorResponse.getMeta();
+ }
+
+ return null;
+ }
+}
View
38 src/main/java/org/springframework/social/appdotnet/api/impl/error/AppdotnetErrorMeta.java
@@ -0,0 +1,38 @@
+package org.springframework.social.appdotnet.api.impl.error;
+
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+
+/**
+ * @author Arik Galansky
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+class AppdotnetErrorMeta {
+ private String code;
+ private String error_message;
+ private String error_slug;
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getError_message() {
+ return error_message;
+ }
+
+ public void setError_message(String error_message) {
+ this.error_message = error_message;
+ }
+
+ public String getError_slug() {
+ return error_slug;
+ }
+
+ public void setError_slug(String error_slug) {
+ this.error_slug = error_slug;
+ }
+}
View
16 ...main/java/org/springframework/social/appdotnet/api/impl/error/AppdotnetErrorResponse.java
@@ -0,0 +1,16 @@
+package org.springframework.social.appdotnet.api.impl.error;
+
+/**
+ * @author Arik Galansky
+ */
+class AppdotnetErrorResponse {
+ private AppdotnetErrorMeta meta;
+
+ public AppdotnetErrorMeta getMeta() {
+ return meta;
+ }
+
+ public void setMeta(AppdotnetErrorMeta meta) {
+ this.meta = meta;
+ }
+}
View
2 src/main/java/org/springframework/social/appdotnet/api/impl/json/ADNLinkMixin.java
@@ -10,7 +10,7 @@
* @author Arik Galansky
*/
@JsonIgnoreProperties(ignoreUnknown = true)
-public class ADNLinkMixin {
+class ADNLinkMixin {
@JsonCreator
ADNLinkMixin(
@JsonProperty("text") String text,
View
33 src/test/java/org/springframework/social/appdotnet/connect/AppdotnetAdapterTest.java
@@ -0,0 +1,33 @@
+package org.springframework.social.appdotnet.connect;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.social.appdotnet.api.Appdotnet;
+import org.springframework.social.appdotnet.api.UsersOperations;
+import org.springframework.social.appdotnet.api.data.user.ADNUser;
+import org.springframework.social.connect.UserProfile;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Arik Galansky
+ */
+public class AppdotnetAdapterTest {
+ private AppdotnetAdapter apiAdapter = new AppdotnetAdapter();
+
+ private Appdotnet api = Mockito.mock(Appdotnet.class);
+
+ @Test
+ public void fetchProfile() {
+ UsersOperations usersOperations = Mockito.mock(UsersOperations.class);
+ Mockito.when(api.usersOperations()).thenReturn(usersOperations);
+ Mockito.when(usersOperations.getUserProfile()).thenReturn(createBasicUser());
+ UserProfile userProfile = apiAdapter.fetchUserProfile(api);
+ assertEquals("Arik Galansky", userProfile.getName());
+ assertEquals("arikg", userProfile.getUsername());
+ }
+
+ private ADNUser createBasicUser() {
+ return new ADNUser("123", "arikg", "Arik Galansky", null, null, null, null, null, null, null, null, null, null, null, null);
+ }
+}

0 comments on commit 1d64527

Please sign in to comment.
Something went wrong with that request. Please try again.