diff --git a/src/main/java/org/gcube/gcat/persistence/ckan/CKAN.java b/src/main/java/org/gcube/gcat/persistence/ckan/CKAN.java index f19b274..32086ac 100644 --- a/src/main/java/org/gcube/gcat/persistence/ckan/CKAN.java +++ b/src/main/java/org/gcube/gcat/persistence/ckan/CKAN.java @@ -247,6 +247,13 @@ public abstract class CKAN { int responseCode = httpURLConnection.getResponseCode(); if(responseCode >= Status.BAD_REQUEST.getStatusCode()) { Status status = Status.fromStatusCode(responseCode); + switch (status) { + case NOT_FOUND: + throw new NotFoundException(); + + default: + break; + } InputStream inputStream = httpURLConnection.getErrorStream(); StringBuilder result = HTTPUtility.getStringBuilder(inputStream); logger.trace(result.toString()); diff --git a/src/main/java/org/gcube/gcat/persistence/ckan/CKANInstance.java b/src/main/java/org/gcube/gcat/persistence/ckan/CKANInstance.java index 2a9f1b5..afb047e 100644 --- a/src/main/java/org/gcube/gcat/persistence/ckan/CKANInstance.java +++ b/src/main/java/org/gcube/gcat/persistence/ckan/CKANInstance.java @@ -256,6 +256,7 @@ public class CKANInstance { public boolean isModerationEnabled() { return true; + // TODO // return moderationEnabled; } diff --git a/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java b/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java index 61472dd..c27d31f 100644 --- a/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java +++ b/src/main/java/org/gcube/gcat/persistence/ckan/CKANPackage.java @@ -195,9 +195,37 @@ public class CKANPackage extends CKAN implements Moderated { if(jsonNode.has(OWNER_ORG_KEY)) { ((ObjectNode) jsonNode).remove(OWNER_ORG_KEY); } + + // Removing all Content Moderation Keys + if(jsonNode.has(EXTRAS_KEY)) { + ArrayNode extras = (ArrayNode) jsonNode.get(EXTRAS_KEY); + // It is not possible to remove the element of an array while iterating it. + // We need to create a new array only with valie elements; + ArrayNode newExtras = mapper.createArrayNode(); + boolean foundOne = false; + for(int i=0; i moderators; protected ObjectMapper objectMapper; + protected ZulipRestExecutor getZulipRestExecutor() { + ZulipAuth zulipAuth = new ZulipAuth(ContextUtility.getUsername()); + return new ZulipRestExecutor(zulipAuth.getEmail(), zulipAuth.getAPIKey(), zulipAuth.getSite()); + } + public ZulipStream() { - // TODO Get gcat email and API key from configuration - // TODO Get zulip URL from configuration - this.gCatZulipRestExecutor = new ZulipRestExecutor("gcat@d4science.com", "apikey", "https://zulip.example.com/"); this.objectMapper = new ObjectMapper(); - this.moderators = new HashSet<>(); + } + + public ZulipRestExecutor getGCatZulipRestExecutor() { + if(gCatZulipRestExecutor==null) { + ApplicationMode applicationMode = new ApplicationMode(Constants.getCatalogueApplicationToken()); + applicationMode.start(); + gCatZulipRestExecutor = getZulipRestExecutor(); + applicationMode.end(); + } + return gCatZulipRestExecutor; } public ZulipRestExecutor getUserZulipRestExecutor() { if(userZulipRestExecutor==null) { - // TODO get API key ?? - // TODO Get zulip URL from configuration - userZulipRestExecutor = new ZulipRestExecutor(ckanUser.getPortalUser().getEMail(), "apikey", "https://zulip.example.com/"); + userZulipRestExecutor = getZulipRestExecutor(); } return userZulipRestExecutor; } @@ -70,27 +86,29 @@ public class ZulipStream { protected String getStreamName() { if(streamName==null) { - streamName = String.format("Item '{}' with id '{}' moderation", itemName, itemID); + streamName = String.format("Item '%s' moderation", itemID); } return streamName; } protected Integer getStreamID() throws Exception { GetStreamID getStreamID = new GetStreamID(getStreamName()); - ZulipResponse zulipResponse = executeZulipCall(getStreamID); + ZulipResponse zulipResponse = executeZulipCall(gCatZulipRestExecutor, getStreamID); JsonNode response = zulipResponse.getResponse(); return response.get("stream_id").asInt(); } protected String getStreamDescription() { if(streamDescription==null) { - streamDescription = String.format("This stream is used to discuss about the moderation of the item '{}' with id '{}'", itemName, itemID); + streamDescription = String.format("This stream is used to discuss about the moderation of the item '%s' with id '%s'", itemName, itemID); } return streamDescription; } - protected ZulipResponse executeZulipCall(ZulipRestAPICall call) throws Exception { - String responseString = gCatZulipRestExecutor.executeCall(call); + protected ZulipResponse executeZulipCall(ZulipRestExecutor zulipRestExecutor, ZulipRestAPICall call) throws Exception { + logger.trace("Going to execute {}", call); + String responseString = zulipRestExecutor.executeCall(call); + logger.trace("Response from {} is {}", call.getClass().getSimpleName(), responseString); ZulipResponse zulipResponse = new ZulipResponse(responseString); if(zulipResponse.getResponseResult()==Result.error) { throw new InternalServerErrorException(zulipResponse.getResponseMessage()); @@ -98,6 +116,24 @@ public class ZulipStream { return zulipResponse; } + protected void renameInitialHelloTopic() throws Exception { + // TODO check if it is possible + /* + Integer streamID = getStreamID(); + + GetAllTopicsOfAStream getAllTopicsOfAStream = new GetAllTopicsOfAStream(streamID.toString()); + ZulipResponse zulipResponse = executeZulipCall(gCatZulipRestExecutor, getAllTopicsOfAStream); + JsonNode jsonNode = zulipResponse.getResponse(); + ArrayNode arrayNode = (ArrayNode) jsonNode.get(TOPICS_KEY); + String initialTopicID = null; + for(JsonNode node : arrayNode) { + if(node.get(NAME_KEY).asText().compareTo(INITIAL_TOPIC_NAME)==0) { + initialTopicID = node.get(MAX_ID_KEY).asText(); + } + } + */ + } + public void create() throws Exception { ArrayNode streamsArrayNode = objectMapper.createArrayNode(); ObjectNode streamobjectNode = objectMapper.createObjectNode(); @@ -109,7 +145,12 @@ public class ZulipStream { // Going to add the item creator String itemCreatorEmail = ckanUser.getPortalUser().getEMail(); principalsArrayNode.add(itemCreatorEmail); + + getGCatZulipRestExecutor(); + + principalsArrayNode.add(gCatZulipRestExecutor.httpController.getUserName()); // Going to add the catalogue moderators + Set moderators = getCatalogueModerators(); for(String moderator : moderators) { principalsArrayNode.add(moderator); } @@ -119,41 +160,62 @@ public class ZulipStream { postCreateStream.setInvite_only(true); postCreateStream.setAnnounce(false); - executeZulipCall(postCreateStream); + executeZulipCall(gCatZulipRestExecutor, postCreateStream); + + renameInitialHelloTopic(); postItemCreated(); - } - protected void postMessageToStream(ZulipRestExecutor zulipRestExecutor, CMItemStatus cmItemStatus, String message) { + private Set getCatalogueModerators() { + Set moderators = new HashSet<>(); + moderators.add("pasquale.pagano@isti.cnr.it"); + moderators.add("leonardo.candela@isti.cnr.it"); + return moderators; + } + + protected void postMessageToStream(ZulipRestExecutor zulipRestExecutor, CMItemStatus cmItemStatus, String message) throws Exception { PostMessage postMessage = new PostMessage(getStreamName(), cmItemStatus.getFancyValue(), message); - String response = zulipRestExecutor.executeCall(postMessage); - logger.trace(response); + logger.debug("Going to send the following message: {}", message); + executeZulipCall(zulipRestExecutor, postMessage); } - public void postMessageToStream(CMItemStatus cmItemStatus, String message) { + public void postUserMessageToStream(CMItemStatus cmItemStatus, String message) throws Exception { postMessageToStream(getUserZulipRestExecutor(), cmItemStatus, message); } - public void postItemCreated(){ - String message = ""; - postMessageToStream(gCatZulipRestExecutor, CMItemStatus.PENDING, message); + private void postItemCreated() throws Exception{ + String username = ckanUser.getPortalUser().getNameSurname(); + CMItemStatus cmItemStatus = CMItemStatus.PENDING; + String message = String.format("@**%s** has created the item with name '%s' (id='%s'). The item is now in **%s** state and must be moderated.", + username, itemName, itemID, cmItemStatus.getFancyValue()); + postMessageToStream(getGCatZulipRestExecutor(), cmItemStatus, message); } - public void postItemUpdated(){ - String message = ""; - postMessageToStream(gCatZulipRestExecutor, CMItemStatus.PENDING, message); + public void postItemUpdated() throws Exception{ + String username = ckanUser.getPortalUser().getNameSurname(); + CMItemStatus cmItemStatus = CMItemStatus.PENDING; + String message = String.format("@**%s** has updated the item with name '%s' (id='%s'). The item is now in **%s** state and must be moderated.", + username, itemName, itemID, cmItemStatus.getFancyValue()); + postMessageToStream(getGCatZulipRestExecutor(), cmItemStatus, message); } - public void postItemRejected() { - String message = ""; - postMessageToStream(gCatZulipRestExecutor, CMItemStatus.REJECTED, message); + public void postItemRejected(String userMessage) throws Exception { + String username = ckanUser.getPortalUser().getNameSurname(); + CMItemStatus cmItemStatus = CMItemStatus.REJECTED; + String message = String.format("@**%s** has **%s** the item with name '%s' (id='%s'). The author can delete the item or update it to try to meet moderators requests if any.", + username, cmItemStatus.getFancyValue(), itemName, itemID); + postMessageToStream(getGCatZulipRestExecutor(), cmItemStatus, message); + postUserMessageToStream(cmItemStatus, userMessage); } - public void postItemApproved(){ - String message = ""; - postMessageToStream(gCatZulipRestExecutor, CMItemStatus.PENDING, message); + public void postItemApproved(String userMessage) throws Exception{ + String username = ckanUser.getPortalUser().getNameSurname(); + CMItemStatus cmItemStatus = CMItemStatus.APPROVED; + String message = String.format("@**%s** has **%s** the item with name '%s' (id='%s'). The item is now available in the catalogue.", + username, cmItemStatus.getFancyValue(), itemName, itemID); + postMessageToStream(getGCatZulipRestExecutor(), cmItemStatus, message); + postUserMessageToStream(cmItemStatus, userMessage); } - } diff --git a/src/test/java/org/gcube/gcat/ContextTest.java b/src/test/java/org/gcube/gcat/ContextTest.java index a494b87..f6bd4f6 100644 --- a/src/test/java/org/gcube/gcat/ContextTest.java +++ b/src/test/java/org/gcube/gcat/ContextTest.java @@ -31,7 +31,11 @@ public class ContextTest { protected static Properties properties; protected static final String PROPERTIES_FILENAME = "token.properties"; - public static final String DEFAULT_TEST_SCOPE_NAME; + public static final String PARENT_DEFAULT_TEST_SCOPE; + public static final String DEFAULT_TEST_SCOPE; + public static final String ALTERNATIVE_TEST_SCOPE; + + public static final String DEV_VRE; static { properties = new Properties(); @@ -45,7 +49,13 @@ public class ContextTest { } //DEFAULT_TEST_SCOPE_NAME = "/pred4s/preprod/preVRE"; - DEFAULT_TEST_SCOPE_NAME = "/gcube/devsec/devVRE"; + // DEFAULT_TEST_SCOPE_NAME = "/gcube/devsec/devVRE"; + + PARENT_DEFAULT_TEST_SCOPE = "/gcube"; + DEFAULT_TEST_SCOPE = PARENT_DEFAULT_TEST_SCOPE + "/devsec"; + ALTERNATIVE_TEST_SCOPE = DEFAULT_TEST_SCOPE + "/devVRE"; + + DEV_VRE = ALTERNATIVE_TEST_SCOPE; } public static String getCurrentContextFullName() { @@ -83,7 +93,7 @@ public class ContextTest { @BeforeClass public static void beforeClass() throws Exception { - setContextByName(DEFAULT_TEST_SCOPE_NAME); + setContextByName(DEFAULT_TEST_SCOPE); } @AfterClass diff --git a/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java b/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java index 30b35b4..8c7ddbc 100644 --- a/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java +++ b/src/test/java/org/gcube/gcat/persistence/ckan/CKANPackageTest.java @@ -1,5 +1,7 @@ package org.gcube.gcat.persistence.ckan; +import static org.gcube.common.authorization.client.Constants.authorizationService; + import java.io.StringWriter; import java.net.URI; import java.util.ArrayList; @@ -9,6 +11,7 @@ import java.util.Map; import java.util.Set; import javax.ws.rs.ForbiddenException; +import javax.ws.rs.NotFoundException; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.PathSegment; @@ -16,6 +19,7 @@ import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import org.gcube.common.authorization.client.exceptions.ObjectNotFound; +import org.gcube.common.authorization.library.provider.UserInfo; import org.gcube.common.resources.gcore.GenericResource; import org.gcube.common.resources.gcore.Resources; import org.gcube.common.scope.impl.ScopeBean; @@ -562,4 +566,73 @@ public class CKANPackageTest extends ContextTest { ckanPackage.update("{\"rating\": 0.0, \"license_title\": \"Academic Free License 3.0\", \"maintainer\": \"Kerekes Kata\", \"relationships_as_object\": [], \"private\": false, \"maintainer_email\": \"kerekeska@nebih.gov.hu\", \"num_tags\": 1, \"id\": \"7731b70f-47ff-4b74-b943-188215e82d07\", \"metadata_created\": \"2020-01-07T16:40:24.822719\", \"owner_org\": \"3571cca5-b0ae-4dc6-b791-434a8e062ce5\", \"metadata_modified\": \"2020-02-03T15:16:42.596068\", \"author\": \"Kerekes Kata\", \"author_email\": \"kerekeska@nebih.gov.hu\", \"state\": \"active\", \"version\": \"1\", \"license_id\": \"AFL-3.0\", \"type\": \"dataset\", \"resources\": [{\"cache_last_updated\": null, \"cache_url\": null, \"mimetype_inner\": null, \"hash\": \"\", \"description\": \"\", \"format\": \"JPEG\", \"url\": \"https://goo.gl/SnwAM7\", \"created\": \"2019-04-01T13:24:40.738838\", \"state\": \"active\", \"package_id\": \"7731b70f-47ff-4b74-b943-188215e82d07\", \"last_modified\": null, \"mimetype\": \"image/jpeg\", \"url_type\": null, \"position\": 0, \"revision_id\": \"06d61000-a0c1-4155-ad2d-78ede56d6bb5\", \"size\": null, \"datastore_active\": false, \"id\": \"1de8851d-1385-47ae-9c93-6040d170a9cc\", \"resource_type\": null, \"name\": \"th.jpeg\"}], \"num_resources\": 1, \"tags\": [{\"vocabulary_id\": null, \"state\": \"active\", \"display_name\": \"DEMETER\", \"id\": \"4e05058b-a006-4dbf-94f5-277a30318323\", \"name\": \"DEMETER\"}], \"groups\": [], \"creator_user_id\": \"7020f836-45f4-4ee8-9c65-e7504209644f\", \"relationships_as_subject\": [], \"name\": \"vre_picture\", \"isopen\": true, \"url\": \"\", \"notes\": \"This is a nice picture of a VRE ;)\", \"title\": \"VRE picture\", \"extras\": [{\"value\": \"https://data.d4science.org/ctlg/DEMETER_trial/vre_picture\", \"key\": \"Item URL\"}, {\"value\": \"ResearchObject\", \"key\": \"system:type\"}], \"license_url\": \"https://www.opensource.org/licenses/AFL-3.0\", \"ratings_count\": 0, \"organization\": {\"description\": \"\", \"title\": \"devVRE\", \"created\": \"2016-05-30T11:30:41.710079\", \"approval_status\": \"approved\", \"is_organization\": true, \"state\": \"active\", \"image_url\": \"\", \"revision_id\": \"7c8463df-ed3f-4d33-87d8-6c0bcbe30d5d\", \"type\": \"organization\", \"id\": \"3571cca5-b0ae-4dc6-b791-434a8e062ce5\", \"name\": \"devvre\"}, \"revision_id\": \"bdb6169a-6268-43d6-b7e1-265c0c9e1a1c\"}"); } + @Test + public void generateLinoTokenModeration() throws Exception { + UserInfo userInfo = new UserInfo("leonardo.candela", new ArrayList<>()); + String userToken = authorizationService().generateUserToken(userInfo, ALTERNATIVE_TEST_SCOPE); + logger.debug(userToken); + } + + + @Test + public void testModeration() throws Exception { + ContextTest.setContextByName(DEV_VRE); + CKANPackage ckanPackage = new CKANPackage(); + ckanPackage.setName(ITEM_NAME_VALUE); + try { + ckanPackage.purge(); + }catch (NotFoundException e) { + logger.trace("The item does not exist. This is correct"); + } + + + ObjectMapper mapper = new ObjectMapper(); + createPackage(mapper); + + + ContextTest.setContextByName("leonardo.candela_"+DEV_VRE); + ckanPackage = new CKANPackage(); + ckanPackage.setName(ITEM_NAME_VALUE); + ckanPackage.message("Please add the notes."); + + + ContextTest.setContextByName(DEV_VRE); + ckanPackage = new CKANPackage(); + ckanPackage.setName(ITEM_NAME_VALUE); + String readItem = ckanPackage.read(); + JsonNode readItemObjectNode = mapper.readTree(readItem); + String updatedNotes = "A research of Luca Frosini made during the PhD"; + ((ObjectNode) readItemObjectNode).put(NOTES_KEY, updatedNotes); + ckanPackage.update(mapper.writeValueAsString(readItemObjectNode)); + ckanPackage.message("I hope now it can be approved."); + + + ContextTest.setContextByName("leonardo.candela_"+DEV_VRE); + ckanPackage = new CKANPackage(); + ckanPackage.setName(ITEM_NAME_VALUE); + ckanPackage.reject("You must specify the institution."); + + + ContextTest.setContextByName(DEV_VRE); + ckanPackage = new CKANPackage(); + ckanPackage.setName(ITEM_NAME_VALUE); + readItem = ckanPackage.read(); + readItemObjectNode = mapper.readTree(readItem); + updatedNotes = "A research of Luca Frosini at ISTI"; + ((ObjectNode) readItemObjectNode).put(NOTES_KEY, updatedNotes); + ckanPackage.update(mapper.writeValueAsString(readItemObjectNode)); + + + ContextTest.setContextByName("pasquale.pagano_"+DEV_VRE); + ckanPackage = new CKANPackage(); + ckanPackage.setName(ITEM_NAME_VALUE); + ckanPackage.approve("It seems fine now"); + + +// ContextTest.setContextByName(DEV_VRE); +// ckanPackage = new CKANPackage(); +// ckanPackage.setName(ITEM_NAME_VALUE); +// ckanPackage.purge(); + } + } diff --git a/src/test/resources/.gitignore b/src/test/resources/.gitignore index 7a97cb1..9d00c19 100644 --- a/src/test/resources/.gitignore +++ b/src/test/resources/.gitignore @@ -1,2 +1,6 @@ /*.gcubekey /*.properties +/gCat_zuliprc +/leonardo.candela_zuliprc +/luca.frosini_zuliprc +/pasquale.pagano_zuliprc