package org.gcube.gcat.social; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.List; import javax.cache.Cache; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response.Status; import org.gcube.common.gxhttp.request.GXHTTPStringRequest; import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogue; import org.gcube.gcat.oldutils.CachesManager; import org.gcube.gcat.persistence.ckan.CKAN; import org.gcube.gcat.utils.Constants; import org.gcube.gcat.utils.ContextUtility; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; /** * @author Luca Frosini (ISTI - CNR) */ public class SocialService extends Thread { private static final Logger logger = LoggerFactory.getLogger(SocialService.class); public static final String ITEM_URL = "Item URL"; // https://wiki.gcube-system.org/gcube/Social_Networking_Service#Write_application_post_2 protected static final String SOCIAL_SERVICE_WRITE_APPLICATION_POST_PATH = "/2/posts/write-post-app"; // https://wiki.gcube-system.org/gcube/Social_Networking_Service protected static final String SOCIAL_SERVICE_GET_USER_INFO_PATH = "2/users/get-profile"; // String.format(NOTIFICATION_MESSAGE, fullName, title, url) protected static final String NOTIFICATION_MESSAGE = "%s just published the item \"%s\"\n" + "Please find it at %s\n"; protected static final String RESULT_KEY = "result"; protected static final String FULLNAME_IN_PROFILE_KEY = "fullname"; protected static final String SOCIAL_POST_TEXT_KEY = "text"; protected static final String SOCIAL_POST_ENABLE_NOTIFICATION_KEY = "enable_notification"; protected static final String SOCIAL_POST_RESPONSE_SUCCESS_KEY = "success"; protected static final String SOCIAL_POST_RESPONSE_MESSAGE_KEY = "message"; protected final GcoreEndpointReaderSNL gcoreEndpointReaderSNL; protected final ObjectMapper objectMapper; protected String itemID; protected String itemURL; protected String itemTitle; protected List tags; protected JsonNode gCubeUserProfile; public SocialService() throws Exception { super(); this.gcoreEndpointReaderSNL = new GcoreEndpointReaderSNL(); this.objectMapper = new ObjectMapper(); } public String getItemID() { return itemID; } public void setItemID(String itemID) { this.itemID = itemID; } public String getItemURL() { return itemURL; } public void setItemURL(String itemURL) { this.itemURL = itemURL; } public String getItemTitle() { return itemTitle; } public void setItemTitle(String itemTitle) { this.itemTitle = itemTitle; } public List getTags() { return tags; } public void setTags(List tags) { this.tags = tags; } public void setTags(ArrayNode tags) { this.tags = new ArrayList<>(); if(tags != null && tags.size() > 0) { for(int i = 0; i < tags.size(); i++) { JsonNode jsonNode = tags.get(i); String tagName = ""; if(jsonNode.has("display_name")) { tagName = jsonNode.get("display_name").asText(); }else { tagName = jsonNode.get("name").asText(); } this.tags.add(tagName); } } } /** * Execute the GET http request at this url, and return the result as string * @return * @throws Exception */ public JsonNode getGCubeUserProfile() throws Exception { if(gCubeUserProfile==null) { String username = ContextUtility.getUsername(); gCubeUserProfile = getGCubeUserProfile(username); } return gCubeUserProfile; } protected StringBuilder getStringBuilder(InputStream inputStream) throws IOException { StringBuilder result = new StringBuilder(); try(BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { String line; while((line = reader.readLine()) != null) { result.append(line); } } return result; } protected String getResultAsString(HttpURLConnection httpURLConnection) throws IOException { int responseCode = httpURLConnection.getResponseCode(); if(responseCode >= Status.BAD_REQUEST.getStatusCode()) { Status status = Status.fromStatusCode(responseCode); InputStream inputStream = httpURLConnection.getErrorStream(); StringBuilder result = getStringBuilder(inputStream); logger.trace(result.toString()); throw new WebApplicationException(status); } InputStream inputStream = httpURLConnection.getInputStream(); String ret = getStringBuilder(inputStream).toString(); logger.trace("Got Respose is {}", ret); return ret; } protected JsonNode getGCubeUserProfile(String username) throws Exception { Cache userCache = CachesManager.getUserCache(); if(userCache.containsKey(username)) return userCache.get(username); else { String socialServiceBasePath = gcoreEndpointReaderSNL.getServiceBasePath(); GXHTTPStringRequest gxhttpStringRequest = GXHTTPStringRequest.newRequest(socialServiceBasePath); gxhttpStringRequest.header("User-Agent", Constants.CATALOGUE_NAME); gxhttpStringRequest.header("Accept", MediaType.APPLICATION_JSON); gxhttpStringRequest.path(SOCIAL_SERVICE_GET_USER_INFO_PATH); HttpURLConnection httpURLConnection = gxhttpStringRequest.get(); String ret = getResultAsString(httpURLConnection); JsonNode jsonNode = objectMapper.readTree(ret); userCache.put(username, jsonNode); return jsonNode; } } public String getFullName() throws Exception { try { if(!ContextUtility.isApplication()) { JsonNode jsonNode = getGCubeUserProfile(); JsonNode result = jsonNode.get(RESULT_KEY); return result.get(FULLNAME_IN_PROFILE_KEY).asText(); } }catch (Exception e) { logger.warn("Unable to get the full name of the requesting user via Social Service. The username corresponsing to the requester token will be used."); } return ContextUtility.getUsername(); } @Override public void run() { try { DataCatalogue dataCatalogue = CKAN.getCatalogue(); if(!dataCatalogue.isSocialPostEnabled()) { logger.info("Social Post are disabled in the context {}", ContextUtility.getCurrentContext()); return; } logger.info("Going to send Social Post about the Item {} available at {}", itemID, itemURL); boolean notifyUsers = dataCatalogue.isNotificationToUsersEnabled(); // write notification post sendSocialPost(notifyUsers); } catch(Exception e) { logger.error("Error while executing post creation actions", e); } } public void sendSocialPost(boolean notifyUsers) { try { String fullName = getFullName(); String basePath = gcoreEndpointReaderSNL.getServiceBasePath(); if(basePath == null) { logger.info("Unable to write a post because there is no social networking service available"); return; } basePath = basePath.endsWith("/") ? basePath : basePath + "/"; StringWriter messageWriter = new StringWriter(); messageWriter.append(String.format(NOTIFICATION_MESSAGE, fullName, itemTitle, itemURL)); for(String tag : tags) { tag = tag.trim(); tag = tag.replaceAll(" ", "_").replace("_+", "_"); if(tag.endsWith("_")) { tag = tag.substring(0, tag.length() - 1); } messageWriter.append("#"); messageWriter.append(tag); messageWriter.append(" "); } String message = messageWriter.toString(); logger.debug("The post that is going to be written is\n{}",message); ObjectNode objectNode = objectMapper.createObjectNode(); objectNode.put(SOCIAL_POST_TEXT_KEY, message); objectNode.put(SOCIAL_POST_ENABLE_NOTIFICATION_KEY, notifyUsers); // Do not use ApplicationMode class here because is a thread and change the current token could impact // on the other threads. GXHTTPStringRequest gxhttpStringRequest = GXHTTPStringRequest.newRequest(basePath); gxhttpStringRequest.from(Constants.CATALOGUE_NAME); gxhttpStringRequest.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); gxhttpStringRequest.setSecurityToken(Constants.getCatalogueApplicationToken()); gxhttpStringRequest.path(SOCIAL_SERVICE_WRITE_APPLICATION_POST_PATH); HttpURLConnection httpURLConnection = gxhttpStringRequest.post(objectMapper.writeValueAsString(objectNode)); String ret = getResultAsString(httpURLConnection); JsonNode jsonNode = objectMapper.readTree(ret); if(jsonNode.get(SOCIAL_POST_RESPONSE_SUCCESS_KEY).asBoolean()) { logger.info("Post written : {}", message); } else { logger.info("Failed to write the post {}. Reason {}", message, jsonNode.get(SOCIAL_POST_RESPONSE_MESSAGE_KEY).asText()); } } catch(Exception e) { logger.error("Unable to send Social Post", e); } } }