improved manage product facility (added enum for status, thread for patching the resource in the catalogue once it has been changed into the knowledge base)

git-svn-id: http://svn.d4science-ii.research-infrastructures.eu/gcube/trunk/portlets/user/gcube-ckan-datacatalog@139808 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
Costantino Perciante 2016-12-07 17:40:27 +00:00
parent 3774df4535
commit 049520f402
8 changed files with 228 additions and 84 deletions

View File

@ -6,7 +6,9 @@ import java.util.List;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.client.GcubeCkanDataCatalogService;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.client.GcubeCkanDataCatalogServiceAsync;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.GRSFStatus;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ManageProductBean;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ex.NoGRSFRecordException;
import com.github.gwtbootstrap.client.ui.AlertBlock;
import com.github.gwtbootstrap.client.ui.Button;
@ -82,10 +84,11 @@ public class ManageProductWidget extends Composite{
Image loadingImage;
public static final String LOADING_IMAGE_URL = GWT.getModuleBaseURL() + "../images/loader.gif";
private final static List<String> STATUS = Arrays.asList("Confirmed", "Rejected", "Pending");
private final static List<GRSFStatus> STATUS = new ArrayList<GRSFStatus>(Arrays.asList(GRSFStatus.values()));
private final static String STATUS_UPDATE_SUCCESS = "The product was correctly updated. Thanks for your collaboration!";
private final static String STATUS_UPDATE_ERROR = "Sorry, there was a problem while trying to update the status of this product";
protected static final String ERROR_ON_RETRIEVING_BEAN = "It seems there was a problem while contacting the service...";
protected static final String NO_GRSF_RECORD_BEAN = "This record is not a GRSF record. It cannot be managed";
private ManageProductBean bean;
public ManageProductWidget(String productIdentifier) {
@ -119,14 +122,14 @@ public class ManageProductWidget extends Composite{
annotationArea.setText("");
infoBlock.setVisible(false);
nameTextBox.setText(bean.getProductName());
currentStatus.setText(bean.getCurrentStatus());
currentStatus.setText(bean.getCurrentStatus().toString());
productType.setText(bean.getProductType());
List<String> statusToShow = new ArrayList<String>(STATUS);
List<GRSFStatus> statusToShow = new ArrayList<GRSFStatus>(STATUS);
statusToShow.remove(bean.getCurrentStatus());
listBoxStatus.addItem("Select the new status");
listBoxStatus.getElement().<SelectElement>cast().getOptions().getItem(0).setDisabled(true);
for (String availableStatus : statusToShow) {
listBoxStatus.addItem(availableStatus);
for (GRSFStatus availableStatus : statusToShow) {
listBoxStatus.addItem(availableStatus.toString());
}
listBoxStatus.setSelectedIndex(0);
}
@ -140,7 +143,10 @@ public class ManageProductWidget extends Composite{
@Override
public void onFailure(Throwable caught) {
showInfo(ERROR_ON_RETRIEVING_BEAN, AlertType.ERROR);
if(caught instanceof NoGRSFRecordException)
showInfo(NO_GRSF_RECORD_BEAN, AlertType.WARNING);
else
showInfo(caught.getMessage(), AlertType.ERROR);
loadingImage.setVisible(false);
}
});
@ -172,7 +178,7 @@ public class ManageProductWidget extends Composite{
// set new values
bean.setAnnotation(new HTML(annotationArea.getText().trim()).getText());
bean.setNewStatus(listBoxStatus.getSelectedItemText());
bean.setNewStatus(GRSFStatus.fromString(listBoxStatus.getSelectedItemText()));
service.notifyProductUpdate(bean, new AsyncCallback<Boolean>() {

View File

@ -16,10 +16,13 @@ import org.gcube.datacatalogue.ckanutillibrary.DataCatalogueFactory;
import org.gcube.datacatalogue.ckanutillibrary.utils.SessionCatalogueAttributes;
import org.gcube.datacatalogue.ckanutillibrary.utils.UtilMethods;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.client.GcubeCkanDataCatalogService;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.server.manage.GRSFNotificationService;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.BeanUserInOrgGroupRole;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.CkanConnectorAccessPoint;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.CkanRole;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.GRSFStatus;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ManageProductBean;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ex.NoGRSFRecordException;
import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.GroupBean;
import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.OrganizationBean;
import org.gcube.vomanagement.usermanagement.GroupManager;
@ -541,7 +544,7 @@ public class GcubeCkanDataCatalogServiceImpl extends RemoteServiceServlet implem
}
@Override
public ManageProductBean getProductBeanById(String identifier) throws Exception {
public ManageProductBean getProductBeanById(String productIdentifier) throws Exception {
ManageProductBean toReturn = null;
@ -550,22 +553,27 @@ public class GcubeCkanDataCatalogServiceImpl extends RemoteServiceServlet implem
String scopePerCurrentUrl = SessionUtil.getScopeFromClientUrl(getThreadLocalRequest());
DataCatalogue catalogue = getCatalogue(scopePerCurrentUrl);
String username = SessionUtil.getCurrentUser(getThreadLocalRequest()).getUsername();
CkanDataset product = catalogue.getDataset(identifier, catalogue.getApiKeyFromUsername(username));
CkanDataset product = catalogue.getDataset(productIdentifier, catalogue.getApiKeyFromUsername(username));
// get extras
Map<String, String> extras = product.getExtrasAsHashMap();
String status = extras.get("Status");
String uuidKB = extras.get("UUID Knowledge Base");
String productType = extras.get("Product type");
String recordType = extras.get("Record type");
String title = product.getTitle();
// it cannot be enabled in this case ...
if(recordType == null || recordType.equals("Source"))
throw new NoGRSFRecordException();
if(status == null || uuidKB == null || productType == null)
throw new Exception("Some information is missing: status = " + status + ", uuid_kb = " + uuidKB +
throw new Exception("Some information is missing in this record: Status = " + status + ", knowledge_base_uuid = " + uuidKB +
", and product type is = " + productType);
toReturn = new ManageProductBean();
toReturn.setCatalogueIdentifier(identifier);
toReturn.setCurrentStatus(status);
toReturn.setCatalogueIdentifier(productIdentifier);
toReturn.setCurrentStatus(GRSFStatus.fromString(status));
toReturn.setKnowledgeBaseIdentifier(uuidKB);
toReturn.setProductName(title);
toReturn.setProductType(productType);
@ -573,8 +581,8 @@ public class GcubeCkanDataCatalogServiceImpl extends RemoteServiceServlet implem
logger.info("Returning product bean " + toReturn);
}catch(Exception e){
logger.error("Failed to retrieve the information for the product with identifier " + identifier, e);
throw new Exception("Failed to retrieve the information for the product with identifier " + identifier + ". " + e.getMessage());
logger.error("Failed to retrieve the information for the product with identifier " + productIdentifier, e);
throw new Exception("Failed to retrieve the information for the product with identifier " + productIdentifier + ". " + e.getMessage());
}
return toReturn;

View File

@ -1,11 +0,0 @@
package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.server;
/**
* Thread used to patch a ckan product.
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
*/
public class PatchProductThread extends Thread{
// TODO
}

View File

@ -1,16 +1,18 @@
package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.server;
package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.server.manage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
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 java.util.Map;
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.datacatalogue.ckanutillibrary.DataCatalogue;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ManageProductBean;
import org.gcube.resources.discovery.client.api.DiscoveryClient;
import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
@ -21,6 +23,7 @@ import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.Htt
import eu.trentorise.opendata.jackan.internal.org.apache.http.entity.StringEntity;
import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.CloseableHttpClient;
import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.HttpClientBuilder;
import eu.trentorise.opendata.jackan.internal.org.apache.http.util.EntityUtils;
/**
* Endpoint for sending update records information to GRSF KB
@ -29,7 +32,7 @@ import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.HttpCl
public class GRSFNotificationService {
private static Logger logger = LoggerFactory.getLogger(GRSFNotificationService.class);
private static final String SERVICE_POST_METHOD= "/service/updater/post";
private static final String SERVICE_POST_METHOD = "/service/updater/post";
// request post fields
private static final String CATALOGUE_ID = "catalog_id";
@ -38,16 +41,58 @@ public class GRSFNotificationService {
private static final String STATUS = "status";
private static final String ANNOTATION = "annotation_msg";
private static final String ERROR = "error";
// the error of the update on success
private static final int STATUS_SUCCESS = 200;
// GRSF update service information
private static final String SERVICE_NAME = "GRSF Updater";
private static final String SERVICE_CATEGORY = "Service";
/**
* Discover the service endpoint and return its url
* @param context
* @return
* @return the url of the service on success, null otherwise
*/
public static String discoverEndPoint(String context){
// TODO discover the instance in this scope
return "http://62.217.127.124:8080/grsf-services-updater";
String oldContext = ScopeProvider.instance.get();
ScopeProvider.instance.set(context);
String toReturn = null;
try{
SimpleQuery query = queryFor(ServiceEndpoint.class);
query.addCondition("$resource/Profile/Name/text() eq '"+ SERVICE_NAME +"'");
query.addCondition("$resource/Profile/Category/text() eq '"+ SERVICE_CATEGORY +"'");
DiscoveryClient<ServiceEndpoint> client = clientFor(ServiceEndpoint.class);
List<ServiceEndpoint> resources = client.submit(query);
if (resources.size() == 0){
logger.error("There is no Runtime Resource having name " + SERVICE_NAME +" and Category " + SERVICE_CATEGORY + " in this scope.");
throw new Exception("There is no Runtime Resource having name " + SERVICE_NAME +" and Category " + SERVICE_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();
// return the path
toReturn = accessPoint.address();
}
}
}
}catch(Exception e){
logger.error("Unable to retrieve such service endpoint information!", e);
}finally{
if(oldContext != null && !oldContext.equals(context))
ScopeProvider.instance.set(oldContext);
}
return toReturn;
}
/**
@ -73,7 +118,7 @@ public class GRSFNotificationService {
obj.put(CATALOGUE_ID, bean.getCatalogueIdentifier());
obj.put(KB_ID, bean.getKnowledgeBaseIdentifier());
obj.put(PRODUCT_TYPE, bean.getProductType().toLowerCase());
obj.put(STATUS, bean.getNewStatus().toLowerCase());
obj.put(STATUS, bean.getNewStatus().toString().toLowerCase());
String annotation = bean.getAnnotation();
if(annotation != null)
@ -90,24 +135,17 @@ public class GRSFNotificationService {
logger.debug("Response code is " + response.getStatusLine().getStatusCode() + " and response message is " + response.getStatusLine().getReasonPhrase());
String result = convertStreamToString(response.getEntity().getContent());
String result = EntityUtils.toString(response.getEntity());
JSONParser parser = new JSONParser();
JSONObject parsedJSON = (JSONObject)parser.parse(result);
if(response.getStatusLine().getStatusCode() > STATUS_SUCCESS)
if(response.getStatusLine().getStatusCode() != STATUS_SUCCESS)
throw new IllegalArgumentException(
"Error while performing the update request: " + response.getStatusLine().getReasonPhrase() +
"and error in the result bean is " + parsedJSON.get(ERROR));
// patch the catalogue product
String apiKey = catalogue.getApiKeyFromUsername(username);
Map<String, List<String>> statusMap = new HashMap<String, List<String>>();
statusMap.put("Status", Arrays.asList(bean.getNewStatus()));
catalogue.patchProductCustomFields(bean.getCatalogueIdentifier(), apiKey, statusMap);
catalogue.removeTag(bean.getCatalogueIdentifier(), apiKey, bean.getCurrentStatus());
catalogue.addTag(bean.getCatalogueIdentifier(), apiKey, bean.getNewStatus());
catalogue.removeDatasetFromGroup(bean.getCurrentStatus().toLowerCase(), bean.getCatalogueIdentifier(), apiKey);
catalogue.assignDatasetToGroup(bean.getCurrentStatus().toLowerCase(), bean.getCatalogueIdentifier(), apiKey);
new PatchProductThread(catalogue, bean, username).start();
}catch(Exception e){
logger.error("Unable to update this record", e);
@ -116,32 +154,4 @@ public class GRSFNotificationService {
return null;
}
/**
* Convert an input stream to a string
* @param is
* @return the json string inside
*/
private static String convertStreamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,67 @@
package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.server.manage;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue;
import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ManageProductBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Thread used to patch a ckan product.
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
*/
public class PatchProductThread extends Thread{
private static Logger logger = LoggerFactory.getLogger(PatchProductThread.class);
private DataCatalogue catalogue;
private ManageProductBean bean;
private String username;
private static final String ANNOTATION_KEY = "Annotation on Status update";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
private static final String STATUS_CUSTOM_FIELD_KEY = "Status";
/**
* @param catalogue
* @param bean
*/
public PatchProductThread(DataCatalogue catalogue, ManageProductBean bean, String username) {
super();
this.catalogue = catalogue;
this.bean = bean;
this.username = username;
}
@Override
public void run() {
logger.info("Going to patch record in the catalogue with identifier " + bean.getCatalogueIdentifier() +
" from user " + username);
String apiKey = catalogue.getApiKeyFromUsername(username);
// remove the "Status" field
catalogue.removeCustomField(bean.getCatalogueIdentifier(), STATUS_CUSTOM_FIELD_KEY, bean.getCurrentStatus().toString(), apiKey);
catalogue.removeTag(bean.getCatalogueIdentifier(), apiKey, bean.getCurrentStatus().toString());
catalogue.removeDatasetFromGroup(bean.getCurrentStatus().toString().toLowerCase(), bean.getCatalogueIdentifier(), apiKey);
Map<String, List<String>> statusMap = new HashMap<String, List<String>>();
statusMap.put(STATUS_CUSTOM_FIELD_KEY, Arrays.asList(bean.getNewStatus().toString()));
statusMap.put(ANNOTATION_KEY, Arrays.asList("date = " + DATE_FORMAT.format(new Date())
+ ", admin = " + username
+ ", annotation = " + (bean.getAnnotation() != null ? bean.getAnnotation() : "none")
+ ", new status = " + bean.getNewStatus().toString()
+ ", old status = " + bean.getCurrentStatus().toString()
));
catalogue.patchProductCustomFields(bean.getCatalogueIdentifier(), apiKey, statusMap);
catalogue.addTag(bean.getCatalogueIdentifier(), apiKey, bean.getNewStatus().toString());
catalogue.assignDatasetToGroup(bean.getCurrentStatus().toString().toLowerCase(), bean.getCatalogueIdentifier(), apiKey);
logger.info("Record patched ...");
}
}

View File

@ -0,0 +1,36 @@
package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared;
/**
* Status of a grsf record.
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
*/
public enum GRSFStatus {
APPROVED("Approved"),
REJECTED("Rejected"),
PENDING("Pending"),
ARCHIVED("Archived"),
HIDDEN("Hidden");
private String asString;
private GRSFStatus(String asString) {
this.asString = asString;
}
@Override
public String toString() {
return asString;
}
public static GRSFStatus fromString(String string){
if(string == null || string.isEmpty())
return null;
for(GRSFStatus value: GRSFStatus.values())
if(value.toString().equalsIgnoreCase(string))
return value;
return null;
}
}

View File

@ -12,8 +12,8 @@ public class ManageProductBean implements Serializable{
private String productName;
private String catalogueIdentifier;
private String knowledgeBaseIdentifier;
private String currentStatus;
private String newStatus;
private GRSFStatus currentStatus;
private GRSFStatus newStatus;
private String annotation;
private String productType; // fishery/stock
@ -30,7 +30,7 @@ public class ManageProductBean implements Serializable{
* @param annotation
*/
public ManageProductBean(String productName, String catalogueIdentifier,
String knowledgeBaseIdentifier, String currentStatus, String newStatus,
String knowledgeBaseIdentifier, GRSFStatus currentStatus, GRSFStatus newStatus,
String annotation, String productType) {
super();
this.productName = productName;
@ -58,19 +58,19 @@ public class ManageProductBean implements Serializable{
this.knowledgeBaseIdentifier = knowledgeBaseIdentifier;
}
public String getCurrentStatus() {
public GRSFStatus getCurrentStatus() {
return currentStatus;
}
public void setCurrentStatus(String currentStatus) {
public void setCurrentStatus(GRSFStatus currentStatus) {
this.currentStatus = currentStatus;
}
public String getNewStatus() {
public GRSFStatus getNewStatus() {
return newStatus;
}
public void setNewStatus(String newStatus) {
public void setNewStatus(GRSFStatus newStatus) {
this.newStatus = newStatus;
}

View File

@ -0,0 +1,28 @@
package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ex;
/**
* This is thrown when the Manage product is pushed on a product that has
* a Record Type field of Source.
* @author Costantino Perciante at ISTI-CNR
* (costantino.perciante@isti.cnr.it)
*/
public class NoGRSFRecordException extends Exception {
private static final long serialVersionUID = 721315478405659218L;
private String errorMessage;
public NoGRSFRecordException() {
super();
}
public NoGRSFRecordException(String errorMessage) {
super();
this.errorMessage = errorMessage;
}
public String getErrorMessage() {
return errorMessage;
}
}