Added code to create a post when a new product is added to the catalogue. Searchable field is set to true when the created dataset is private

git-svn-id: http://svn.d4science-ii.research-infrastructures.eu/gcube/trunk/portlets/widgets/ckan-metadata-publisher-widget@133744 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
Costantino Perciante 2016-10-28 16:47:40 +00:00
parent 173933bc5c
commit efaa3669fe
6 changed files with 374 additions and 7 deletions

View File

@ -88,7 +88,7 @@ public class TagsPanel extends Composite{
}
// ckan accepts only alphanumeric values
String[] subTags = itemBox.getValue().split(" ");
String[] subTags = itemBox.getValue().trim().split(" ");
if(subTags.length == 1){
if(!subTags[0].matches(REGEX_TAG))
return;
@ -102,7 +102,7 @@ public class TagsPanel extends Composite{
}
}
final String value = itemBox.getValue();
final String value = itemBox.getValue().trim();
final ListItem displayItem = new ListItem();
displayItem.setStyleName("tag-style");
Span tagText = new Span(itemBox.getValue());
@ -134,7 +134,7 @@ public class TagsPanel extends Composite{
return;
// ckan accepts only alphanumeric values
String[] subTags = tag.split(" ");
String[] subTags = tag.trim().split(" ");
if(subTags.length == 1){
if(!subTags[0].matches(REGEX_TAG))
return;
@ -173,7 +173,7 @@ public class TagsPanel extends Composite{
*/
private void removeTag(ListItem displayItem, String value) {
tagsList.remove(value);
tagsList.remove(value.trim());
tagsPanel.remove(displayItem);
}

View File

@ -389,10 +389,33 @@ public class CKANPublisherServicesImpl extends RemoteServiceServlet implements C
// start a thread that will associate this dataset with the group
if(toCreate.getChosenProfile() != null){
AssociationToGroupThread thread = new AssociationToGroupThread(toCreate.getChosenProfile(), datasetId, userName, utils, organizationNameOrId);
thread.start();
AssociationToGroupThread threadAssociationToGroup =
new AssociationToGroupThread(
toCreate.getChosenProfile(),
datasetId,
userName,
utils,
organizationNameOrId
);
threadAssociationToGroup.start();
}
// ask token for this context
// launch notification thread
WritePostCatalogueManagerThread threadWritePost =
new WritePostCatalogueManagerThread(
userName,
scope,
toCreate.getTitle(),
datasetUrl,
false,
toCreate.getTags(),
toCreate.getAuthorFullName()
);
threadWritePost.start();
return toCreate;

View File

@ -0,0 +1,89 @@
package org.gcube.portlets.widgets.ckandatapublisherwidget.server;
import static org.gcube.common.authorization.client.Constants.authorizationService;
import java.util.ArrayList;
import java.util.List;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.authorization.library.provider.UserInfo;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Write a post in a VRe
* @author Costantino Perciante at ISTI-CNR
* (costantino.perciante@isti.cnr.it)
*/
public class WritePostCatalogueManagerThread extends Thread {
private static final Logger logger = LoggerFactory.getLogger(WritePostCatalogueManagerThread.class);
private String username;
private String scope;
private String productTitle;
private String productUrl;
private boolean enableNotification;
private List<String> hashtags;
String userFullName;
/**
* @param token
* @param scope
* @param productTitle
* @param productUrl
* @param enableNotification
* @param hashtags
* @param userFullName
*/
public WritePostCatalogueManagerThread(
String username, String scope,
String productTitle, String productUrl, boolean enableNotification,
List<String> hashtags, String userFullName) {
super();
this.username = username;
this.scope = scope;
this.productTitle = productTitle;
this.productUrl = productUrl;
this.enableNotification = enableNotification;
this.hashtags = hashtags;
this.userFullName = userFullName;
}
@Override
public void run() {
try{
// evaluate user's token for this scope
String token = authorizationService().generateUserToken(new UserInfo(username, new ArrayList<String>()), scope);
logger.info("Started request to write application post "
+ "for new product created. Scope is " + scope + " and "
+ "token is " + token.substring(0, 10) + "****************");
// set token and scope
ScopeProvider.instance.set(scope);
SecurityTokenProvider.instance.set(token);
// write
Utils.writeProductPost(
productTitle,
productUrl,
userFullName,
hashtags,
enableNotification
);
}catch(Exception e){
logger.error("Failed to write the post because of the following error ", e);
}finally{
// remove token and scope
SecurityTokenProvider.instance.reset();
ScopeProvider.instance.reset();
}
}
}

View File

@ -0,0 +1,104 @@
package org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils;
import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
import java.util.Iterator;
import java.util.List;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.resources.discovery.client.api.DiscoveryClient;
import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Retrieves the base url of the social-networking service in the scope provided
* @author Costantino Perciante at ISTI-CNR
* (costantino.perciante@isti.cnr.it)
*/
public class ServiceEndPointReaderSocial {
private String basePath = null;
private static Logger logger = LoggerFactory.getLogger(ServiceEndPointReaderSocial.class);
private final static String RUNTIME_RESOURCE_NAME = "SocialNetworking";
private final static String CATEGORY = "Portal";
public ServiceEndPointReaderSocial(String context){
if(context == null || context.isEmpty())
throw new IllegalArgumentException("A valid context is needed to discover the service");
String oldContext = ScopeProvider.instance.get();
ScopeProvider.instance.set(context);
try{
List<ServiceEndpoint> resources = getConfigurationFromIS();
if (resources.size() == 0){
logger.error("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME +" and Category " + CATEGORY + " in this scope.");
throw new Exception("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME +" and Category " + CATEGORY + " in this scope.");
}
else {
for (ServiceEndpoint res : resources) {
Iterator<AccessPoint> accessPointIterator = res.profile().accessPoints().iterator();
while (accessPointIterator.hasNext()) {
ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.AccessPoint) accessPointIterator
.next();
// get base path
basePath = accessPoint.address();
// break
break;
}
}
}
}catch(Exception e){
logger.error("Unable to retrieve such service endpoint information!", e);
}finally{
if(oldContext != null && !oldContext.equals(context))
ScopeProvider.instance.set(oldContext);
}
logger.info("Found base path " + basePath + " for the service");
}
/**
* Retrieve endpoints information from IS for the Service endpoint
* @return list of endpoints
* @throws Exception
*/
private List<ServiceEndpoint> getConfigurationFromIS() throws Exception{
SimpleQuery query = queryFor(ServiceEndpoint.class);
query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_RESOURCE_NAME +"'");
query.addCondition("$resource/Profile/Category/text() eq '"+ CATEGORY +"'");
DiscoveryClient<ServiceEndpoint> client = clientFor(ServiceEndpoint.class);
List<ServiceEndpoint> toReturn = client.submit(query);
return toReturn;
}
/**
* Get the base path of the social networking service
* @return
*/
public String getBasePath() {
return basePath;
}
}

View File

@ -1,5 +1,8 @@
package org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -7,7 +10,14 @@ import java.util.Map;
import javax.servlet.http.HttpSession;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.gcube.application.framework.core.session.ASLSession;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.homelibrary.home.exceptions.InternalErrorException;
import org.gcube.common.homelibrary.home.workspace.WorkspaceItem;
import org.gcube.common.homelibrary.home.workspace.folder.items.GCubeItem;
@ -41,6 +51,8 @@ import org.gcube.vomanagement.usermanagement.model.GCubeRole;
import org.gcube.vomanagement.usermanagement.model.GatewayRolesNames;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.trentorise.opendata.jackan.model.CkanOrganization;
/**
@ -51,6 +63,11 @@ public class Utils {
// Logger
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Utils.class);
private static final String APPLICATION_ID_CATALOGUE_MANAGER = "org.gcube.datacatalogue.DataCatalogueManager";
private static final String NOTIFICATION_MESSAGE = "Dear members,<br>The product <b></em>PRODUCT_TITLE</em></b> has been just published by <b>USER_FULLNAME</b>.<br>You can find it here: PRODUCT_URL <br>";
private static final String SOCIAL_SERVICE_APPLICATION_TOKEN = "2/tokens/generate-application-token/";
private static final String SOCIAL_SERVICE_WRITE_APPLICATION_POST = "2/posts/write-post-app/";
private static final String MEDIATYPE_JSON = "application/json";
/** Gets the gcube item properties.
*
@ -342,4 +359,121 @@ public class Utils {
return beans;
}
}
/**
* Send notification to vre members about the created product by writing a post.
* @param productName the title of the product
* @param productUrl the url of the product
* @param hashtags a list of product's hashtags
*/
public static void writeProductPost(String productName, String productUrl, String userFullname, List<String> hashtags, boolean enablePostNotification){
// discover service endpoint for the social networking library
String currentScope = ScopeProvider.instance.get();
String tokenUser = SecurityTokenProvider.instance.get();
logger.info("Current scope for writeProductPost is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************");
String basePath = new ServiceEndPointReaderSocial(currentScope).getBasePath();
if(basePath == null){
logger.error("Unable to write a post because there is no social networking service available");
}else{
try(CloseableHttpClient client = HttpClientBuilder.create().build();){
// ask token application
HttpPost postRequest = new HttpPost(basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser);
StringEntity input = new StringEntity("{\"app_id\":\"" + APPLICATION_ID_CATALOGUE_MANAGER + "\"}");
input.setContentType(MEDIATYPE_JSON);
postRequest.setEntity(input);
HttpResponse response = client.execute(postRequest);
logger.debug("Url is " + basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser);
if (response.getStatusLine().getStatusCode() != 201) {
throw new RuntimeException("Failed to retrieve application token : HTTP error code : "
+ response.getStatusLine().getStatusCode());
}else{
Map<String, Object> mapResponseGeneratedToken = getResponseEntityAsJSON(response);
boolean successGeneratedToken = (boolean)mapResponseGeneratedToken.get("success");
if(!successGeneratedToken){
throw new RuntimeException("Failed to generate the token for the application!"
+ " Error message is " + mapResponseGeneratedToken.get("message"));
}else{
String applicationToken = (String)mapResponseGeneratedToken.get("result");
// replace
String message = NOTIFICATION_MESSAGE.replace("PRODUCT_TITLE", productName).replace("PRODUCT_URL", productUrl).replace("USER_FULLNAME", userFullname);
if(hashtags != null && !hashtags.isEmpty())
for (String hashtag : hashtags) {
String modifiedHashtag = hashtag.replaceAll(" ", "_").replace("_+", "_");
if(modifiedHashtag.endsWith("_"))
modifiedHashtag = modifiedHashtag.substring(0, modifiedHashtag.length() - 1);
message += " #" + modifiedHashtag; // ckan accepts tag with empty spaces, we don't
}
logger.info("The post that is going to be written is -> " + message);
postRequest = new HttpPost(basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken);
input = new StringEntity("{\"text\":\"" + message + "\", \"enable_notification\" : "+ enablePostNotification+ "}");
input.setContentType(MEDIATYPE_JSON);
postRequest.setEntity(input);
response = client.execute(postRequest);
Map<String, Object> mapResponseWritePost = getResponseEntityAsJSON(response);
if (response.getStatusLine().getStatusCode() != 201)
throw new RuntimeException("Failed to write application post : HTTP error code : "
+ response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
}
}
}catch(Exception e){
logger.error("Failed to create a post", e);
}
}
}
/**
* Convert the json response to a map
* @param response
* @return
*/
public static Map<String, Object> getResponseEntityAsJSON(HttpResponse response){
Map<String, Object> toReturn = null;
HttpEntity entity = response.getEntity();
if (entity != null) {
try {
toReturn = new HashMap<String, Object>();
InputStream is = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
logger.debug("Response as string is " + sb.toString());
ObjectMapper objectMapper = new ObjectMapper();
toReturn = objectMapper.readValue(sb.toString(), HashMap.class);
logger.debug("Map is " + toReturn);
}catch(Exception e){
logger.error("Failed to read json object", e);
}
}
return toReturn;
}
}

View File

@ -1,8 +1,10 @@
package org.gcube.portlets.widgets.ckandatapublisherwidget.client;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.homelibrary.home.HomeLibrary;
import org.gcube.common.homelibrary.home.exceptions.HomeNotFoundException;
import org.gcube.common.homelibrary.home.exceptions.InternalErrorException;
@ -21,6 +23,7 @@ import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataFormat;
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataValidator;
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataVocabulary;
import org.gcube.portlets.widgets.ckandatapublisherwidget.server.CKANPublisherServicesImpl;
import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.Utils;
import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.DataType;
import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.MetaDataTypeWrapper;
import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.MetadataFieldWrapper;
@ -163,4 +166,18 @@ public class TestClass {
}
//@Test
public void testDataCatalogueManagerApplication(){
String token = "d423aed7-e9e2-424a-b9e7-2bbbd151d9c4-98187548";
String scope = "/gcube/devNext/NextNext";
ScopeProvider.instance.set(scope);
SecurityTokenProvider.instance.set(token);
Utils.writeProductPost("a great test product",
"https://next.d4science.org/group/nextnext/data-catalogue?path=/dataset/test_for_visibility",
"Costantino Perciante", Arrays.asList("tag1", "tag2", "tag33"), false);
}
}