Skip to content
This repository has been archived by the owner on Apr 5, 2022. It is now read-only.

Facebook graph version flexibility #132

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ spring-social-core/src/test/java/exploration
**/.project
**/.settings
**/bin
*.iml
*.ipr
*.iws
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
import org.springframework.social.connect.web.SignInAdapter;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.facebook.api.GraphApi;
import org.springframework.social.oauth2.AccessGrant;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
Expand Down Expand Up @@ -72,16 +73,23 @@ public class CanvasSignInController {

private String scope;

private String graphVersion;

@Inject
public CanvasSignInController(ConnectionFactoryLocator connectionFactoryLocator, UsersConnectionRepository usersConnectionRepository, SignInAdapter signInAdapter, String clientId, String clientSecret, String canvasPage) {
this(connectionFactoryLocator, usersConnectionRepository, signInAdapter, clientId, clientSecret, canvasPage, GraphApi.DEFAULT_GRAPH_API_VERSION);
}

public CanvasSignInController(ConnectionFactoryLocator connectionFactoryLocator, UsersConnectionRepository usersConnectionRepository, SignInAdapter signInAdapter, String clientId, String clientSecret, String canvasPage, String graphVersion) {
this.usersConnectionRepository = usersConnectionRepository;
this.signInAdapter = signInAdapter;
this.clientId = clientId;
this.canvasPage = canvasPage;
this.connectionFactoryLocator = connectionFactoryLocator;
this.signedRequestDecoder = new SignedRequestDecoder(clientSecret);
this.graphVersion = graphVersion;
}

/**
* The URL or path to redirect to after successful canvas authorization.
* @param postSignInUrl the url to redirect to after successful canvas authorization. Defaults to "/".
Expand Down Expand Up @@ -131,7 +139,7 @@ protected String getRedirectUrl(Map<String, ?> model) {
String clientId = (String) model.get("clientId");
String canvasPage = (String) model.get("canvasPage");
String scope = (String) model.get("scope");
String redirectUrl = "https://www.facebook.com/v1.0/dialog/oauth?client_id=" + clientId + "&redirect_uri=" + canvasPage;
String redirectUrl = "https://www.facebook.com/" + graphVersion + "/dialog/oauth?client_id=" + clientId + "&redirect_uri=" + canvasPage;
if (scope != null) {
redirectUrl += "&scope=" + formEncode(scope);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,14 @@ public interface GraphApi {
* May be null if no namespace was specified.
*/
String getApplicationNamespace();

/**
* @return The Graph version associated with this GraphApi instance.
*/
String getGraphApiUrl();

static final String GRAPH_API_URL = "https://graph.facebook.com/v2.2/";
static final String GRAPH_API_URL_VERSIONLESS = "https://graph.facebook.com/";

static final String DEFAULT_GRAPH_API_VERSION = "v2.2";

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,7 @@
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.social.NotAuthorizedException;
import org.springframework.social.UncategorizedApiException;
import org.springframework.social.facebook.api.AchievementOperations;
import org.springframework.social.facebook.api.CommentOperations;
import org.springframework.social.facebook.api.EventOperations;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.facebook.api.FeedOperations;
import org.springframework.social.facebook.api.FqlOperations;
import org.springframework.social.facebook.api.FriendOperations;
import org.springframework.social.facebook.api.GroupOperations;
import org.springframework.social.facebook.api.ImageType;
import org.springframework.social.facebook.api.LikeOperations;
import org.springframework.social.facebook.api.MediaOperations;
import org.springframework.social.facebook.api.OpenGraphOperations;
import org.springframework.social.facebook.api.PageOperations;
import org.springframework.social.facebook.api.PagedList;
import org.springframework.social.facebook.api.PagingParameters;
import org.springframework.social.facebook.api.UserOperations;
import org.springframework.social.facebook.api.*;
import org.springframework.social.facebook.api.impl.json.FacebookModule;
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
import org.springframework.social.oauth2.OAuth2Version;
Expand Down Expand Up @@ -103,6 +88,8 @@ public class FacebookTemplate extends AbstractOAuth2ApiBinding implements Facebo

private String applicationNamespace;

private String graphVersion;

/**
* Create a new instance of FacebookTemplate.
* This constructor creates a new FacebookTemplate able to perform unauthenticated operations against Facebook's Graph API.
Expand All @@ -112,7 +99,8 @@ public class FacebookTemplate extends AbstractOAuth2ApiBinding implements Facebo
* Those operations requiring authentication will throw {@link NotAuthorizedException}.
*/
public FacebookTemplate() {
initialize();
this.graphVersion = GraphApi.DEFAULT_GRAPH_API_VERSION;
initialize();
}

/**
Expand All @@ -125,7 +113,12 @@ public FacebookTemplate(String accessToken) {
}

public FacebookTemplate(String accessToken, String applicationNamespace) {
this(accessToken, applicationNamespace, GraphApi.DEFAULT_GRAPH_API_VERSION);
}

public FacebookTemplate(String accessToken, String applicationNamespace, String graphVersion) {
super(accessToken);
this.graphVersion = graphVersion;
this.applicationNamespace = applicationNamespace;
initialize();
}
Expand Down Expand Up @@ -194,7 +187,7 @@ public String getApplicationNamespace() {

// low-level Graph API operations
public <T> T fetchObject(String objectId, Class<T> type) {
URI uri = URIBuilder.fromUri(GRAPH_API_URL + objectId).build();
URI uri = URIBuilder.fromUri(getGraphApiUrl() + objectId).build();
return getRestTemplate().getForObject(uri, type);
}

Expand All @@ -208,7 +201,7 @@ public <T> T fetchObject(String objectId, Class<T> type, String... fields) {
}

public <T> T fetchObject(String objectId, Class<T> type, MultiValueMap<String, String> queryParameters) {
URI uri = URIBuilder.fromUri(GRAPH_API_URL + objectId).queryParams(queryParameters).build();
URI uri = URIBuilder.fromUri(getGraphApiUrl() + objectId).queryParams(queryParameters).build();
return getRestTemplate().getForObject(uri, type);
}

Expand All @@ -223,14 +216,14 @@ public <T> PagedList<T> fetchConnections(String objectId, String connectionType,

public <T> PagedList<T> fetchConnections(String objectId, String connectionType, Class<T> type, MultiValueMap<String, String> queryParameters) {
String connectionPath = connectionType != null && connectionType.length() > 0 ? "/" + connectionType : "";
URIBuilder uriBuilder = URIBuilder.fromUri(GRAPH_API_URL + objectId + connectionPath).queryParams(queryParameters);
URIBuilder uriBuilder = URIBuilder.fromUri(getGraphApiUrl() + objectId + connectionPath).queryParams(queryParameters);
JsonNode jsonNode = getRestTemplate().getForObject(uriBuilder.build(), JsonNode.class);
return pagify(type, jsonNode);
}

public <T> PagedList<T> fetchPagedConnections(String objectId, String connectionType, Class<T> type, MultiValueMap<String, String> queryParameters) {
String connectionPath = connectionType != null && connectionType.length() > 0 ? "/" + connectionType : "";
URIBuilder uriBuilder = URIBuilder.fromUri(GRAPH_API_URL + objectId + connectionPath).queryParams(queryParameters);
URIBuilder uriBuilder = URIBuilder.fromUri(getGraphApiUrl() + objectId + connectionPath).queryParams(queryParameters);
JsonNode jsonNode = getRestTemplate().getForObject(uriBuilder.build(), JsonNode.class);
return pagify(type, jsonNode);
}
Expand All @@ -255,7 +248,7 @@ private <T> PagedList<T> pagify(Class<T> type, JsonNode jsonNode) {
}

public byte[] fetchImage(String objectId, String connectionType, ImageType type) {
URI uri = URIBuilder.fromUri(GRAPH_API_URL + objectId + "/" + connectionType + "?type=" + type.toString().toLowerCase()).build();
URI uri = URIBuilder.fromUri(getGraphApiUrl() + objectId + "/" + connectionType + "?type=" + type.toString().toLowerCase()).build();
ResponseEntity<byte[]> response = getRestTemplate().getForEntity(uri, byte[].class);
if(response.getStatusCode() == HttpStatus.FOUND) {
throw new UnsupportedOperationException("Attempt to fetch image resulted in a redirect which could not be followed. Add Apache HttpComponents HttpClient to the classpath " +
Expand All @@ -267,36 +260,40 @@ public byte[] fetchImage(String objectId, String connectionType, ImageType type)
@SuppressWarnings("unchecked")
public String publish(String objectId, String connectionType, MultiValueMap<String, Object> data) {
MultiValueMap<String, Object> requestData = new LinkedMultiValueMap<String, Object>(data);
URI uri = URIBuilder.fromUri(GRAPH_API_URL + objectId + "/" + connectionType).build();
URI uri = URIBuilder.fromUri(getGraphApiUrl() + objectId + "/" + connectionType).build();
Map<String, Object> response = getRestTemplate().postForObject(uri, requestData, Map.class);
return (String) response.get("id");
}

public void post(String objectId, String connectionType, MultiValueMap<String, String> data) {
URI uri = URIBuilder.fromUri(GRAPH_API_URL + objectId + "/" + connectionType).build();
URI uri = URIBuilder.fromUri(getGraphApiUrl() + objectId + "/" + connectionType).build();
getRestTemplate().postForObject(uri, new LinkedMultiValueMap<String, String>(data), String.class);
}

public void delete(String objectId) {
LinkedMultiValueMap<String, String> deleteRequest = new LinkedMultiValueMap<String, String>();
deleteRequest.set("method", "delete");
URI uri = URIBuilder.fromUri(GRAPH_API_URL + objectId).build();
URI uri = URIBuilder.fromUri(getGraphApiUrl() + objectId).build();
getRestTemplate().postForObject(uri, deleteRequest, String.class);
}

public void delete(String objectId, String connectionType) {
LinkedMultiValueMap<String, String> deleteRequest = new LinkedMultiValueMap<String, String>();
deleteRequest.set("method", "delete");
URI uri = URIBuilder.fromUri(GRAPH_API_URL + objectId + "/" + connectionType).build();
URI uri = URIBuilder.fromUri(getGraphApiUrl() + objectId + "/" + connectionType).build();
getRestTemplate().postForObject(uri, deleteRequest, String.class);
}

public void delete(String objectId, String connectionType, MultiValueMap<String, String> data) {
data.set("method", "delete");
URI uri = URIBuilder.fromUri(GRAPH_API_URL + objectId + "/" + connectionType).build();
URI uri = URIBuilder.fromUri(getGraphApiUrl() + objectId + "/" + connectionType).build();
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<MultiValueMap<String, String>>(data, new HttpHeaders());
getRestTemplate().exchange(uri, HttpMethod.POST, entity, String.class);
}

public String getGraphApiUrl() {
return GRAPH_API_URL_VERSIONLESS + graphVersion + "/";
}

// AbstractOAuth2ApiBinding hooks
@Override
Expand Down Expand Up @@ -360,5 +357,9 @@ private String join(String[] strings) {
}
return builder.toString();
}

public void setGraphVersion(String graphVersion) {
this.graphVersion = graphVersion;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public PagedList<Post> getFeed(String ownerId) {

public PagedList<Post> getFeed(String ownerId, PagingParameters pagedListParameters) {
requireAuthorization();
JsonNode responseNode = fetchConnectionList(GraphApi.GRAPH_API_URL + ownerId + "/feed", pagedListParameters);
JsonNode responseNode = fetchConnectionList(graphApi.getGraphApiUrl() + ownerId + "/feed", pagedListParameters);
return deserializeList(responseNode, null, Post.class);
}

Expand All @@ -84,7 +84,7 @@ public PagedList<Post> getHomeFeed() {

public PagedList<Post> getHomeFeed(PagingParameters pagedListParameters) {
requireAuthorization();
JsonNode responseNode = fetchConnectionList(GraphApi.GRAPH_API_URL + "me/home", pagedListParameters);
JsonNode responseNode = fetchConnectionList(graphApi.getGraphApiUrl() + "me/home", pagedListParameters);
return deserializeList(responseNode, null, Post.class);
}

Expand All @@ -102,7 +102,7 @@ public PagedList<Post> getStatuses(String userId) {

public PagedList<Post> getStatuses(String userId, PagingParameters pagedListParameters) {
requireAuthorization();
JsonNode responseNode = fetchConnectionList(GraphApi.GRAPH_API_URL + userId + "/statuses", pagedListParameters);
JsonNode responseNode = fetchConnectionList(graphApi.getGraphApiUrl() + userId + "/statuses", pagedListParameters);
return deserializeList(responseNode, "status", Post.class);
}

Expand All @@ -120,7 +120,7 @@ public PagedList<Post> getLinks(String ownerId) {

public PagedList<Post> getLinks(String ownerId, PagingParameters pagedListParameters) {
requireAuthorization();
JsonNode responseNode = fetchConnectionList(GraphApi.GRAPH_API_URL + ownerId + "/links", pagedListParameters);
JsonNode responseNode = fetchConnectionList(graphApi.getGraphApiUrl() + ownerId + "/links", pagedListParameters);
return deserializeList(responseNode, "link", Post.class);
}

Expand All @@ -138,13 +138,13 @@ public PagedList<Post> getPosts(String ownerId) {

public PagedList<Post> getPosts(String ownerId, PagingParameters pagedListParameters) {
requireAuthorization();
JsonNode responseNode = fetchConnectionList(GraphApi.GRAPH_API_URL + ownerId + "/posts", pagedListParameters);
JsonNode responseNode = fetchConnectionList(graphApi.getGraphApiUrl() + ownerId + "/posts", pagedListParameters);
return deserializeList(responseNode, null, Post.class);
}

public Post getPost(String entryId) {
requireAuthorization();
ObjectNode responseNode = (ObjectNode) restTemplate.getForObject(GraphApi.GRAPH_API_URL + entryId, JsonNode.class);
ObjectNode responseNode = (ObjectNode) restTemplate.getForObject(graphApi.getGraphApiUrl() + entryId, JsonNode.class);
return deserializePost(null, Post.class, responseNode);
}

Expand Down Expand Up @@ -189,7 +189,7 @@ public PagedList<Post> searchPublicFeed(String query) {
}

public PagedList<Post> searchPublicFeed(String query, PagingParameters pagedListParameters) {
String url = GraphApi.GRAPH_API_URL + "search?q={query}&type=post";
String url = graphApi.getGraphApiUrl() + "search?q={query}&type=post";
Map<String, Object> params = new HashMap<String, Object>();
params.put("query", query);
if (pagedListParameters.getLimit() != null) {
Expand All @@ -214,7 +214,7 @@ public PagedList<Post> searchHomeFeed(String query) {

public PagedList<Post> searchHomeFeed(String query, PagingParameters pagedListParameters) {
requireAuthorization();
URIBuilder uriBuilder = URIBuilder.fromUri(GraphApi.GRAPH_API_URL + "me/home").queryParam("q", query);
URIBuilder uriBuilder = URIBuilder.fromUri(graphApi.getGraphApiUrl() + "me/home").queryParam("q", query);
uriBuilder = appendPagedListParameters(pagedListParameters, uriBuilder);
URI uri = uriBuilder.build();
JsonNode responseNode = restTemplate.getForObject(uri, JsonNode.class);
Expand All @@ -235,7 +235,7 @@ public PagedList<Post> searchUserFeed(String userId, String query) {

public PagedList<Post> searchUserFeed(String userId, String query, PagingParameters pagedListParameters) {
requireAuthorization();
URIBuilder uriBuilder = URIBuilder.fromUri(GraphApi.GRAPH_API_URL + userId + "/feed").queryParam("q", query);
URIBuilder uriBuilder = URIBuilder.fromUri(graphApi.getGraphApiUrl() + userId + "/feed").queryParam("q", query);
uriBuilder = appendPagedListParameters(pagedListParameters, uriBuilder);
URI uri = uriBuilder.build();
JsonNode responseNode = restTemplate.getForObject(uri, JsonNode.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ public PagedList<Reference> getFriends(String userId) {
}

public PagedList<String> getFriendIds(String userId) {
requireAuthorization();
URI uri = URIBuilder.fromUri(GraphApi.GRAPH_API_URL + userId + "/friends").queryParam("fields", "id").build();
requireAuthorization();
URI uri = URIBuilder.fromUri(graphApi.getGraphApiUrl() + userId + "/friends").queryParam("fields", "id").build();
@SuppressWarnings("unchecked")
Map<String,PagedList<Map<String, String>>> response = restTemplate.getForObject(uri, Map.class);
List<Map<String,String>> entryList = response.get("data");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public byte[] getUserProfileImage(String userId, ImageType imageType) {

public List<String> getUserPermissions() {
requireAuthorization();
JsonNode responseNode = restTemplate.getForObject(GraphApi.GRAPH_API_URL + "me/permissions", JsonNode.class);
JsonNode responseNode = restTemplate.getForObject(graphApi.getGraphApiUrl() + "me/permissions", JsonNode.class);
return deserializePermissionsNodeToList(responseNode);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void setConnectionValues(Facebook facebook, ConnectionValues values) {
values.setProviderUserId(profile.getId());
values.setDisplayName(profile.getName());
values.setProfileUrl("https://www.facebook.com/app_scoped_user_id/" + profile.getId() + '/');
values.setImageUrl("https://graph.facebook.com/v2.2/" + profile.getId() + "/picture");
values.setImageUrl("https://graph.facebook.com/" + profile.getId() + "/picture");
}

public UserProfile fetchUserProfile(Facebook facebook) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import org.springframework.social.connect.support.OAuth2ConnectionFactory;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.facebook.api.GraphApi;

/**
* Facebook ConnectionFactory implementation.
Expand All @@ -42,7 +43,18 @@ public FacebookConnectionFactory(String appId, String appSecret) {
* @param appNamespace The application's App Namespace as configured with Facebook. Enables use of Open Graph operations.
*/
public FacebookConnectionFactory(String appId, String appSecret, String appNamespace) {
super("facebook", new FacebookServiceProvider(appId, appSecret, appNamespace), new FacebookAdapter());
this(appId, appSecret, appNamespace, GraphApi.DEFAULT_GRAPH_API_VERSION);
}

/**
* Creates a FacebookConnectionFactory for the given application ID, secret, and namespace.
* @param appId The application's App ID as assigned by Facebook
* @param appSecret The application's App Secret as assigned by Facebook
* @param appNamespace The application's App Namespace as configured with Facebook. Enables use of Open Graph operations.
* @param graphApiVersion The version of Open Graph to use.
*/
public FacebookConnectionFactory(String appId, String appSecret, String appNamespace, String graphApiVersion) {
super("facebook", new FacebookServiceProvider(appId, appSecret, appNamespace, graphApiVersion), new FacebookAdapter());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.springframework.http.MediaType;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.social.facebook.api.GraphApi;
import org.springframework.social.oauth2.AccessGrant;
import org.springframework.social.oauth2.OAuth2Template;
import org.springframework.social.support.ClientHttpRequestFactorySelector;
Expand All @@ -34,8 +35,13 @@
*/
public class FacebookOAuth2Template extends OAuth2Template {

public FacebookOAuth2Template(String clientId, String clientSecret, String graphVersion) {
super(clientId, clientSecret, "https://www.facebook.com/" + graphVersion + "/dialog/oauth", "https://graph.facebook.com/" + graphVersion +"/oauth/access_token");
setUseParametersForClientAuthentication(true);
}

public FacebookOAuth2Template(String clientId, String clientSecret) {
super(clientId, clientSecret, "https://www.facebook.com/v2.2/dialog/oauth", "https://graph.facebook.com/v2.2/oauth/access_token");
this(clientId, clientSecret, GraphApi.DEFAULT_GRAPH_API_VERSION);
setUseParametersForClientAuthentication(true);
}

Expand Down
Loading