diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index b90405e..13b3428 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -3,11 +3,11 @@ org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component
index d0ac04b..3afe46d 100644
--- a/.settings/org.eclipse.wst.common.component
+++ b/.settings/org.eclipse.wst.common.component
@@ -4,7 +4,7 @@
-
+
uses
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetService.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetService.java
index c0fcf38..d674253 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetService.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetService.java
@@ -27,22 +27,21 @@ public interface GRSFManageWidgetService extends RemoteService {
*/
String notifyProductUpdate(ManageProductBean bean) throws Exception;
- /**
- * Check that a record with such semantic identifier exists
- * @param semanticIdentifier
- * @return true or false
- */
- boolean checkSemanticIdentifierExists(String semanticIdentifier) throws Exception;
-
- /**
- * Check that a record with such semantic identifier exists in a given domain
- * @param semanticIdentifier
- * @param domain
- * @return
- * @throws Exception
- */
- boolean checkSemanticIdentifierExistsInDomain(String semanticIdentifier, String domain) throws Exception;
-
+ // /**
+ // * Check that a record with such semantic identifier exists
+ // * @param semanticIdentifier
+ // * @return true or false
+ // */
+ // boolean checkSemanticIdentifierExists(String semanticIdentifier) throws Exception;
+ //
+ // /**
+ // * Check that a record with such semantic identifier exists in a given domain
+ // * @param semanticIdentifier
+ // * @param domain
+ // * @return
+ // * @throws Exception
+ // */
+ // boolean checkSemanticIdentifierExistsInDomain(String semanticIdentifier, String domain) throws Exception;
/**
* Identifier of the record (UUID)
@@ -61,4 +60,11 @@ public interface GRSFManageWidgetService extends RemoteService {
*/
String checkIdentifierExistsInDomain(String id, String domain) throws Exception;
+ /**
+ * Check if the given url for revertin the operation is valid and send the request to the knowledge base
+ * @param url
+ * @throws Exception
+ */
+ void validateRevertOperation(String url) throws Exception;
+
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetServiceAsync.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetServiceAsync.java
index 6501992..df46c05 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetServiceAsync.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/client/GRSFManageWidgetServiceAsync.java
@@ -21,11 +21,11 @@ public interface GRSFManageWidgetServiceAsync {
void isAdminUser(AsyncCallback callback);
- void checkSemanticIdentifierExists(String semanticIdentifier,
- AsyncCallback callback);
-
- void checkSemanticIdentifierExistsInDomain(String semanticIdentifier,
- String domain, AsyncCallback callback);
+ // void checkSemanticIdentifierExists(String semanticIdentifier,
+ // AsyncCallback callback);
+ //
+ // void checkSemanticIdentifierExistsInDomain(String semanticIdentifier,
+ // String domain, AsyncCallback callback);
void checkIdentifierExists(String id,
AsyncCallback callback);
@@ -33,4 +33,6 @@ public interface GRSFManageWidgetServiceAsync {
void checkIdentifierExistsInDomain(String id,
String domain, AsyncCallback callback);
+ void validateRevertOperation(String url, AsyncCallback callback);
+
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFNotificationService.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFNotificationService.java
index 9d2ebde..fe35a55 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFNotificationService.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFNotificationService.java
@@ -5,7 +5,12 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.portal.PortalContext;
+import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogue;
import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogueFactory;
import org.gcube.datacatalogue.ckanutillibrary.server.utils.UtilMethods;
@@ -22,11 +27,14 @@ import org.gcube.datacatalogue.grsf_manage_widget.shared.ex.NoGRSFRecordExceptio
import org.gcube.vomanagement.usermanagement.RoleManager;
import org.gcube.vomanagement.usermanagement.impl.LiferayRoleManager;
import org.gcube.vomanagement.usermanagement.model.GCubeTeam;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+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.model.CkanDataset;
import eu.trentorise.opendata.jackan.model.CkanPair;
import eu.trentorise.opendata.jackan.model.CkanResource;
@@ -52,7 +60,7 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
String currentScope = Utils.getCurrentContext(getThreadLocalRequest(), true);
DataCatalogue instance = null;
try{
- String scopeInWhichDiscover = discoverScope != null && !discoverScope.isEmpty() ? discoverScope : currentScope;
+ String scopeInWhichDiscover = (discoverScope != null && !discoverScope.isEmpty()) ? discoverScope : currentScope;
logger.debug("Discovering ckan utils library into scope " + scopeInWhichDiscover);
instance = DataCatalogueFactory.getFactory().getUtilsPerScope(scopeInWhichDiscover);
}catch(Exception e){
@@ -65,17 +73,29 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
@Override
public ManageProductBean getProductBeanById(String productIdentifier) throws Exception {
-
ManageProductBean toReturn = null;
+ // check into session first
+ HttpSession httpSession = getThreadLocalRequest().getSession();
+ String sessionProductKey = ScopeProvider.instance.get() + productIdentifier;
+ if(httpSession.getAttribute(sessionProductKey) != null)
+ return (ManageProductBean) httpSession.getAttribute(sessionProductKey);
+
if(!Utils.isIntoPortal()){
toReturn = new ManageProductBean();
toReturn.setCatalogueIdentifier(UUID.randomUUID().toString());
List connectTo = new ArrayList<>();
- connectTo.add(new ConnectedBean("91f1e413-dc9f-3b4e-b1c5-0e8560177253","Stock",
- "aksldsam asd", "asdasjnk:fas", UUID.randomUUID().toString(), "http://data.d4science.org/ctlg/GRSF_Admin/91f1e413-dc9f-3b4e-b1c5-0e8560177253"));
- toReturn.setConnectTo(connectTo);
+ connectTo.add(new ConnectedBean(
+ "91f1e413-dc9f-3b4e-b1c5-0e8560177253",
+ "Stock",
+ "http://data.d4science.org/ctlg/GRSF_Admin/91f1e413-dc9f-3b4e-b1c5-0e8560177253",
+ "89f1e413-dc9f-3b4e-b1c5-0e8560177254",
+ "Random title",
+ "http://data.d4science.org/ctlg/GRSF_Admin/89f1e413-dc9f-3b4e-b1c5-0e8560177254",
+ "Fishery"
+ ));
+ toReturn.setCurrentConnections(connectTo);
toReturn.setGrsfDomain("Stock");
toReturn.setGrsfType("Assessment Unit");
toReturn.setKnowledgeBaseIdentifier("91f1e413-dc9f-3b4e-b1c5-0e8560177253");
@@ -105,52 +125,63 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
}else{
- // retrieve scope per current portlet url
String scopePerCurrentUrl = Utils.getScopeFromClientUrl(getThreadLocalRequest());
DataCatalogue catalogue = getCatalogue(scopePerCurrentUrl);
String username = Utils.getCurrentUser(getThreadLocalRequest()).getUsername();
- CkanDataset record = catalogue.getDataset(productIdentifier, catalogue.getApiKeyFromUsername(username));
+ String apiKey = catalogue.getApiKeyFromUsername(username);
+ CkanDataset record = catalogue.getDataset(productIdentifier, apiKey);
// it cannot be enabled in this case ...
if(record == null)
throw new Exception("Unable to retrieve information for the selected record, sorry");
else{
- logger.debug("Trying to fetch record....");
+ logger.debug("Trying to fetch the record....");
// check it is a grsf record (Source records have a different System Type)
- String systemType = record.getExtrasAsHashMap().get(Constants.SYSTEM_TYPE_CUSTOM_KEY);
- if(systemType == null || systemType.isEmpty() || systemType.equals(Constants.SYSTEM_TYPE_FOR_SOURCES_VALUE))
- throw new NoGRSFRecordException("This is not a GRSF Record");
+ Map extrasAsMap = record.getExtrasAsHashMap();
// get extras as hashmap and pairs
List extrasAsPairs = record.getExtras();
+ String systemType = extrasAsMap.get(Constants.SYSTEM_TYPE_CUSTOM_KEY);
+ if(systemType == null || systemType.isEmpty() || systemType.equals(Constants.SYSTEM_TYPE_FOR_SOURCES_VALUE))
+ throw new NoGRSFRecordException("This is not a GRSF Record");
+
+ boolean isStock = record.getExtrasAsHashMap().get(Constants.DOMAIN_CUSTOM_KEY).contains(Product_Type.STOCK.getOrigName());
+
// fetch map for namespaces
- Map fieldsNamespacesMap = Utils.getFieldToFieldNameSpaceMapping(getThreadLocalRequest().getSession(),
- record.getExtrasAsHashMap().get(Constants.DOMAIN_CUSTOM_KEY).contains(Product_Type.STOCK.getOrigName()) ? Constants.GENERIC_RESOURCE_NAME_MAP_KEY_NAMESPACES_STOCK
- : Constants.GENERIC_RESOURCE_NAME_MAP_KEY_NAMESPACES_FISHERY);
+ Map fieldsNamespacesMap =
+ Utils.getFieldToFieldNameSpaceMapping(httpSession, isStock ?
+ Constants.GENERIC_RESOURCE_NAME_MAP_KEY_NAMESPACES_STOCK : Constants.GENERIC_RESOURCE_NAME_MAP_KEY_NAMESPACES_FISHERY);
Map> extrasWithoutNamespaces = Utils.replaceFieldsKey(extrasAsPairs, fieldsNamespacesMap);
// get extras fields (wrt the mandatory ones) to show in the management panel TODO
// Utils.getExtrasToShow();
String catalogueIdentifier = record.getId();
- String status = extrasWithoutNamespaces.get(Constants.STATUS_OF_THE_GRSF_RECORD_CUSTOM_KEY).get(0);
+ Status status = Status.fromString(extrasWithoutNamespaces.get(Constants.STATUS_OF_THE_GRSF_RECORD_CUSTOM_KEY).get(0));
String uuidKB = extrasWithoutNamespaces.get(Constants.UUID_KB_CUSTOM_KEY).get(0);
String grsfDomain = extrasWithoutNamespaces.get(Constants.DOMAIN_CUSTOM_KEY).get(0);
- logger.debug(Constants.DOMAIN_CUSTOM_KEY + " is " + grsfDomain);
if(status == null || uuidKB == null)
throw new Exception("Some information is missing in this record: Status = " + status + ", knowledge base uuid = " + uuidKB +
", and grsf domain is = " + grsfDomain);
+ if(status.equals(Status.To_be_Merged) || status.equals(Status.Rejected))
+ throw new Exception("Records under merging activity or rejected cannot be updated");
+
String semanticId = extrasWithoutNamespaces.get(Constants.GRSF_SEMANTIC_IDENTIFIER_CUSTOM_KEY).get(0);
String shortName = extrasWithoutNamespaces.get(Constants.SHORT_NAME_CUSTOM_KEY).get(0);
String grsfType = extrasWithoutNamespaces.get(Constants.GRSF_TYPE_CUSTOM_KEY).get(0);
- String grsfName = extrasWithoutNamespaces.get(grsfDomain.contains(Product_Type.STOCK.getOrigName()) ?
- Constants.STOCK_NAME_CUSTOM_KEY : Constants.FISHERY_NAME_CUSTOM_KEY).get(0);
- boolean traceabilityFlag = extrasWithoutNamespaces.get(Constants.TRACEABILITY_FLAG_CUSTOM_KEY).get(0).equalsIgnoreCase("true");
+ String recordUrl = extrasWithoutNamespaces.get(Constants.ITEM_URL_FIELD).get(0);
+ String grsfName = extrasWithoutNamespaces.get(grsfDomain.contains(Product_Type.STOCK.getOrigName()) ? Constants.STOCK_NAME_CUSTOM_KEY : Constants.FISHERY_NAME_CUSTOM_KEY).get(0);
+ boolean traceabilityFlag = false;
+ try{
+ traceabilityFlag = extrasWithoutNamespaces.get(Constants.TRACEABILITY_FLAG_CUSTOM_KEY).get(0).equalsIgnoreCase("true");
+ }catch(Exception e){
+ logger.warn("Unable to fetch traceability flag", e);
+ }
// Get similar GRSF records, if any (each of which should have name, description, url and id(i.e semantic identifier))
List similarGrsfRecordsAsStrings = extrasWithoutNamespaces.containsKey(Constants.SIMILAR_GRSF_RECORDS_CUSTOM_KEY) ? extrasWithoutNamespaces.get(Constants.SIMILAR_GRSF_RECORDS_CUSTOM_KEY): null;
@@ -160,87 +191,87 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
for (String similarGRSFRecord : similarGrsfRecordsAsStrings) {
if(similarGRSFRecord.equals(Constants.NO_SIMILAR_GRSF_RECORDS)) // stop here if there is a single element with this information
break;
-
similarRecords.add(Utils.similarGRSFRecordFromJson(similarGRSFRecord));
}
}
logger.debug("SimilarGRSFRecords are " + similarRecords);
- // get connected records
- List connectedBeansAsStrings = extrasWithoutNamespaces.containsKey(Constants.CONNECTED_CUSTOM_KEY) ? extrasWithoutNamespaces.get(Constants.CONNECTED_CUSTOM_KEY): null;
+ // get connected records (and the proposed ones)
+ List connectedBeanUrls =
+ extrasWithoutNamespaces.containsKey(Constants.CONNECTED_CUSTOM_KEY) ? extrasWithoutNamespaces.get(Constants.CONNECTED_CUSTOM_KEY): null;
- List connectedBeans = new ArrayList(0);
- if(connectedBeansAsStrings != null){
- for (String connectedBean : connectedBeansAsStrings) {
- if(connectedBean.equals(Constants.NO_CONNECTED_RECORDS)) // stop here if there is a single element with this information
- break;
- connectedBeans.add(Utils.connectedBeanRecordFromJson(connectedBean, uuidKB, grsfDomain, catalogue));
- }
- }
+ List connectedBeans = new ArrayList(0);
+ if(connectedBeanUrls != null){
+ for (String connectedBean : connectedBeanUrls) {
+ if(connectedBean.equals(Constants.NO_CONNECTED_RECORDS)) // stop here if there is a single element with this information
+ break;
+ ConnectedBean builtBean = Utils.connectedBeanRecordFromUrl(recordUrl, connectedBean, uuidKB, grsfDomain, catalogue, apiKey);
+ if(builtBean != null)
+ connectedBeans.add(builtBean);
+ }
+ }
- logger.debug("Connected records are " + connectedBeans);
+ logger.debug("Already connected records are " + connectedBeans);
- // Get sources
- List resources = record.getResources();
- List sources = new ArrayList(3);
- for (CkanResource ckanResource : resources) {
- if(Sources.getListNames().contains(ckanResource.getName()))
- sources.add(new SourceRecord(ckanResource.getName(), ckanResource.getUrl()));
- }
+ // get the connections the knowledge base suggests
+ List suggestedConnectionsByKnowledgeBase = new ArrayList(0);
+ List exploitedResourcesUrls = isStock ?
+ (extrasWithoutNamespaces.containsKey(Constants.EXPLOITING_FISHERY_JSON_KEY) ?
+ extrasWithoutNamespaces.get(Constants.EXPLOITING_FISHERY_JSON_KEY) : null):
+ (extrasWithoutNamespaces.containsKey(Constants.RESOURCES_EXPLOITED_JSON_KEY) ? extrasWithoutNamespaces.get(Constants.RESOURCES_EXPLOITED_JSON_KEY) : null);
- // set the values
- toReturn = new ManageProductBean(semanticId, catalogueIdentifier, uuidKB, grsfType,
- grsfDomain, grsfName, shortName, traceabilityFlag, Status.fromString(status), null,
- null, null, sources, similarRecords, connectedBeans, false);
+ if(exploitedResourcesUrls != null && !exploitedResourcesUrls.isEmpty()){
+ for (String exploited : exploitedResourcesUrls) {
+ ConnectedBean builtBean = Utils.connectedBeanRecordFromUrl(recordUrl, exploited, uuidKB, grsfDomain, catalogue, apiKey);
+ if(builtBean != null)
+ suggestedConnectionsByKnowledgeBase.add(builtBean);
+ }
+ }
+
+ logger.debug("Knowledge base suggests " + suggestedConnectionsByKnowledgeBase);
+
+ // Get sources
+ List resources = record.getResources();
+ List sources = new ArrayList(3);
+ for (CkanResource ckanResource : resources) {
+ if(Sources.getListNames().contains(ckanResource.getName()))
+ sources.add(new SourceRecord(ckanResource.getName(), ckanResource.getUrl()));
+ }
+
+ // set the values
+ toReturn = new ManageProductBean(semanticId, catalogueIdentifier, uuidKB, grsfType,
+ grsfDomain, grsfName, shortName, traceabilityFlag, status, recordUrl,
+ null, sources, similarRecords, connectedBeans, suggestedConnectionsByKnowledgeBase);
}
}
logger.info("Returning item bean " + toReturn);
+ httpSession.setAttribute(sessionProductKey, toReturn);
return toReturn;
}
@Override
public boolean isAdminUser() {
try{
-
Boolean inSession = (Boolean)getThreadLocalRequest().getSession().getAttribute(Constants.GRSF_ADMIN_SESSION_KEY);
-
if(inSession != null)
return inSession;
else{
- if(!Utils.isIntoPortal()){
- getThreadLocalRequest().getSession().setAttribute(Constants.GRSF_ADMIN_SESSION_KEY, true);
- return true;
- }
-
- PortalContext pContext = PortalContext.getConfiguration();
- RoleManager roleManager = new LiferayRoleManager();
- String username = pContext.getCurrentUser(getThreadLocalRequest()).getUsername();
- long userId = pContext.getCurrentUser(getThreadLocalRequest()).getUserId();
- long groupId = pContext.getCurrentGroupId(getThreadLocalRequest());
- // List vreRoles = roleManager.listRolesByUserAndGroup(userId, groupId);
- List teamRoles = roleManager.listTeamsByUserAndGroup(userId, groupId);
boolean toSetInSession = false;
- for (GCubeTeam team : teamRoles) {
- if(team.getTeamName().equals(Constants.GRSF_CATALOGUE_EDITOR_ROLE) || team.getTeamName().equals(Constants.GRSF_CATALOGUE_REVIEWER_ROLE)){
- logger.info("User " + username + " is allowed to modify GRSF records");
- toSetInSession = true;
- break;
- }
+ if(!Utils.isIntoPortal()){
+ toSetInSession = true;
+ }else{
+ PortalContext pContext = PortalContext.getConfiguration();
+ RoleManager roleManager = new LiferayRoleManager();
+ String username = pContext.getCurrentUser(getThreadLocalRequest()).getUsername();
+ long userId = pContext.getCurrentUser(getThreadLocalRequest()).getUserId();
+ long groupId = pContext.getCurrentGroupId(getThreadLocalRequest());
+ List teamRoles = roleManager.listTeamsByUserAndGroup(userId, groupId);
+ toSetInSession = isEditor(username, teamRoles) | isReviewer(username, teamRoles);
}
-
- // if(!toSetInSession)
- // for (GCubeRole gCubeTeam : vreRoles) {
- // if(gCubeTeam.getRoleName().equals(GatewayRolesNames.VRE_MANAGER.getRoleName())){
- // logger.info("User " + username + " is " + GatewayRolesNames.VRE_MANAGER.getRoleName());
- // toSetInSession = true;
- // break;
- // }
- // }
-
getThreadLocalRequest().getSession().setAttribute(Constants.GRSF_ADMIN_SESSION_KEY, toSetInSession);
return toSetInSession;
}
@@ -251,42 +282,132 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
}
@Override
- public boolean checkSemanticIdentifierExists(String semanticIdentifier)
- throws Exception {
+ public String notifyProductUpdate(ManageProductBean bean) throws Exception{
- return getDataset(semanticIdentifier) != null;
+ logger.info("Creating notification for the bean " + bean + " to send to the knowledge base");
+ try{
+
+ String context = Utils.getScopeFromClientUrl(getThreadLocalRequest());
+ String token = SecurityTokenProvider.instance.get();
+ DataCatalogue catalogue = getCatalogue(context);
+ String administratorFullName = Utils.getCurrentUser(getThreadLocalRequest()).getFullname();
+ String username = Utils.getCurrentUser(getThreadLocalRequest()).getUsername();
+ // check if the base url of the service is in session
+ String keyPerContext = UtilMethods.concatenateSessionKeyScope(Constants.GRSF_UPDATER_SERVICE, context);
+ HttpServletRequest threadRequest = getThreadLocalRequest();
+ String baseUrl = (String)threadRequest.getSession().getAttribute(keyPerContext);
+ if(baseUrl == null || baseUrl.isEmpty()){
+ baseUrl = GRSFUpdaterServiceClient.discoverEndPoint(context);
+ threadRequest.getSession().setAttribute(keyPerContext, baseUrl);
+ }
+
+ // remove it from the session
+ String sessionProductKey = ScopeProvider.instance.get() + bean.getCatalogueIdentifier();
+ threadRequest.getSession().removeAttribute(sessionProductKey);
+
+ return Utils.updateRecord(baseUrl, bean, catalogue, username, administratorFullName, threadRequest,
+ PortalContext.getConfiguration().getCurrentGroupId(threadRequest), context, token);
+
+ }catch(Exception e){
+ logger.error("Unable to update the product", e);
+ throw e;
+ }
}
@Override
- public boolean checkSemanticIdentifierExistsInDomain(String semanticIdentifier, String domain)
- throws Exception {
+ public void validateRevertOperation(String encryptedUrl) throws Exception {
+ PortalContext pContext = PortalContext.getConfiguration();
+ String context = Utils.getScopeFromClientUrl(getThreadLocalRequest());
+ RoleManager roleManager = new LiferayRoleManager();
+ GCubeUser user = pContext.getCurrentUser(getThreadLocalRequest());
+ String username = user.getUsername();
+ String fullName = user.getFullname();
+ long userId = pContext.getCurrentUser(getThreadLocalRequest()).getUserId();
+ long groupId = pContext.getCurrentGroupId(getThreadLocalRequest());
+ List teamRoles = roleManager.listTeamsByUserAndGroup(userId, groupId);
- CkanDataset dataset = getDataset(semanticIdentifier);
+ boolean isEditor = isEditor(username, teamRoles);
+ boolean isReviewer = isReviewer(username, teamRoles);
- // look for the right domain this time
- List extrasAsPairs = dataset.getExtras();
+ if(!(isEditor | isReviewer))
+ throw new Exception("You are not allowed to perform this operation!");
- for (CkanPair ckanPair : extrasAsPairs) {
- if(ckanPair.getKey().contains(Constants.DOMAIN_CUSTOM_KEY)){
- return ckanPair.getValue().equalsIgnoreCase(domain);
- }
+ // decrypt the url
+ RevertOperationUrl decryptedUrl = new RevertOperationUrl(encryptedUrl);
+ String adminInUrl = decryptedUrl.getAdmin();
+ String uuid = decryptedUrl.getUuid();
+
+ logger.info("User " + username + " has requested to invert an operation on record with id " + uuid + " and admin in url was " + adminInUrl);
+
+ // we need to check the timestamp (it has 24h validity)
+ boolean isValidTimestamp = decryptedUrl.isTimestampValid();
+
+ if(!isValidTimestamp)
+ throw new Exception("This operation can no longer be reverted (link expired)!");
+
+ String keyPerContext = UtilMethods.concatenateSessionKeyScope(Constants.GRSF_UPDATER_SERVICE, context);
+ String baseUrl = (String)getThreadLocalRequest().getSession().getAttribute(keyPerContext);
+ if(baseUrl == null || baseUrl.isEmpty()){
+ baseUrl = GRSFUpdaterServiceClient.discoverEndPoint(context);
+ getThreadLocalRequest().getSession().setAttribute(keyPerContext, baseUrl);
}
+ if(baseUrl == null || baseUrl.isEmpty())
+ throw new Exception("Unable to discover grsf-updater service!");
+
+ // check if it is a reviewer, than he can do what he wants (no matter the admin)
+ try(CloseableHttpClient httpClient = HttpClientBuilder.create().build();){
+ if(isReviewer){
+ GRSFUpdaterServiceClient.revertOperation(httpClient, baseUrl, fullName, uuid);
+ }else{
+
+ if(!username.equals(adminInUrl))
+ throw new Exception("You are not the editor allowed to perform this operation!");
+ else
+ GRSFUpdaterServiceClient.revertOperation(httpClient, baseUrl, fullName, uuid);
+ }
+ }catch(Exception e){
+ logger.error("Unable to update this Item ", e);
+ throw e;
+ }
+
+ }
+
+ /**
+ * Check if the current user is an editor
+ * @param username
+ * @param teamRoles
+ * @return true if he/she is an editor, false otherwise
+ */
+ private boolean isEditor(String username, List teamRoles){
+
+ for (GCubeTeam team : teamRoles) {
+ if(team.getTeamName().equals(Constants.GRSF_CATALOGUE_EDITOR_ROLE)){
+ logger.info("User " + username + " is allowed to modify GRSF records as editor");
+ return true;
+ }
+ }
return false;
}
- private CkanDataset getDataset(String semanticIdentifier) throws Exception{
-
- String scopePerCurrentUrl = Utils.getScopeFromClientUrl(getThreadLocalRequest());
- DataCatalogue catalogue = getCatalogue(scopePerCurrentUrl);
- String username = Utils.getCurrentUser(getThreadLocalRequest()).getUsername();
- CkanDataset dataset = Utils.getRecordBySemanticIdentifier(semanticIdentifier, catalogue, catalogue.getApiKeyFromUsername(username));
- return dataset;
+ /**
+ * Check if the current user is a reviewer
+ * @param username
+ * @param teamRoles
+ * @return true if he/she is an reviewer, false otherwise
+ */
+ private boolean isReviewer(String username, List teamRoles){
+ for (GCubeTeam team : teamRoles) {
+ if(team.getTeamName().equals(team.getTeamName().equals(Constants.GRSF_CATALOGUE_REVIEWER_ROLE))){
+ logger.info("User " + username + " is allowed to modify GRSF records as reviewer");
+ return true;
+ }
+ }
+ return false;
}
-
@Override
public String checkIdentifierExists(String id)
throws Exception {
@@ -295,8 +416,7 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
String username = Utils.getCurrentUser(getThreadLocalRequest()).getUsername();
CkanDataset dataset = catalogue.getDataset(id, catalogue.getApiKeyFromUsername(username));
if(dataset == null)
- throw new Exception("This record doesn't exist");
-
+ throw new Exception("A record with id " + id + " doesn't exist");
return dataset.getExtrasAsHashMap().get(Constants.ITEM_URL_FIELD);
}
@@ -308,7 +428,7 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
String username = Utils.getCurrentUser(getThreadLocalRequest()).getUsername();
CkanDataset dataset = catalogue.getDataset(id, catalogue.getApiKeyFromUsername(username));
if(dataset == null)
- throw new Exception("This record doesn't exist");
+ throw new Exception("A record with id " + id + " doesn't exist");
List extrasAsPairs = dataset.getExtras();
@@ -319,33 +439,39 @@ public class GRSFNotificationService extends RemoteServiceServlet implements GRS
}
}
- throw new Exception("This record doesn't exist in the specified domain");
+ throw new Exception("A record with id " + id + " doesn't exist in domain " + domain);
}
- @Override
- public String notifyProductUpdate(ManageProductBean bean) throws Exception{
+ // @Override
+ // public boolean checkSemanticIdentifierExists(String semanticIdentifier)
+ // throws Exception {
+ //
+ // return getDataset(semanticIdentifier) != null;
+ // }
+ //
+ // @Override
+ // public boolean checkSemanticIdentifierExistsInDomain(String semanticIdentifier, String domain)
+ // throws Exception {
+ // CkanDataset dataset = getDataset(semanticIdentifier);
+ //
+ // // look for the right domain this time
+ // List extrasAsPairs = dataset.getExtras();
+ //
+ // for (CkanPair ckanPair : extrasAsPairs) {
+ // if(ckanPair.getKey().contains(Constants.DOMAIN_CUSTOM_KEY)){
+ // return ckanPair.getValue().equalsIgnoreCase(domain);
+ // }
+ // }
+ //
+ // return false;
+ // }
- logger.info("Creating notification for the bean " + bean + " to send to the knowledge base");
- try{
-
- String context = Utils.getScopeFromClientUrl(getThreadLocalRequest());
- DataCatalogue catalogue = getCatalogue(context);
- String administratorFullName = Utils.getCurrentUser(getThreadLocalRequest()).getFullname();
- String username = Utils.getCurrentUser(getThreadLocalRequest()).getUsername();
- // check if the base url of the service is in session
- String keyPerContext = UtilMethods.concatenateSessionKeyScope(Constants.GRSF_UPDATER_SERVICE, context);
- String baseUrl = (String)getThreadLocalRequest().getSession().getAttribute(keyPerContext);
- if(baseUrl == null || baseUrl.isEmpty()){
- baseUrl = Utils.discoverEndPoint(context);
- getThreadLocalRequest().getSession().setAttribute(keyPerContext, baseUrl);
- }
- return Utils.updateRecord(baseUrl, bean, catalogue, username, administratorFullName, getThreadLocalRequest(),
- PortalContext.getConfiguration().getCurrentGroupId(getThreadLocalRequest()));
-
- }catch(Exception e){
- logger.error("Unable to update the product.." + e.getMessage());
- throw e;
- }
- }
+ // private CkanDataset getDataset(String semanticIdentifier) throws Exception{
+ // String scopePerCurrentUrl = Utils.getScopeFromClientUrl(getThreadLocalRequest());
+ // DataCatalogue catalogue = getCatalogue(scopePerCurrentUrl);
+ // String username = Utils.getCurrentUser(getThreadLocalRequest()).getUsername();
+ // CkanDataset dataset = Utils.getRecordBySemanticIdentifier(semanticIdentifier, catalogue, catalogue.getApiKeyFromUsername(username));
+ // return dataset;
+ // }
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFUpdaterServiceClient.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFUpdaterServiceClient.java
new file mode 100644
index 0000000..01ba036
--- /dev/null
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/GRSFUpdaterServiceClient.java
@@ -0,0 +1,214 @@
+package org.gcube.datacatalogue.grsf_manage_widget.server.manage;
+
+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.datacatalogue.ckanutillibrary.server.DataCatalogue;
+import org.gcube.datacatalogue.common.Constants;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.ConnectedBean;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.ManageProductBean;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.SimilarGRSFRecord;
+import org.gcube.resources.discovery.client.api.DiscoveryClient;
+import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpResponse;
+import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpPost;
+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.util.EntityUtils;
+
+/**
+ * Exploits the grsf-services-updater service's methods https://app.swaggerhub.com/apis/ymark/grsf-services-updater/1.1.0
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class GRSFUpdaterServiceClient {
+
+ private static final Logger logger = LoggerFactory.getLogger(GRSFUpdaterServiceClient.class);
+
+ /**
+ * Discover the service endpoint of the GRSF Updater service and return its url
+ * @param context
+ * @return
+ * @throws Exception
+ */
+ public static String discoverEndPoint(String context) throws Exception{
+
+ 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 '"+ Constants.SERVICE_NAME +"'");
+ query.addCondition("$resource/Profile/Category/text() eq '"+ Constants.SERVICE_CATEGORY +"'");
+ DiscoveryClient client = clientFor(ServiceEndpoint.class);
+ List resources = client.submit(query);
+
+ if (resources.size() == 0){
+ logger.error("There is no Runtime Resource having name " + Constants.SERVICE_NAME +" and Category " + Constants.SERVICE_CATEGORY + " in this scope.");
+ throw new Exception("There is no Runtime Resource having name " + Constants.SERVICE_NAME +" and Category " + Constants.SERVICE_CATEGORY + " in this scope.");
+ }
+ else {
+
+ for (ServiceEndpoint res : resources) {
+ Iterator 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);
+ throw e;
+ }finally{
+ if(oldContext != null && !oldContext.equals(context))
+ ScopeProvider.instance.set(oldContext);
+ }
+ return toReturn;
+ }
+
+ /**
+ * Send updates to the knowledge base
+ * @param httpClient
+ * @param serviceUrl
+ * @param bean
+ * @param catalogue
+ * @param username
+ * @param fullName
+ */
+ @SuppressWarnings("unchecked")
+ public static void updateKB(CloseableHttpClient httpClient, String serviceUrl, ManageProductBean bean,
+ DataCatalogue catalogue, String username, String fullName) throws Exception{
+
+ JSONObject obj = new JSONObject();
+ obj.put(Constants.ADMINISTRATOR_FULLNAME, fullName);
+ obj.put(Constants.CATALOGUE_ID, bean.getCatalogueIdentifier());
+ obj.put(Constants.KB_ID, bean.getKnowledgeBaseIdentifier());
+ obj.put(Constants.NEW_STATUS, bean.getNewStatus().toString().toLowerCase());
+ obj.put(Constants.OLD_STATUS, bean.getCurrentStatus().toString().toLowerCase());
+ obj.put(Constants.TRACEABILITY_FLAG, bean.isTraceabilityFlag());
+
+ String annotation = bean.getAnnotation();
+ if(annotation != null)
+ obj.put(Constants.ANNOTATION, annotation.replaceAll("\"", ""));
+
+ obj.put(Constants.SHORT_NAME_OLD, bean.getShortName());
+
+ if(bean.getShortNameUpdated() == null || bean.getShortNameUpdated().isEmpty())
+ bean.setShortNameUpdated(bean.getShortName());
+
+ obj.put(Constants.SHORT_NAME_NEW, bean.getShortNameUpdated());
+ obj.put(Constants.OLD_STATUS, bean.getCurrentStatus().toString().toLowerCase());
+
+ // prepare connections
+ List connections = bean.getConnections();
+ JSONArray connectionsJson = new JSONArray();
+
+ for(ConnectedBean c: connections){
+ JSONObject cc = new JSONObject();
+ if(c.isRemove() || (c.isConnect() && !c.isRemove())){ // do not send it if it needs to be unconnected but not removed
+ cc.put(Constants.SOURCE_KNOWLEDGE_BASE_ID, c.getSourceKnowledgeBaseId());
+ cc.put(Constants.DEST_KNOWLEDGE_BASE_ID, c.getDestKnowledgeBaseId());
+ cc.put(Constants.SOURCE_DOMAIN, c.getSourceDomain());
+ cc.put(Constants.CONNECTION_TO_REMOVE, c.isRemove());
+ }
+ connectionsJson.add(cc);
+ }
+ obj.put(Constants.CONNECTIONS, connectionsJson);
+
+ // prepare similar grsf records
+ List similarRecords = bean.getSimilarGrsfRecords();
+ JSONArray similarRecordsJson = new JSONArray();
+ for(SimilarGRSFRecord s: similarRecords){
+ JSONObject ss = new JSONObject();
+ ss.put(Constants.KB_ID, s.getKnowledgeBaseId());
+ ss.put(Constants.MERGE, s.isSuggestedMerge());
+ similarRecordsJson.add(ss);
+ }
+ obj.put(Constants.SIMILAR_GRSF_RECORDS, similarRecordsJson);
+
+ logger.info("Update request looks like " + obj.toJSONString());
+
+ HttpPost request = new HttpPost(serviceUrl + Constants.SERVICE_POST_UPDATER_METHOD);
+ request.setHeader("Accept", "application/json");
+ request.setHeader("Content-type", "application/json");
+ StringEntity params = new StringEntity(obj.toJSONString());
+ request.setEntity(params);
+ HttpResponse response = httpClient.execute(request);
+
+ logger.debug("Response code is " + response.getStatusLine().getStatusCode() + " and response message is " + response.getStatusLine().getReasonPhrase());
+
+ String result = EntityUtils.toString(response.getEntity());
+ JSONParser parser = new JSONParser();
+ JSONObject parsedJSON = (JSONObject)parser.parse(result);
+
+ if(parsedJSON == null)
+ throw new Exception("There was a problem while performing this operation at knowledge base side");
+
+ if(response.getStatusLine().getStatusCode() == 200){
+ logger.info("Record updated " + bean);
+ }else if(!(boolean) parsedJSON.get(Constants.UPDATE_RESULT))
+ throw new IllegalArgumentException(
+ "Update failed for the following reason " + parsedJSON.get(Constants.ERROR_MESSAGE));
+
+ }
+
+ /**
+ * Send updates to the knowledge base
+ * @param httpClient
+ * @param serviceUrl
+ * @param bean
+ * @param catalogue
+ * @param username
+ * @param fullName
+ */
+ @SuppressWarnings("unchecked")
+ public static void revertOperation(CloseableHttpClient httpClient, String serviceUrl, String fullName, String uuid) throws Exception{
+
+ JSONObject obj = new JSONObject();
+ obj.put(Constants.ADMINISTRATOR_FULLNAME, fullName);
+ obj.put(Constants.KB_ID, uuid);
+
+ logger.info("Update request looks like " + obj.toJSONString());
+
+ HttpPost request = new HttpPost(serviceUrl + Constants.SERVICE_POST_REVERT_METHOD);
+ request.setHeader("Accept", "application/json");
+ request.setHeader("Content-type", "application/json");
+ StringEntity params = new StringEntity(obj.toJSONString());
+ request.setEntity(params);
+ HttpResponse response = httpClient.execute(request);
+
+ logger.debug("Response code is " + response.getStatusLine().getStatusCode() + " and response message is " +
+ response.getStatusLine().getReasonPhrase());
+
+ String result = EntityUtils.toString(response.getEntity());
+ JSONParser parser = new JSONParser();
+ JSONObject parsedJSON = (JSONObject)parser.parse(result);
+
+ if(parsedJSON == null)
+ throw new Exception("There was a problem while performing this operation at knowledge base side");
+
+ if(response.getStatusLine().getStatusCode() == 200){
+ logger.info("Request has been submitted");
+ }else if(!(boolean) parsedJSON.get(Constants.UPDATE_RESULT))
+ throw new IllegalArgumentException(
+ "Request failed for the following reason " + parsedJSON.get(Constants.ERROR_MESSAGE));
+
+ }
+
+}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/RevertOperationUrl.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/RevertOperationUrl.java
similarity index 72%
rename from src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/RevertOperationUrl.java
rename to src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/RevertOperationUrl.java
index b16fb6e..4b20e0b 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/RevertOperationUrl.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/RevertOperationUrl.java
@@ -1,4 +1,4 @@
-package org.gcube.datacatalogue.grsf_manage_widget.shared;
+package org.gcube.datacatalogue.grsf_manage_widget.server.manage;
import java.net.URLDecoder;
import java.net.URLEncoder;
@@ -22,11 +22,16 @@ public class RevertOperationUrl {
public static final String TIMESTAMP_QUERY_PARAM = "t";
public static final String UUID_QUERY_PARAM = "uuid";
public static final String OPERATION_REVERT_QUERY_PARAM = "operation_revert";
+ public static final long TTL = 1000 * 60 * 60 * 24;
+ /**
+ * For now only Merge can be reverted
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
public enum Operation {
- MERGE("merge"),
- DISSECT("dissect");
+ MERGE("merge");
+ // DISSECT("dissect");
private String name;
private Operation(String name) {
@@ -45,7 +50,6 @@ public class RevertOperationUrl {
private long timestamp;
private String uuid;
private Operation operation;
- private Operation op;
/**
* @param admin
@@ -55,17 +59,20 @@ public class RevertOperationUrl {
* @param op
*/
public RevertOperationUrl(String baseUrl, String admin, long timestamp, String uuid,
- Operation operation, Operation op) {
+ Operation operation) {
super();
this.baseUrl = baseUrl;
this.admin = admin;
this.timestamp = timestamp;
this.uuid = uuid;
this.operation = operation;
- this.op = op;
}
-
+ /**
+ * Build a crypted, encoded and shortened url
+ * @return
+ * @throws Exception
+ */
public String getShortUrl() throws Exception{
String query = ADMIN_QUERY_PARAM + "=" + admin + "&" + TIMESTAMP_QUERY_PARAM + "=" + timestamp +"&" + UUID_QUERY_PARAM + "=" + uuid + "&" + OPERATION_REVERT_QUERY_PARAM + "=" + operation;
@@ -131,13 +138,16 @@ public class RevertOperationUrl {
default:
break;
}
-
}
}catch(Exception e){
logger.error("Failed to parse url", e);
}
}
+
+ public boolean isTimestampValid() {
+ return TTL + this.timestamp < System.currentTimeMillis();
+ }
public String getBaseUrl() {
return baseUrl;
@@ -188,58 +198,11 @@ public class RevertOperationUrl {
this.operation = operation;
}
-
- public Operation getOp() {
- return op;
- }
-
-
- public void setOp(Operation op) {
- this.op = op;
- }
-
-
@Override
public String toString() {
return "RevertOperationUrl [baseUrl=" + baseUrl + ", admin=" + admin
+ ", timestamp=" + timestamp + ", uuid=" + uuid
- + ", operation=" + operation + ", op=" + op + "]";
+ + ", operation=" + operation + "]";
}
- // public static void main(String[] args) throws Exception {
- //
- // ScopeProvider.instance.set("/gcube/devNext/NextNext");
- // String url = "https://bluebridge.d4science.org/group/grsf_admin/data-catalogue?";
- //
- // // try encrypt + encode
- // String query = "admin=costantino.perciante&t="+ System.currentTimeMillis() +"&uuid=" + UUID.randomUUID().toString() + "&operation_revert=merge";
- // String encrypted = StringEncrypter.getEncrypter().encrypt(query);
- // encrypted = URLEncoder.encode(encrypted, "UTF-8");
- //
- //
- // String encryptedUrl = url + encrypted;
- // System.out.println("Encrypted is " + encryptedUrl);
- //
- // UrlShortener shortener = new UrlShortener();
- // String shortUrl = null;
- // try{
- // if(shortener!=null && shortener.isAvailable())
- // shortUrl = shortener.shorten(encryptedUrl);
- // }catch (Exception e) {
- // e.printStackTrace();
- // shortUrl = encryptedUrl;
- // }
- //
- // System.out.println("Encrypted is " + shortUrl);
- //
- //
- // // try decode + decrypt
- // String params = encryptedUrl.split("\\?")[1];
- // System.out.println("Params encrypted are " + params);
- // String decoded = URLDecoder.decode(encrypted, "UTF-8");
- // String decrypted = StringEncrypter.getEncrypter().decrypt(decoded);
- //
- // System.out.println("Decrypted is " + decrypted);
- //}
-
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/SocialCommunications.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/SocialCommunications.java
new file mode 100644
index 0000000..2a8a60a
--- /dev/null
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/SocialCommunications.java
@@ -0,0 +1,400 @@
+package org.gcube.datacatalogue.grsf_manage_widget.server.manage;
+
+import static org.gcube.resources.discovery.icclient.ICFactory.client;
+import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
+import org.gcube.common.resources.gcore.GCoreEndpoint;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogue;
+import org.gcube.datacatalogue.common.Constants;
+import org.gcube.datacatalogue.grsf_manage_widget.server.manage.RevertOperationUrl.Operation;
+import org.gcube.datacatalogue.grsf_manage_widget.shared.ManageProductBean;
+import org.gcube.resources.discovery.client.api.DiscoveryClient;
+import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
+import org.gcube.vomanagement.usermanagement.RoleManager;
+import org.gcube.vomanagement.usermanagement.UserManager;
+import org.gcube.vomanagement.usermanagement.impl.LiferayRoleManager;
+import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager;
+import org.gcube.vomanagement.usermanagement.model.GCubeTeam;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpEntity;
+import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpResponse;
+import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpPost;
+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.impl.client.LaxRedirectStrategy;
+import eu.trentorise.opendata.jackan.internal.org.apache.http.util.EntityUtils;
+
+
+/**
+ * For managing the different interactions with social channels (posts and mails)
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class SocialCommunications {
+
+ private static final Logger logger = LoggerFactory.getLogger(SocialCommunications.class);
+
+ // for discovering social networking service
+ private static final String resource = "jersey-servlet";
+ private static final String serviceName = "SocialNetworking";
+ private static final String serviceClass = "Portal";
+
+ // social operations
+ 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 SOCIAL_SEND_EMAIL = "/2/messages/write-message/";
+ private static final String MEDIATYPE_JSON = "application/json";
+
+ // for writing a post in the GRSF admin context
+ private static final String APPLICATION_ID_CATALOGUE_MANAGER = "org.gcube.datacatalogue.GRSFNotifier";
+
+ // emails to be sent to editors and reviewers and post to be written into the grsf admin vre
+ private static final String POST_MESSAGE = "Dear members,"
+ + "
The record 'PRODUCT_TITLE' has been just updated by USER_FULLNAME."
+ + "
You can inspect it here: PRODUCT_URL
";
+
+ private static final String EMAIL_MESSAGE_REVIEWER = "Dear GRSF Reviewer,"
+ + "
an update on the record named 'PRODUCT_TITLE' has been requested by USER_FULLNAME."
+ + "
It is available here LINK_RECORD.";
+
+ private static final String EMAIL_MESSAGE_EDITOR = "Dear USER_FULLNAME,"
+ + "
your request for the record 'PRODUCT_TITLE' has been accepted."
+ + "
It is available here LINK_RECORD.";
+
+ private static final String REVERT_LINK_PIECE = "
The request involves a merge operation. You can reject the merge by exploiting this link LINK in the following 24 hours.";
+
+ /**
+ *
+ * @param context
+ * @return
+ */
+ private static String getBaseUrlSocialService(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);
+
+ String basePath = null;
+ try{
+
+ SimpleQuery query = queryFor(GCoreEndpoint.class);
+ query.addCondition(String.format("$resource/Profile/ServiceClass/text() eq '%s'",serviceClass));
+ query.addCondition("$resource/Profile/DeploymentData/Status/text() eq 'ready'");
+ query.addCondition(String.format("$resource/Profile/ServiceName/text() eq '%s'",serviceName));
+ query.setResult("$resource/Profile/AccessPoint/RunningInstanceInterfaces//Endpoint[@EntryName/string() eq \""+resource+"\"]/text()");
+
+ DiscoveryClient client = client();
+ List endpoints = client.submit(query);
+ if (endpoints == null || endpoints.isEmpty())
+ throw new Exception("Cannot retrieve the GCoreEndpoint serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+context);
+
+
+ basePath = endpoints.get(0);
+ if(basePath==null)
+ throw new Exception("Endpoint:"+resource+", is null for serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+context);
+
+ }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");
+ return basePath;
+ }
+
+ /**
+ * Notify the users about the required changes.
+ * @param bean
+ * @param url
+ * @param username
+ * @param fullName
+ * @param hashtags
+ * @param enablePostNotification
+ */
+ @SuppressWarnings("unchecked")
+ public static void writeProductPost(ManageProductBean bean, String username, String fullName, List 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 = getBaseUrlSocialService(currentScope);
+
+ if(basePath == null){
+
+ logger.error("Unable to write a post because there is no social networking service available");
+
+ }else{
+
+ basePath = basePath.endsWith("/") ? basePath : basePath + "/";
+
+ try(CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).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 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 = POST_MESSAGE.replace("PRODUCT_TITLE", bean.getGrsfName()).replace("PRODUCT_URL", bean.getRecordUrl()).replace("USER_FULLNAME", fullName);
+
+ if(hashtags != null && !hashtags.isEmpty())
+ for (String hashtag : hashtags) {
+ String modifiedHashtag = hashtag.replaceAll(" ", "_").replace("_+", "_"); // no empty spaces allowed
+ if(modifiedHashtag.endsWith("_"))
+ modifiedHashtag = modifiedHashtag.substring(0, modifiedHashtag.length() - 1);
+ message += " #" + modifiedHashtag;
+ }
+
+ logger.info("The post that is going to be written is -> " + message);
+ postRequest = new HttpPost(basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken);
+ JSONObject object = new JSONObject();
+ object.put("text", message);
+ object.put("enable_notification", enablePostNotification);
+ input = new StringEntity(object.toJSONString());
+ input.setContentType(MEDIATYPE_JSON);
+ postRequest.setEntity(input);
+ response = client.execute(postRequest);
+
+ Map 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);
+ }
+ }
+ }
+
+ /**
+ * Send an email to the administrator as well as the
+ * @param bean
+ * @param catalogue
+ * @param username
+ * @param fullName
+ * @param isMergeInvolved
+ * @param httpSession
+ * @throws Exceptio
+ */
+ @SuppressWarnings("unchecked")
+ public static void sendEmailAdministrators(
+ ManageProductBean bean,
+ DataCatalogue catalogue,
+ String username,
+ String fullName,
+ long groupId,
+ HttpServletRequest httpServletRequest,
+ boolean isMergeInvolved) throws Exception {
+
+ // get the list of GRSF Reviewers to alert them as well
+ RoleManager roleManager = new LiferayRoleManager();
+ List teamRoles = roleManager.listTeamsByGroup(groupId);
+ List reviewers = new ArrayList<>();
+ UserManager um = new LiferayUserManager();
+
+ for(GCubeTeam tr: teamRoles){
+ if(tr.getTeamName().equals(Constants.GRSF_CATALOGUE_REVIEWER_ROLE))
+ reviewers.add(um.getUserById(tr.getUserId()).getUsername());
+ }
+
+ // if the user is a reviewer, then send the email just once
+ reviewers.remove(username);
+
+ logger.info("List of " + Constants.GRSF_CATALOGUE_REVIEWER_ROLE + " is " + reviewers);
+
+ // build the url that allows to revert the operation
+ Operation operation = Operation.MERGE;
+
+ // 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 = getBaseUrlSocialService(currentScope);
+
+ if(basePath == null){
+
+ logger.error("Unable to write a post because there is no social networking service available");
+
+ }else{
+
+ basePath = basePath.endsWith("/") ? basePath : basePath + "/";
+
+ try(CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).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 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");
+
+ String revertUrl = getEncodedUrlManage(operation, username, System.currentTimeMillis(), bean.getKnowledgeBaseIdentifier(), httpServletRequest);
+
+ String messageToEditor = (EMAIL_MESSAGE_EDITOR +
+ (isMergeInvolved? REVERT_LINK_PIECE : "")).replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getGrsfName()).replace("LINK_RECORD", bean.getRecordUrl()).replace("LINK", revertUrl);
+ String messageToReviewer = (EMAIL_MESSAGE_REVIEWER+
+ (isMergeInvolved? REVERT_LINK_PIECE : "")).replace("USER_FULLNAME", fullName).replace("PRODUCT_TITLE", bean.getGrsfName()).replace("LINK_RECORD", bean.getRecordUrl()).replace("LINK", revertUrl);
+ String subject = "Update request on GRSF Record";
+
+ // send email to the editor
+ logger.info("The message that is going to be send to the editor is\n" + messageToEditor);
+ postRequest = new HttpPost(basePath + SOCIAL_SEND_EMAIL + "?gcube-token=" + applicationToken);
+ JSONObject reqMessage = new JSONObject();
+ reqMessage.put("subject", subject);
+ reqMessage.put("body", messageToEditor);
+ JSONArray recipients = new JSONArray();
+ JSONObject recipient = new JSONObject();
+ recipient.put("id", username);
+ recipients.add(recipient);
+ reqMessage.put("recipients", recipients);
+ input = new StringEntity(reqMessage.toJSONString());
+ input.setContentType(MEDIATYPE_JSON);
+ postRequest.setEntity(input);
+ response = client.execute(postRequest);
+
+ Map mapResponseWritePost = getResponseEntityAsJSON(response);
+
+ if (response.getStatusLine().getStatusCode() != 201){
+ logger.error("Failed to send message to editor : HTTP error code : "
+ + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
+ }
+
+ // send email to the reviewers
+ logger.info("The message that is going to be send to the reviewers is\n" + messageToReviewer);
+ postRequest = new HttpPost(basePath + SOCIAL_SEND_EMAIL + "?gcube-token=" + applicationToken);
+ reqMessage = new JSONObject();
+ reqMessage.put("subject", subject);
+ reqMessage.put("body", messageToReviewer);
+ recipients = new JSONArray();
+ for(String reviewer: reviewers){
+ JSONObject recip = new JSONObject();
+ recip.put("id", reviewer);
+ recipients.add(recip);
+ }
+ reqMessage.put("recipients", recipients);
+ input = new StringEntity(reqMessage.toJSONString());
+ input.setContentType(MEDIATYPE_JSON);
+ postRequest.setEntity(input);
+ response = client.execute(postRequest);
+ mapResponseWritePost = getResponseEntityAsJSON(response);
+
+ if (response.getStatusLine().getStatusCode() != 201){
+ logger.error("Failed to send message to editor : HTTP error code : "
+ + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message"));
+ }
+
+ }
+
+ }
+
+ }catch(Exception e){
+ logger.error("Failed to create a post", e);
+ }
+ }
+
+ }
+
+ /**
+ * Create the url to be send for reverting the operation
+ * @param httpSession
+ * @return
+ * @throws Exception
+ */
+ public static String getEncodedUrlManage(Operation operation, String administrator, long timestamp, String uuid, HttpServletRequest httpServletRequest) throws Exception{
+ String clientUrl = Utils.getCurrentClientUrl(httpServletRequest).split("\\?")[0]; // ignore other parameters
+ RevertOperationUrl operationUrl = new RevertOperationUrl(clientUrl, administrator, timestamp, uuid, operation);
+ String shortUrl = operationUrl.getShortUrl();
+ return shortUrl;
+ }
+
+ /**
+ * Convert the json response to a map
+ * @param response
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ private static Map getResponseEntityAsJSON(HttpResponse response){
+
+ Map toReturn = null;
+ HttpEntity entity = response.getEntity();
+
+ if (entity != null) {
+ try {
+ toReturn = new HashMap();
+ String jsonString = EntityUtils.toString(response.getEntity());
+ logger.debug("Response as string is " + jsonString);
+ ObjectMapper objectMapper = new ObjectMapper();
+ toReturn = objectMapper.readValue(jsonString, HashMap.class);
+ logger.debug("Map is " + toReturn);
+ }catch(Exception e){
+ logger.error("Failed to read json object", e);
+ }
+ }
+
+ return toReturn;
+ }
+
+}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/Utils.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/Utils.java
index a5c388d..03360a5 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/Utils.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/server/manage/Utils.java
@@ -22,6 +22,7 @@ import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
+import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.encryption.StringEncrypter;
import org.gcube.common.portal.PortalContext;
import org.gcube.common.resources.gcore.ServiceEndpoint;
@@ -34,10 +35,9 @@ import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogueRunningCluste
import org.gcube.datacatalogue.ckanutillibrary.shared.ex.ApplicationProfileNotFoundException;
import org.gcube.datacatalogue.common.Constants;
import org.gcube.datacatalogue.common.enums.Status;
+import org.gcube.datacatalogue.grsf_manage_widget.server.manage.RevertOperationUrl.Operation;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ConnectedBean;
import org.gcube.datacatalogue.grsf_manage_widget.shared.ManageProductBean;
-import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertOperationUrl;
-import org.gcube.datacatalogue.grsf_manage_widget.shared.RevertOperationUrl.Operation;
import org.gcube.datacatalogue.grsf_manage_widget.shared.SimilarGRSFRecord;
import org.gcube.portlets.user.urlshortener.UrlShortener;
import org.gcube.resources.discovery.client.api.DiscoveryClient;
@@ -177,7 +177,9 @@ public class Utils {
}
}
logger.debug("Map is " + namespacesMap);
- httpSession.setAttribute(sessionKey, namespacesMap);
+
+ // put them into session for speeding up the operations
+ httpSession.setAttribute(sessionKey, namespacesMap);
return namespacesMap;
} catch (Exception e) {
logger.error("Error while trying to fetch applicationProfile profile from the infrastructure", e);
@@ -221,7 +223,7 @@ public class Utils {
* @param extrasAsPairs
* @return
*/
- public static Map> getExtras(List extrasAsPairs){
+ public static Map> getExtrasAsHashMap(List extrasAsPairs){
Map> toReturn = new HashMap>();
@@ -242,52 +244,6 @@ public class Utils {
return toReturn;
}
- /**
- * Discover the service endpoint and return its url
- * @param context
- * @return the url of the service on success, null otherwise
- */
- public static String discoverEndPoint(String context){
-
- 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 '"+ Constants.SERVICE_NAME +"'");
- query.addCondition("$resource/Profile/Category/text() eq '"+ Constants.SERVICE_CATEGORY +"'");
- DiscoveryClient client = clientFor(ServiceEndpoint.class);
- List resources = client.submit(query);
-
- if (resources.size() == 0){
- logger.error("There is no Runtime Resource having name " + Constants.SERVICE_NAME +" and Category " + Constants.SERVICE_CATEGORY + " in this scope.");
- throw new Exception("There is no Runtime Resource having name " + Constants.SERVICE_NAME +" and Category " + Constants.SERVICE_CATEGORY + " in this scope.");
- }
- else {
-
- for (ServiceEndpoint res : resources) {
-
- Iterator 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;
- }
-
/**
* Send an update for this bean
* @param baseUrl
@@ -297,7 +253,7 @@ public class Utils {
* @return true on success, false otherwise
*/
public static String updateRecord(String serviceUrl, ManageProductBean bean, DataCatalogue catalogue, String username,
- String fullName, HttpServletRequest httpServletRequest, long groupId) throws Exception{
+ String fullName, HttpServletRequest httpServletRequest, long groupId, String context, String token) throws Exception{
if(serviceUrl == null)
throw new IllegalArgumentException("GRSF Updater service url cannot be null");
@@ -312,14 +268,21 @@ public class Utils {
updateStatusInvolvedRecords(bean, catalogue);
// send update to the knowledge base
- updateKB(httpClient, serviceUrl, bean, catalogue, username, fullName);
+ GRSFUpdaterServiceClient.updateKB(httpClient, serviceUrl, bean, catalogue, username, fullName);
- // send email to Editors and Reviewers if merges are involved or the record was rejected (but the record was the result of a merge) TODO
- if(bean.isMergesInvolved() || bean.getNewStatus().equals(Status.Rejected))
- sendEmailAdministrators(bean, catalogue, username, fullName, groupId, httpServletRequest);
+ // manage interactions through a separated thread but set there security token and context
+ new Thread(()->{
+
+ ScopeProvider.instance.set(context);
+ SecurityTokenProvider.instance.set(token);
+
+ // send email to Editors and Reviewers
+ SocialCommunications.sendEmailAdministrators(bean, catalogue, username, fullName, groupId, httpServletRequest, bean.isMergesInvolved());
- // create a post about the operation
- createSocialPost(bean, catalogue, username, fullName);
+ // create a post about the operation
+ SocialCommunications.createSocialPost(bean, catalogue, username, fullName, hashtags);
+
+ }).start();
}catch(Exception e){
logger.error("Unable to update this Item ", e);
@@ -343,7 +306,7 @@ public class Utils {
for(SimilarGRSFRecord s: bean.getSimilarGrsfRecords()){
if(s.isSuggestedMerge()){
String productId = s.getKnowledgeBaseId();
- Map> extrasMap = getExtras(catalogue.getDataset(productId, sysApi).getExtras());
+ Map> extrasMap = getExtrasAsHashMap(catalogue.getDataset(productId, sysApi).getExtras());
extrasMap.put(Constants.STATUS_OF_THE_GRSF_RECORD_CUSTOM_KEY, Arrays.asList(Status.To_be_Merged.getOrigName()));
catalogue.patchProductCustomFields(productId, sysApi, extrasMap);
}
@@ -351,174 +314,12 @@ public class Utils {
// update the current status record
String productId = bean.getKnowledgeBaseIdentifier();
- Map> extrasMap = getExtras(catalogue.getDataset(productId, sysApi).getExtras());
+ Map> extrasMap = getExtrasAsHashMap(catalogue.getDataset(productId, sysApi).getExtras());
extrasMap.put(Constants.STATUS_OF_THE_GRSF_RECORD_CUSTOM_KEY, Arrays.asList(Status.To_be_Merged.getOrigName()));
catalogue.patchProductCustomFields(productId, sysApi, extrasMap);
}
- /**
- * Create a post with proper hashtags of the action taken by who and on which record
- * @param bean
- * @param catalogue
- * @param username
- * @param fullName
- */
- private static void createSocialPost(ManageProductBean bean,
- DataCatalogue catalogue, String username, String fullName) {
- List hashtags = getHashTagsFromActions(bean);
- // TODO
-
- }
-
- /**
- * Send an email to the administrator as well as the
- * @param bean
- * @param catalogue
- * @param username
- * @param fullName
- * @param httpSession
- * @throws Exceptio
- */
- private static void sendEmailAdministrators(ManageProductBean bean,
- DataCatalogue catalogue, String username, String fullName, long groupId, HttpServletRequest httpServletRequest) throws Exception {
-
- // get the list of GRSF Reviewers to alert as well
- RoleManager roleManager = new LiferayRoleManager();
- List teamRoles = roleManager.listTeamsByGroup(groupId);
- List reviewers = new ArrayList<>();
- UserManager um = new LiferayUserManager();
-
- for(GCubeTeam tr: teamRoles){
- if(tr.getTeamName().equals(Constants.GRSF_CATALOGUE_REVIEWER_ROLE))
- reviewers.add(um.getUserById(tr.getUserId()));
- }
-
- logger.info("List of " + Constants.GRSF_CATALOGUE_REVIEWER_ROLE + " is " + reviewers);
-
- // build the url that allows to revert the operation TODO
- Operation operation = bean.isMergesInvolved() ? Operation.MERGE : Operation.DISSECT;
- getEncodedUrlManage(operation, username, System.currentTimeMillis(), bean.getKnowledgeBaseIdentifier(), httpServletRequest);
-
- String object = "A GRSF Record has been modified";
-
- // send the emails reviewers
- String messageReviewer = "";
-
- // send email to the editor
- String messageEditor = "";
-
- // TODO
- }
-
- /**
- * Create the url to be send for reverting the operation
- * @param httpSession
- * @return
- * @throws Exception
- */
- public static String getEncodedUrlManage(Operation operation, String administrator, long timestamp, String uuid, HttpServletRequest httpServletRequest) throws Exception{
-
- String clientUrl = getCurrentClientUrl(httpServletRequest).split("\\?")[0]; // ignore other parameters
- RevertOperationUrl operationUrl = new RevertOperationUrl(clientUrl, administrator, timestamp, uuid, operation, operation);
- String shortUrl = operationUrl.getShortUrl();
- logger.info("Encrypted and shortened url " + shortUrl);
- return shortUrl;
- }
-
- /**
- * Get the list of hashtags from the actions taken onto the record
- * @param bean
- * @return
- */
- private static List getHashTagsFromActions(ManageProductBean bean) {
- // TODO Auto-generated method stub
- return null;
- }
-
- /**
- * Send updates to the knowledge base
- * @param httpClient
- * @param serviceUrl
- * @param bean
- * @param catalogue
- * @param username
- * @param fullName
- */
- @SuppressWarnings("unchecked")
- private static void updateKB(CloseableHttpClient httpClient, String serviceUrl, ManageProductBean bean,
- DataCatalogue catalogue, String username, String fullName) throws Exception{
-
- JSONObject obj = new JSONObject();
- obj.put(Constants.ADMINISTRATOR_FULLNAME, fullName);
- obj.put(Constants.CATALOGUE_ID, bean.getCatalogueIdentifier());
- obj.put(Constants.KB_ID, bean.getKnowledgeBaseIdentifier());
- obj.put(Constants.NEW_STATUS, bean.getNewStatus().toString().toLowerCase());
- obj.put(Constants.OLD_STATUS, bean.getCurrentStatus().toString().toLowerCase());
- obj.put(Constants.TRACEABILITY_FLAG, bean.isTraceabilityFlag());
-
- String annotation = bean.getAnnotation();
- if(annotation != null)
- obj.put(Constants.ANNOTATION, annotation.replaceAll("\"", ""));
-
- obj.put(Constants.SHORT_NAME_OLD, bean.getShortName());
-
- if(bean.getShortNameUpdated() == null || bean.getShortNameUpdated().isEmpty())
- bean.setShortNameUpdated(bean.getShortName());
-
- obj.put(Constants.SHORT_NAME_NEW, bean.getShortNameUpdated());
- obj.put(Constants.OLD_STATUS, bean.getCurrentStatus().toString().toLowerCase());
-
- // prepare connections
- List connections = bean.getConnectTo();
- JSONArray connectionsJson = new JSONArray();
-
- for(ConnectedBean c: connections){
- JSONObject cc = new JSONObject();
- if(c.isRemove() || (c.isConnect() && !c.isRemove())){ // do not send it if it needs to be unconnected but not removed
- cc.put(Constants.SOURCE_KNOWLEDGE_BASE_ID, c.getSourceKnowledgeBaseId());
- cc.put(Constants.DEST_KNOWLEDGE_BASE_ID, c.getDestKnowledgeBaseId());
- cc.put(Constants.SOURCE_DOMAIN, c.getSourceDomain());
- cc.put(Constants.CONNECTION_TO_REMOVE, c.isRemove());
- }
- connectionsJson.add(cc);
- }
- obj.put(Constants.CONNECTIONS, connectionsJson);
-
- // prepare similar grsf records
- List similarRecords = bean.getSimilarGrsfRecords();
- JSONArray similarRecordsJson = new JSONArray();
- for(SimilarGRSFRecord s: similarRecords){
- JSONObject ss = new JSONObject();
- ss.put(Constants.KB_ID, s.getKnowledgeBaseId());
- ss.put(Constants.MERGE, s.isSuggestedMerge());
- similarRecordsJson.add(ss);
- }
- obj.put(Constants.SIMILAR_GRSF_RECORDS, similarRecordsJson);
-
- logger.info("Update request looks like " + obj.toJSONString());
-
- HttpPost request = new HttpPost(serviceUrl + Constants.SERVICE_POST_METHOD);
- request.setHeader("Accept", "application/json");
- request.setHeader("Content-type", "application/json");
- StringEntity params = new StringEntity(obj.toJSONString());
- request.setEntity(params);
- HttpResponse response = httpClient.execute(request);
-
- logger.debug("Response code is " + response.getStatusLine().getStatusCode() + " and response message is " + response.getStatusLine().getReasonPhrase());
-
- String result = EntityUtils.toString(response.getEntity());
- JSONParser parser = new JSONParser();
- JSONObject parsedJSON = (JSONObject)parser.parse(result);
-
- if(response.getStatusLine().getStatusCode() != Constants.STATUS_SUCCESS){
- throw new Exception("Update failed at knowledge base side!");
- }else if(!(boolean) parsedJSON.get(Constants.UPDATE_RESULT))
- throw new IllegalArgumentException(
- "Update failed for the following reason " + parsedJSON.get(Constants.ERROR_MESSAGE));
-
- }
-
/**
* Get the scope in which ckan information needs to be discovered from the url
* @param httpServletRequest
@@ -649,8 +450,9 @@ public class Utils {
* @param url
* @param clg
* @return
+ * @throws Exception
*/
- public static CkanDataset getDatasetFromUrl(String url, DataCatalogue clg, String apiKey){
+ public static CkanDataset getDatasetFromUrl(String url, DataCatalogue clg, String apiKey) throws Exception{
if(url == null || url.isEmpty())
return null;
@@ -667,7 +469,7 @@ public class Utils {
return clg.getDataset(uuidFound, apiKey);
}
- return null;
+ throw new Exception("No record exists with such url " + url);
}
/**
@@ -768,27 +570,31 @@ public class Utils {
* @param json
* @param sourceIdentifier
* @param sourceDomain
+ * @param grsfDomain
* @return
* @throws ParseException
*/
- public static ConnectedBean connectedBeanRecordFromJson(String json, String sourceIdentifier, String sourceDomain,
- DataCatalogue clg) throws ParseException {
+ public static ConnectedBean connectedBeanRecordFromUrl(
+ String sourceIdentifier,
+ String sourceDomain,
+ String sourceUrl,
+ String destUrl,
+ DataCatalogue clg,
+ String apiKey) throws ParseException {
- if(json == null)
+ if(destUrl == null)
return null;
- JSONParser parser = new JSONParser();
- JSONObject object = (JSONObject)parser.parse(json);
-
- String uuidDest = (String)object.get(Constants.CONNECTED_RECORD_KNOWLEDGE_BASE_ID_JSON_KEY);
- String url = clg.getUrlFromDatasetIdOrName(uuidDest);
+ String connectedBeanUuid = Utils.getDatasetKnowledgeBaseIdFromUrl(destUrl);
+ CkanDataset destDataset = clg.getDataset(connectedBeanUuid, apiKey);
return new ConnectedBean(
sourceIdentifier,
sourceDomain,
- (String)object.get(Constants.CONNECTED_RECORD_SHORT_NAME_JSON_KEY),
- (String)object.get(Constants.CONNECTED_RECORD_SEMANTIC_IDENTIFIER_JSON_KEY),
- uuidDest,
- url
+ sourceUrl,
+ connectedBeanUuid,
+ destDataset.getTitle(),
+ destUrl,
+ destDataset.getExtrasAsHashMap().get(Constants.DOMAIN_CUSTOM_KEY)
);
}
@@ -799,10 +605,8 @@ public class Utils {
* @throws Exception
*/
public static String fetchSysAPI(String context) throws Exception{
-
DataCatalogueRunningCluster catalogueRunningInstance = new DataCatalogueRunningCluster(context);
return catalogueRunningInstance.getSysAdminToken();
-
}
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ConnectedBean.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ConnectedBean.java
index 586f5a3..ddcd7e2 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ConnectedBean.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ConnectedBean.java
@@ -12,10 +12,11 @@ public class ConnectedBean implements Serializable{
private static final long serialVersionUID = -4863776727351488790L;
private String sourceKnowledgeBaseId;
private String sourceDomain; // i.e. Stock or Fishery
- private String destShortName;
- private String destSemanticIdentifier;
- private String destKnowledgeBaseId; // the dest indentifier of a Fishery or Stock (the link is from a Stock to a Fishery and viceversa)
- private String url;
+ private String sourceUrl;
+ private String destKnowledgeBaseId; // the dest identifier of a Fishery or Stock (the link is from a Stock to a Fishery and vice versa)
+ private String destName;
+ private String destUrl;
+ private String destDomain; // please note that this MUST be different from sourceDomain
private boolean remove;
private boolean connect;
@@ -24,25 +25,26 @@ public class ConnectedBean implements Serializable{
}
/**
- *
* @param sourceKnowledgeBaseId
* @param sourceDomain
- * @param destShortName
- * @param destSemanticIdentifier
+ * @param sourceUrl
* @param destKnowledgeBaseId
- * @param url
+ * @param destTitle
+ * @param destUrl
+ * @param remove
+ * @param connect
*/
public ConnectedBean(String sourceKnowledgeBaseId, String sourceDomain,
- String destShortName, String destSemanticIdentifier,
- String destKnowledgeBaseId, String url) {
+ String sourceUrl, String destKnowledgeBaseId, String destName,
+ String destUrl, String destDomain) {
super();
this.sourceKnowledgeBaseId = sourceKnowledgeBaseId;
this.sourceDomain = sourceDomain;
- this.destShortName = destShortName;
- this.destSemanticIdentifier = destSemanticIdentifier;
+ this.sourceUrl = sourceUrl;
this.destKnowledgeBaseId = destKnowledgeBaseId;
- this.url = url;
-
+ this.destName = destName;
+ this.destUrl = destUrl;
+ this.destDomain = destDomain;
}
public boolean isConnect() {
@@ -53,21 +55,6 @@ public class ConnectedBean implements Serializable{
this.connect = connect;
}
- public String getDestShortName() {
- return destShortName;
- }
-
- public void setDestShortName(String destShortName) {
- this.destShortName = destShortName;
- }
-
- public String getDestSemanticIdentifier() {
- return destSemanticIdentifier;
- }
-
- public void setDestSemanticIdentifier(String destSemanticIdentifier) {
- this.destSemanticIdentifier = destSemanticIdentifier;
- }
public String getSourceKnowledgeBaseId() {
return sourceKnowledgeBaseId;
}
@@ -99,20 +86,45 @@ public class ConnectedBean implements Serializable{
this.remove = remove;
}
- public String getUrl() {
- return url;
+ public String getSourceUrl() {
+ return sourceUrl;
}
- public void setUrl(String url) {
- this.url = url;
+
+ public void setSourceUrl(String sourceUrl) {
+ this.sourceUrl = sourceUrl;
+ }
+
+ public String getDestName() {
+ return destName;
+ }
+
+ public void setDestName(String destName) {
+ this.destName = destName;
+ }
+
+ public String getDestUrl() {
+ return destUrl;
+ }
+
+ public void setDestUrl(String destUrl) {
+ this.destUrl = destUrl;
+ }
+
+ public String getDestDomain() {
+ return destDomain;
+ }
+
+ public void setDestDomain(String destDomain) {
+ this.destDomain = destDomain;
}
@Override
public String toString() {
return "ConnectedBean [sourceKnowledgeBaseId=" + sourceKnowledgeBaseId
- + ", sourceDomain=" + sourceDomain + ", destShortName="
- + destShortName + ", destSemanticIdentifier="
- + destSemanticIdentifier + ", destKnowledgeBaseId="
- + destKnowledgeBaseId + ", url=" + url + ", remove=" + remove
+ + ", sourceDomain=" + sourceDomain + ", sourceUrl=" + sourceUrl
+ + ", destKnowledgeBaseId=" + destKnowledgeBaseId
+ + ", destName=" + destName + ", destUrl=" + destUrl
+ + ", destDomain=" + destDomain + ", remove=" + remove
+ ", connect=" + connect + "]";
}
diff --git a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ManageProductBean.java b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ManageProductBean.java
index 14741bd..c30c02a 100644
--- a/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ManageProductBean.java
+++ b/src/main/java/org/gcube/datacatalogue/grsf_manage_widget/shared/ManageProductBean.java
@@ -1,6 +1,7 @@
package org.gcube.datacatalogue.grsf_manage_widget.shared;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -14,13 +15,13 @@ public class ManageProductBean implements Serializable{
private static final long serialVersionUID = -4882608487467259326L;
private String semanticIdentifier; // Stock id or Fishery id
- private String catalogueIdentifier; // catalogue id
+ private String catalogueIdentifier; // Catalogue id
private String knowledgeBaseIdentifier; // GRSF UUID
private String grsfType; // Fishery or Stock type (e.g., Assessment_Unit, Marine Resource and so on)
private String grsfDomain; // fishery/stock
private String grsfName; // Fishery name or Stock name
private String shortName; // it is editable ...
- private String shortNameUpdated;
+ private String shortNameUpdated; // the updated one, if any
private boolean traceabilityFlag; //from false to true etc
private Status currentStatus;
private Status newStatus;
@@ -28,20 +29,34 @@ public class ManageProductBean implements Serializable{
private Map extrasIfAvailable; // read from GRSFManageEntries resource
private List sources; // sources for this record
private List similarGrsfRecords;
- private List connectTo;
- private boolean mergesInvolved;
+ private List suggestedByKnowledgeBase;
+ private List suggestdByAdministrator = new ArrayList(0);
+ private List currentConnections;
+ private List connections; // the one to used eventually
+ private boolean mergesInvolved; // important: in this case an email must be sent to the editors/reviewers
+ private String recordUrl; // this record url
public ManageProductBean() {
super();
}
- public ManageProductBean(String semanticIdentifier,
- String catalogueIdentifier, String knowledgeBaseIdentifier,
- String grsfType, String grsfDomain, String grsfName,
- String shortName, boolean traceabilityFlag, Status currentStatus,
- Status newStatus, String annotation,
- Map extrasIfAvailable, List sources,
- List similarGrsfRecords, List connectedBeans, boolean mergesInvolved) {
+ public ManageProductBean(
+ String semanticIdentifier,
+ String catalogueIdentifier,
+ String knowledgeBaseIdentifier,
+ String grsfType,
+ String grsfDomain,
+ String grsfName,
+ String shortName,
+ boolean traceabilityFlag,
+ Status currentStatus,
+ String recordUrl,
+ Map extrasIfAvailable,
+ List sources,
+ List similarGrsfRecords,
+ List currentConnections,
+ List suggestedByKnowledgeBase
+ ) {
super();
this.semanticIdentifier = semanticIdentifier;
this.catalogueIdentifier = catalogueIdentifier;
@@ -53,13 +68,12 @@ public class ManageProductBean implements Serializable{
this.shortNameUpdated = shortName;
this.traceabilityFlag = traceabilityFlag;
this.currentStatus = currentStatus;
- this.newStatus = newStatus;
- this.annotation = annotation;
this.extrasIfAvailable = extrasIfAvailable;
this.sources = sources;
this.similarGrsfRecords = similarGrsfRecords;
- this.connectTo = connectedBeans;
- this.mergesInvolved = mergesInvolved;
+ this.currentConnections = currentConnections;
+ this.suggestedByKnowledgeBase = suggestedByKnowledgeBase;
+ this.recordUrl = recordUrl;
}
public String getSemanticIdentifier() {
@@ -184,12 +198,30 @@ public class ManageProductBean implements Serializable{
this.shortNameUpdated = shortNameUpdated;
}
- public List getConnectTo() {
- return connectTo;
+ public List getSuggestedByKnowledgeBase() {
+ return suggestedByKnowledgeBase;
}
- public void setConnectTo(List connectTo) {
- this.connectTo = connectTo;
+ public void setSuggestedByKnowledgeBase(
+ List suggestedByKnowledgeBase) {
+ this.suggestedByKnowledgeBase = suggestedByKnowledgeBase;
+ }
+
+ public List getSuggestdByAdministrator() {
+ return suggestdByAdministrator;
+ }
+
+ public void setSuggestdByAdministrator(
+ List suggestdByAdministrator) {
+ this.suggestdByAdministrator = suggestdByAdministrator;
+ }
+
+ public List getCurrentConnections() {
+ return currentConnections;
+ }
+
+ public void setCurrentConnections(List currentConnections) {
+ this.currentConnections = currentConnections;
}
public boolean isMergesInvolved() {
@@ -200,6 +232,22 @@ public class ManageProductBean implements Serializable{
this.mergesInvolved = mergesInvolved;
}
+ public String getRecordUrl() {
+ return recordUrl;
+ }
+
+ public void setRecordUrl(String recordUrl) {
+ this.recordUrl = recordUrl;
+ }
+
+ public List getConnections() {
+ return connections;
+ }
+
+ public void setConnections(List connections) {
+ this.connections = connections;
+ }
+
@Override
public String toString() {
return "ManageProductBean [semanticIdentifier=" + semanticIdentifier
@@ -212,7 +260,12 @@ public class ManageProductBean implements Serializable{
+ currentStatus + ", newStatus=" + newStatus + ", annotation="
+ annotation + ", extrasIfAvailable=" + extrasIfAvailable
+ ", sources=" + sources + ", similarGrsfRecords="
- + similarGrsfRecords + ", connectTo=" + connectTo
- + ", mergesInvolved=" + mergesInvolved + "]";
+ + similarGrsfRecords + ", suggestedByKnowledgeBase="
+ + suggestedByKnowledgeBase + ", suggestdByAdministrator="
+ + suggestdByAdministrator + ", currentConnections="
+ + currentConnections + ", connections=" + connections
+ + ", mergesInvolved=" + mergesInvolved + ", recordUrl="
+ + recordUrl + "]";
}
+
}