social-networking-library-ws/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Posts.java

592 lines
22 KiB
Java

package org.gcube.portal.social.networking.ws.methods.v2;
import java.util.ArrayList;
import java.util.List;
import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.authorization.library.utils.Caller;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.portal.databook.server.DatabookStore;
import org.gcube.portal.databook.shared.ApplicationProfile;
import org.gcube.portal.databook.shared.Feed;
import org.gcube.portal.databook.shared.Post;
import org.gcube.portal.databook.shared.RangePosts;
import org.gcube.portal.databook.shared.ex.ColumnNameNotFoundException;
import org.gcube.portal.databook.shared.ex.FeedTypeNotFoundException;
import org.gcube.portal.databook.shared.ex.PrivacyLevelTypeNotFoundException;
import org.gcube.portal.social.networking.ws.inputs.PostInputBean;
import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
import org.gcube.portal.social.networking.ws.utils.CassandraConnection;
import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
import org.gcube.portal.social.networking.ws.utils.Filters;
import org.gcube.portal.social.networking.ws.utils.SocialUtils;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
/**
* REST interface for the social networking library (posts).
*/
@Path("2/posts")
@RequestHeaders ({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader( name = "Content-Type", description = "application/json")
})
public class Posts {
// Logger
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Posts.class);
@GET
@Path("get-posts-user-since/")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes ({
@ResponseCode ( code = 200, condition = "Successful retrieval of posts, reported in the 'result' field of the returned object"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* Retrieve posts of the auth token's owner, and allow to filter them by time"
* @param timeInMillis The reference time since when retrieving posts
* @return the posts
* @throws ValidationException
*/
public Response getRecentPostsByUserAndDate(
@QueryParam("time") @Min(value = 0, message="time cannot be negative")
long timeInMillis
) throws ValidationException{
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
Caller caller = AuthorizationProvider.instance.get();
String context = ScopeProvider.instance.get();
String username = caller.getClient().getId();
List<Post> posts = null;
try{
logger.info("Retrieving post for user id " + username + " and reference time " + timeInMillis);
posts = CassandraConnection.getInstance().getDatabookStore().getRecentPostsByUserAndDate(username, timeInMillis);
Filters.filterPostsPerContext(posts, context);
Filters.hideSensitiveInformation(posts, caller.getClient().getId());
responseBean.setResult(posts);
responseBean.setMessage("");
responseBean.setSuccess(true);
}catch(Exception e){
logger.error("Unable to retrieve such posts.", e);
responseBean.setMessage(e.getMessage());
responseBean.setSuccess(false);
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("get-posts-user/")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes ({
@ResponseCode ( code = 200, condition = "Successful retrieval of posts, reported in the 'result' field of the returned object"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* Retrieve all user's posts
* @return all posts of the auth token's owner in the context identified by the token
*/
public Response getAllPostsByUser() {
Caller caller = AuthorizationProvider.instance.get();
String username = caller.getClient().getId();
String context = ScopeProvider.instance.get();
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
List<Post> posts = null;
try{
logger.debug("Retrieving posts for user with id " + username);
posts = CassandraConnection.getInstance().getDatabookStore().getAllPostsByUser(username);
Filters.filterPostsPerContext(posts, context);
Filters.hideSensitiveInformation(posts, caller.getClient().getId());
responseBean.setResult(posts);
responseBean.setMessage("");
responseBean.setSuccess(true);
}catch(Exception e){
logger.error("Unable to retrieve such feeds.", e);
responseBean.setMessage(e.getMessage());
responseBean.setSuccess(false);
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("get-posts-user-quantity/")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes ({
@ResponseCode ( code = 200, condition = "Successful retrieval of posts, reported in the 'result' field of the returned object"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* Retrieve a given quantity of latest user's posts
* @param quantity the number of latest post to get
* @return all posts of the auth token's owner in the context identified by the token, in reverse chronological order up to quantity (at most)
* @throws ValidationException
*/
public Response getQuantityPostsByUser(
@DefaultValue("10")
@QueryParam("quantity")
@Min(value=0, message="quantity cannot be negative")
int quantity) throws ValidationException{
Caller caller = AuthorizationProvider.instance.get();
String username = caller.getClient().getId();
String context = ScopeProvider.instance.get();
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
List<Post> posts = new ArrayList<>();
// if quantity is zero, just return an empty list
if(quantity == 0){
responseBean.setSuccess(true);
return Response.status(status).entity(responseBean).build();
}
try{
logger.debug("getRecentPostsByUser first, posts made by user " + username);
posts = CassandraConnection.getInstance().getDatabookStore().getAllPostsByUser(username);
if (posts != null)
logger.debug("got " + posts.size() + " posts");
logger.debug("Retrieving last " + quantity + " posts made by user " + username + " in context = "+context);
Filters.filterPostsPerContext(posts, context);
quantity = (quantity > posts.size()) ? posts.size() : quantity;
posts = posts.subList(0, quantity);
Filters.hideSensitiveInformation(posts, caller.getClient().getId());
responseBean.setResult(posts);
responseBean.setSuccess(true);
}catch(Exception e){
logger.error("Unable to retrieve such posts.", e);
responseBean.setMessage(e.getMessage());
responseBean.setSuccess(false);
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@POST
@Path("write-post-user")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes ({
@ResponseCode ( code = 201, condition = "Successfull created, the new post is reported in the 'result' field of the returned object"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* Create a new user post having as owner the auth token's owner
* @param post The post to be written
* @return
* @throws ValidationException
*/
public Response writePostUser(
@NotNull(message="Post to write is missing")
@Valid
PostInputBean post) throws ValidationException{
logger.debug("Request of writing a feed coming from user " + post);
Caller caller = AuthorizationProvider.instance.get();
String username = caller.getClient().getId();
String context = ScopeProvider.instance.get();
ResponseBean responseBean = new ResponseBean();
Status status = Status.CREATED;
// parse
String postText = post.getText();
String previewTitle = post.getPreviewtitle();
String previewDescription = post.getPreviewdescription();
String previewHost = post.getPreviewhost();
String previewUrl = post.getPreviewurl();
String httpImageUrl = post.getHttpimageurl();
boolean enableNotification = post.isEnablenotification();
// convert enablenotification parameter
if(enableNotification)
logger.info("Enable notification for this user post.");
else
logger.info("Disable notification for this user post.");
// try to share
logger.debug("Trying to share user post...");
Feed res = SocialUtils.shareUserUpdate(
username,
postText,
context,
previewTitle,
previewDescription,
previewHost,
previewUrl,
httpImageUrl,
enableNotification
);
if(res != null){
logger.debug("Post correctly written by user " + username);
responseBean.setResult(res);
responseBean.setSuccess(true);
return Response.status(status).entity(responseBean).build();
}
logger.error("Unable to write post.");
responseBean.setMessage("Unable to write post");
responseBean.setSuccess(false);
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("get-posts-app/")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes ({
@ResponseCode ( code = 200, condition = "Successfull created, the new post is reported in the 'result' field of the returned object"),
@ResponseCode ( code = 403, condition = "\"There is no application profile with such token"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* Retrieve the application's posts
* @return the application's posts belonging to the token's owner (i.e., an application)"
*/
public Response getAllPostsByApp() {
Caller caller = AuthorizationProvider.instance.get();
String appId = caller.getClient().getId();
String context = ScopeProvider.instance.get();
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
// check if the token actually matches an application
ApplicationProfile appProfile = SocialUtils.getProfileFromInfrastrucure(appId, context);
if(appProfile == null){
logger.error("The given token is not belonging to an application!!!");
status = Status.FORBIDDEN;
responseBean.setSuccess(false);
responseBean.setMessage(ErrorMessages.NOT_APP_TOKEN);
return Response.status(status).entity(responseBean).build();
}
try{
logger.debug("Retrieving posts for app with id " + appId);
List<Post> feeds = CassandraConnection.getInstance().getDatabookStore().getAllPostsByApp(appId);
Filters.filterPostsPerContext(feeds, context);
responseBean.setSuccess(true);
responseBean.setResult(feeds);
}catch(Exception e){
logger.error("Unable to retrieve such feeds.", e);
responseBean.setMessage(e.getMessage());
responseBean.setSuccess(false);
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@POST
@Path("write-post-app")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes ({
@ResponseCode ( code = 200, condition = "Successfull created, the new post is reported in the 'result' field of the returned object"),
@ResponseCode ( code = 403, condition = "\"There is no application profile with such token"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* Create a new application post having as owner-application the token's owner (the IAM Client)
* @param post The post to be written
* @return
*/
public Response writePostApp(
@NotNull(message="Post to write is null")
@Valid
PostInputBean post){
Caller caller = AuthorizationProvider.instance.get();
String appId = caller.getClient().getId();
String context = ScopeProvider.instance.get();
ResponseBean responseBean = new ResponseBean();
Status status = Status.CREATED;
logger.debug("Request of writing a feed coming from an application.");
// check if the token actually matches an application profile
ApplicationProfile appProfile = SocialUtils.getProfileFromInfrastrucure(appId, context);
if(appProfile == null){
logger.error("The given token doesn't belong to an application!!!");
responseBean.setSuccess(false);
responseBean.setMessage(ErrorMessages.NOT_APP_TOKEN);
status = Status.FORBIDDEN;
return Response.status(status).entity(responseBean).build();
}
// parse
String postText = post.getText();
String previewTitle = post.getPreviewtitle();
String previewDescription = post.getPreviewdescription();
String httpImageUrl = post.getHttpimageurl();
boolean enableNotification = post.isEnablenotification();
String params = post.getParams();
// convert enablenotification parameter
if(enableNotification)
logger.debug("Enable notification for this application post.");
else
logger.debug("Disable notification for this application post.");
// write feed + notification if it is the case
Feed written = SocialUtils.shareApplicationUpdate(
postText,
params,
previewTitle,
previewDescription,
httpImageUrl,
appProfile,
caller,
enableNotification
);
if(written != null){
responseBean.setResult(written);
responseBean.setSuccess(true);
return Response.status(status).entity(responseBean).build();
}
logger.error("Unable to write post.");
responseBean.setMessage("Unable to write post");
responseBean.setSuccess(false);
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("get-posts-vre/")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes ({
@ResponseCode ( code = 201, condition = "Sccessfull retrieved posts, they are reported in the 'result' field of the returned object"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
*
* @return all the posts in the context bound to the auth token
*/
public Response getAllPostsByVRE() {
String context = ScopeProvider.instance.get();
Caller caller = AuthorizationProvider.instance.get();
logger.debug("Retrieving all posts coming from vre = " + context);
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
try{
List<Post> posts = CassandraConnection.getInstance().getDatabookStore().getAllPostsByVRE(context);
Filters.hideSensitiveInformation(posts, caller.getClient().getId());
responseBean.setResult(posts);
responseBean.setSuccess(true);
}catch(Exception e){
logger.error("Unable to retrieve posts for vre = " + context, e);
status = Status.INTERNAL_SERVER_ERROR;
responseBean.setMessage(e.toString());
responseBean.setSuccess(false);
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("get-recent-posts-vre-by-range/")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes ({
@ResponseCode ( code = 201, condition = "Sccessfull retrieved posts, they are reported in the 'result' field of the returned object"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* return the most recent posts for this vre up to quantity param and the last index of the posts in the timeline
* lastReturnedPostTimelineIndex is useful to know from where to start the range the next time you ask, because there are deletions
*
* @param from the range start (most recent feeds for this vre) has to be greater than 0
* @param quantity the number of most recent feeds for this vre starting from "from" param
* @return a <class>RangePosts</class> containing of most recent feeds for this vre
* @throws FeedTypeNotFoundException
* @throws PrivacyLevelTypeNotFoundException
* @throws ColumnNameNotFoundException
*/
public Response getRecentPostsByVREAndRange(
@QueryParam("from")
@Min(value=1, message="from cannot be negative")
int from,
@QueryParam("quantity")
@Min(value=1, message="quantity cannot be negative")
int quantity) throws ValidationException {
String context = ScopeProvider.instance.get();
Caller caller = AuthorizationProvider.instance.get();
logger.debug("Retrieving all posts coming from vre = " + context);
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
try{
RangePosts rangePosts = CassandraConnection.getInstance().getDatabookStore().getRecentPostsByVREAndRange(context, from, quantity);
Filters.hideSensitiveInformation(rangePosts.getPosts(), caller.getClient().getId());
responseBean.setResult(rangePosts);
responseBean.setSuccess(true);
}catch(Exception e){
logger.error("Unable to retrieve posts for vre = " + context, e);
status = Status.INTERNAL_SERVER_ERROR;
responseBean.setMessage(e.toString());
responseBean.setSuccess(false);
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("get-posts-by-hashtag/")
@Produces({MediaType.APPLICATION_JSON})
@StatusCodes ({
@ResponseCode ( code = 201, condition = "Sccessfull retrieved posts, they are reported in the 'result' field of the returned object"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* Retrieve posts containing the hashtag in the context bound to the auth token
* @param hashtag he hashtag to be contained within the posts
* @return the posts in the context bound to the auth token matching the hashtag
* @throws ValidationException
*/
public Response getPostsByHashTags(
@QueryParam("hashtag")
@NotNull(message="hashtag cannot be missing")
String hashtag) throws ValidationException {
Caller caller = AuthorizationProvider.instance.get();
String username = caller.getClient().getId();
String context = ScopeProvider.instance.get();
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
logger.info("User " + username + " has requested posts containing hashtag " + hashtag + " in context " + context);
try{
DatabookStore datastore = CassandraConnection.getInstance().getDatabookStore();
List<Post> posts = datastore.getVREPostsByHashtag(context, hashtag);
Filters.hideSensitiveInformation(posts, caller.getClient().getId());
responseBean.setResult(posts);
responseBean.setSuccess(true);
}catch(Exception e){
logger.error("Failed to retrieve hashtags", e);
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("get-id-liked-posts/")
@Produces({MediaType.APPLICATION_JSON})
@StatusCodes ({
@ResponseCode ( code = 201, condition = "Sccessfull retrieved ids, they are reported in the 'result' field of the returned object"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* Retrieve ids (UUID) of the liked by the user
* @return ids (UUID) of the liked by the user in the context bound to the auth token
*/
public Response getAllLikedPostIdsByUser() {
Caller caller = AuthorizationProvider.instance.get();
String username = caller.getClient().getId();
String context = ScopeProvider.instance.get();
logger.debug("Retrieving all liked posts for user with id " + username + " in context " + context);
List<String> retrievedLikedPostsIds = null;
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
try{
DatabookStore datastore = CassandraConnection.getInstance().getDatabookStore();
retrievedLikedPostsIds = datastore.getAllLikedPostIdsByUser(username);
Filters.filterFeedsPerContextById(retrievedLikedPostsIds, context);
responseBean.setResult(retrievedLikedPostsIds);
responseBean.setSuccess(true);
logger.debug("Ids of liked posts by " + username + " retrieved");
}catch(Exception e){
logger.error("Unable to read such ids of liked Posts.", e);
responseBean.setMessage(e.getMessage());
responseBean.setSuccess(false);
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("get-liked-posts/")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes ({
@ResponseCode ( code = 200, condition = "Successfull retrieved posts, they are reported in the 'result' field of the returned object"),
@ResponseCode ( code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
/**
* Retrieve posts liked by the user
* @param limit The maximum number of posts to be retrieved
* @return posts liked by the user (up to a given quantity) in the context bound to the auth token
* @throws ValidationException
*/
public Response getAllLikedPostsByUser(
@DefaultValue("10")
@QueryParam("limit")
@Min(message="limit cannot be negative", value = 0)
int limit) throws ValidationException{
Caller caller = AuthorizationProvider.instance.get();
String username = caller.getClient().getId();
String context = ScopeProvider.instance.get();
List<Post> retrievedLikedPosts = null;
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
try{
logger.debug("Retrieving " + limit + " liked posts for user with id " + username + " in context " + context);
retrievedLikedPosts = CassandraConnection.getInstance().getDatabookStore().getAllLikedPostsByUser(username, limit);
Filters.filterPostsPerContext(retrievedLikedPosts, context);
Filters.hideSensitiveInformation(retrievedLikedPosts, caller.getClient().getId());
responseBean.setResult(retrievedLikedPosts);
responseBean.setSuccess(true);
logger.debug("Liked posts by " + username + " retrieved");
}catch(Exception e){
logger.error("Unable to read such liked posts.", e);
responseBean.setMessage(e.getMessage());
responseBean.setSuccess(false);
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
}