From 9fb01322c02c6ed721ec9f7055129651b2385e8b Mon Sep 17 00:00:00 2001 From: Costantino Perciante Date: Mon, 10 Oct 2016 16:50:11 +0000 Subject: [PATCH] Methods for publishing stock or fishery records added. The association thread that should associate the product to the groups fails. This needs to be fixed. Target selected as folder to be ignored git-svn-id: https://svn.d4science.research-infrastructures.eu/gcube/trunk/data-catalogue/grsf-publisher-ws@133011 82a268e6-3cf1-43bd-a215-b396298e98cf --- .classpath | 1 + .project | 19 ++ .settings/.jsdtscope | 12 ++ .settings/org.eclipse.jdt.core.prefs | 3 + .settings/org.eclipse.wst.common.component | 12 ++ ...se.wst.common.project.facet.core.prefs.xml | 7 + ....eclipse.wst.common.project.facet.core.xml | 8 + ...rg.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + .settings/org.eclipse.wst.validation.prefs | 2 + pom.xml | 18 +- .../grsf_publish_ws/json/input/Common.java | 31 +-- .../json/input/FisheryRecord.java | 16 +- .../json/input/StockRecord.java | 17 +- .../services/GrsfPublisherFisheryService.java | 192 +++++++++++++++++- .../services/GrsfPublisherStockService.java | 192 +++++++++++++++--- .../utils/AssociationToGroupThread.java | 33 ++- .../utils/GcoreEndpointReaderSocialWS.java | 73 +++++++ .../grsf_publish_ws/utils/HelperMethods.java | 169 ++++++++++++++- src/main/webapp/WEB-INF/web.xml | 18 +- src/main/webapp/index.jsp | 5 + .../grsf_publish_ws/JTests.java | 67 +++++- 22 files changed, 809 insertions(+), 88 deletions(-) create mode 100644 .settings/.jsdtscope create mode 100644 .settings/org.eclipse.wst.common.component create mode 100644 .settings/org.eclipse.wst.common.project.facet.core.prefs.xml create mode 100644 .settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 .settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100644 .settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100644 .settings/org.eclipse.wst.validation.prefs create mode 100644 src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/GcoreEndpointReaderSocialWS.java create mode 100644 src/main/webapp/index.jsp diff --git a/.classpath b/.classpath index 7b8885c..6b5910d 100644 --- a/.classpath +++ b/.classpath @@ -15,6 +15,7 @@ + diff --git a/.project b/.project index 936dba4..025ddc6 100644 --- a/.project +++ b/.project @@ -5,11 +5,26 @@ + + org.eclipse.wst.jsdt.core.javascriptValidator + + + + + org.eclipse.wst.common.project.facet.core.builder + + + org.eclipse.jdt.core.javabuilder + + org.eclipse.wst.validation.validationbuilder + + + org.eclipse.m2e.core.maven2Builder @@ -17,7 +32,11 @@ + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature diff --git a/.settings/.jsdtscope b/.settings/.jsdtscope new file mode 100644 index 0000000..b46b920 --- /dev/null +++ b/.settings/.jsdtscope @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index ec4300d..443e085 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,5 +1,8 @@ eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 org.eclipse.jdt.core.compiler.compliance=1.7 +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 diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..d3d2b68 --- /dev/null +++ b/.settings/org.eclipse.wst.common.component @@ -0,0 +1,12 @@ + + + + + + + uses + + + + + diff --git a/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml b/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml new file mode 100644 index 0000000..cc81385 --- /dev/null +++ b/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..25d173a --- /dev/null +++ b/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.container b/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 0000000..3bd5d0a --- /dev/null +++ b/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.name b/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 0000000..05bd71b --- /dev/null +++ b/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/.settings/org.eclipse.wst.validation.prefs b/.settings/org.eclipse.wst.validation.prefs new file mode 100644 index 0000000..04cad8c --- /dev/null +++ b/.settings/org.eclipse.wst.validation.prefs @@ -0,0 +1,2 @@ +disabled=06target +eclipse.preferences.version=1 diff --git a/pom.xml b/pom.xml index d9c17cf..b133e98 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ org.gcube.data-catalogue grsf-publisher-ws 1.0.0-SNAPSHOT - jar + war grsf-publisher-ws @@ -70,6 +70,11 @@ [2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT) compile + + org.gcube.core + common-encryption + provided + org.gcube.common authorization-client @@ -112,6 +117,11 @@ common-smartgears provided + + org.gcube.resources.discovery + ic-client + provided + org.glassfish.jersey.test-framework.providers jersey-test-framework-provider-jetty @@ -130,9 +140,9 @@ compile - - - + + exploded + diff --git a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/Common.java b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/Common.java index f1a0933..2f408e5 100644 --- a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/Common.java +++ b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/Common.java @@ -8,7 +8,6 @@ import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.CustomField; import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Group; import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Tag; import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Source; -import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Status; import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Type; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -26,14 +25,14 @@ public class Common { @JsonProperty("license") private String license; - - @JsonProperty("author")// filled automatically by the server + + // filled automatically by the server private String author; @JsonProperty("version") private Long version; - @JsonProperty("author_contact")// filled automatically by the service + // filled automatically by the service private String authorContact; @JsonProperty("maintainer") @@ -65,11 +64,6 @@ public class Common { @CustomField(key="Type") private Type type; - @JsonProperty("status") - @Tag - @CustomField(key="Status") - private Status status; - @JsonProperty("resources") private List resources; @@ -88,7 +82,7 @@ public class Common { Long version, String authorContact, String maintainer, String maintainerContact, String catchesOrLandings, Source databaseSources, String sourceOfInformation, - String dataOwner, Type type, Status status, + String dataOwner, Type type, List resources, Map extras, String spatial) { super(); @@ -104,7 +98,6 @@ public class Common { this.sourceOfInformation = sourceOfInformation; this.dataOwner = dataOwner; this.type = type; - this.status = status; this.resources = resources; this.extras = extras; this.spatial = spatial; @@ -149,7 +142,7 @@ public class Common { public void setVersion(Long version) { this.version = version; } - + public String getAuthorContact() { return authorContact; } @@ -214,14 +207,6 @@ public class Common { this.type = type; } - public Status getStatus() { - return status; - } - - public void setStatus(Status status) { - this.status = status; - } - public List getResources() { return resources; } @@ -247,9 +232,9 @@ public class Common { + ", catchesOrLandings=" + catchesOrLandings + ", databaseSources=" + databaseSources + ", sourceOfInformation=" + sourceOfInformation - + ", dataOwner=" + dataOwner + ", type=" + type + ", status=" - + status + ", resources=" + resources + ", extras=" - + extras + ", spatial=" + spatial + "]"; + + ", dataOwner=" + dataOwner + ", type=" + type + + ", resources=" + resources + ", extras=" + extras + + ", spatial=" + spatial + "]"; } } diff --git a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/FisheryRecord.java b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/FisheryRecord.java index 0879b18..73b5537 100644 --- a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/FisheryRecord.java +++ b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/FisheryRecord.java @@ -4,6 +4,7 @@ import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.CustomField; import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Group; import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Tag; import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Production_System_Type; +import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Status; import com.fasterxml.jackson.annotation.JsonProperty; @@ -57,6 +58,10 @@ public class FisheryRecord extends Common{ @CustomField(key="Fishing gear") private String fishingGear; + @JsonProperty("status") + @CustomField(key="Status") + private Status status; + public FisheryRecord() { super(); } @@ -171,6 +176,14 @@ public class FisheryRecord extends Common{ this.fishingGear = fishingGear; } + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + @Override public String toString() { return "FisheryRecord [" + super.toString() + ", fisheryName=" + fisheryName + ", fisheryId=" @@ -180,8 +193,7 @@ public class FisheryRecord extends Common{ + ", jurisdictionArea=" + jurisdictionArea + ", productionSystemType=" + productionSystemType + ", flagState=" + flagState + ", fishingGear=" + fishingGear - + "]"; + + ", status=" + status + "]"; } - } diff --git a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/StockRecord.java b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/StockRecord.java index df0b252..aa6476f 100644 --- a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/StockRecord.java +++ b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/json/input/StockRecord.java @@ -5,6 +5,7 @@ import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Group; import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Tag; import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Abundance_Level; import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Exploitation_Rate; +import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Status; import com.fasterxml.jackson.annotation.JsonProperty; @@ -71,6 +72,11 @@ public class StockRecord extends Common{ @JsonProperty("reporting_year") @CustomField(key="Reporting year") private Long reportingYear; + + @JsonProperty("status") + @Tag + @CustomField(key="Status") + private Status status; /** * @@ -230,6 +236,14 @@ public class StockRecord extends Common{ public void setReportingYear(Long reportingYear) { this.reportingYear = reportingYear; } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } @Override public String toString() { @@ -244,6 +258,7 @@ public class StockRecord extends Common{ + ", narrativeStateAndTrend=" + narrativeStateAndTrend + ", scientificAdvice=" + scientificAdvice + ", reportingEntity=" + reportingEntity + ", reportingYear=" - + reportingYear + "]"; + + reportingYear + ", status=" + status + "]"; } + } diff --git a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/services/GrsfPublisherFisheryService.java b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/services/GrsfPublisherFisheryService.java index df7ab86..6779069 100644 --- a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/services/GrsfPublisherFisheryService.java +++ b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/services/GrsfPublisherFisheryService.java @@ -1,17 +1,34 @@ package org.gcube.data_catalogue.grsf_publish_ws.services; -import java.util.UUID; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.servlet.ServletContext; import javax.ws.rs.Consumes; +import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.authorization.library.utils.Caller; +import org.gcube.common.scope.api.ScopeProvider; import org.gcube.data_catalogue.grsf_publish_ws.json.input.FisheryRecord; +import org.gcube.data_catalogue.grsf_publish_ws.json.input.Resource; import org.gcube.data_catalogue.grsf_publish_ws.json.output.ResponseCreationBean; +import org.gcube.data_catalogue.grsf_publish_ws.utils.AssociationToGroupThread; +import org.gcube.data_catalogue.grsf_publish_ws.utils.HelperMethods; +import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue; +import org.gcube.datacatalogue.ckanutillibrary.models.ResourceBean; +import org.gcube.datacatalogue.ckanutillibrary.models.RolesCkanGroupOrOrg; +import org.gcube.datacatalogue.ckanutillibrary.utils.UtilMethods; import org.slf4j.LoggerFactory; /** @@ -20,9 +37,21 @@ import org.slf4j.LoggerFactory; */ @Path("fishery/") public class GrsfPublisherFisheryService { + + // the context + @Context + ServletContext contextServlet; // Logger private static final org.slf4j.Logger logger = LoggerFactory.getLogger(GrsfPublisherFisheryService.class); + + @GET + @Path("hello") + @Produces(MediaType.TEXT_PLAIN) + public Response hello(){ + return Response.ok("Hello.. Fishery service is here").build(); + } + @POST @Path("publish-product") @@ -30,11 +59,164 @@ public class GrsfPublisherFisheryService { @Produces(MediaType.APPLICATION_JSON) public Response publishFishery(FisheryRecord record){ - logger.info("Incoming request for creating a fishery record"); - System.out.println(record); + // retrieve context and username + Caller caller = AuthorizationProvider.instance.get(); + String username = caller.getClient().getId(); + String context = ScopeProvider.instance.get(); - // TODO - return Response.status(Status.CREATED).entity(new ResponseCreationBean(UUID.randomUUID().toString(), null)).build(); + logger.info("Incoming request for creating a fishery record = " + record); + logger.info("Request coming from user " + username + " in context " + context); + + ResponseCreationBean responseBean = new ResponseCreationBean(); + Status status = Status.INTERNAL_SERVER_ERROR; + String id = ""; + + try{ + + // determine the organization in which this product should be put + String contextInWhichPublish = HelperMethods.getContextFromStatus(record.getStatus(), contextServlet); + + if(contextInWhichPublish == null || !contextInWhichPublish.equals(context)){ + + // stop, this value must be defined + status = Status.BAD_REQUEST; + responseBean.setId(id); + throw new IllegalArgumentException("Status attribute is not defined or the Token you are using is not correct to perform such request!"); + + }else{ + + DataCatalogue catalogue = HelperMethods.getDataCatalogueRunningInstance(context); + if(catalogue == null){ + + status = Status.INTERNAL_SERVER_ERROR; + responseBean.setId(null); + throw new Exception("There was a problem while serving your request"); + + }else{ + + // check the user has editor/admin role into the org + String organization = HelperMethods.retrieveOrgNameFromScope(contextInWhichPublish); + if(catalogue.getRoleOfUserInOrganization(username, organization, catalogue.getApiKeyFromUsername(username)).equals(RolesCkanGroupOrOrg.MEMBER)){ + + status = Status.FORBIDDEN; + responseBean.setId(null); + throw new Exception("You are not authorized to create a product. Please check you have at least the editor role!"); + + } + + // check the record has a name, at least + String futureTitle = record.getFisheryName(); + if(!HelperMethods.isValid(futureTitle)){ + + status = Status.BAD_REQUEST; + responseBean.setId(null); + throw new Exception("The name requested for the product is not correct! It should contain only alphanumeric characters, and symbols like '.' or '_', '-'"); + + }else{ + + logger.debug("Checking if such name [" + futureTitle + "]doesn't exist yet..."); + String futureName = UtilMethods.fromProductTitleToName(futureTitle); + logger.info("Transformed name is " + futureName); + boolean alreadyExist = catalogue.existProductWithNameOrId(futureName); + + if(alreadyExist){ + + logger.debug("A product with name " + futureName + " already exists"); + responseBean.setId(null); + status = Status.BAD_REQUEST; + throw new Exception("Sorry but a product with such name already exists!"); + + }else{ + + // evaluate the tags of the product + List tags = new ArrayList(); + HelperMethods.getTags(tags, record); + + // evaluate the groups + List groups = new ArrayList(); + HelperMethods.getGroups(groups, record); + + // evaluate the custom fields + Map customFields = new HashMap(); + + if(record.getExtras() != null) + customFields = record.getExtras(); + + // automatically retrieve the other ones + HelperMethods.getExtras(customFields, record); + + // retrieve the user's email and fullname + String authorMail = HelperMethods.getUserEmail(context, SecurityTokenProvider.instance.get()); + String authorFullname = HelperMethods.getUserFullname(context, SecurityTokenProvider.instance.get()); + + if(authorMail == null || authorFullname == null){ + + logger.debug("Author fullname or mail missing, cannot continue"); + responseBean.setId(null); + status = Status.INTERNAL_SERVER_ERROR; + throw new Exception("Sorry but there was not possible to retrieve your fullname/email!"); + + }else{ + + // evaluate the resources TODO + List resourcesRecord = record.getResources(); + List resources = null; + if(resourcesRecord != null && !resourcesRecord.isEmpty()){ + resources = new ArrayList(); + for (Resource res : resourcesRecord) { + logger.debug("Trying to add resource " + res); + if(res.getName() != null && res.getUrl() != null) + resources.add(new ResourceBean(res.getUrl(), res.getName(), res.getDescription(), null, username, null, null)); + else + logger.warn("Unable to add resource because url or name is null"); + } + } + + + + // if confirmed, set to visible TODO anyway if it is confirmed we should another method + boolean setPublic = record.getStatus() == org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Status.Confirmed; + + // create the product + id = catalogue.createCKanDataset( + catalogue.getApiKeyFromUsername(username), + futureTitle, + organization, + authorFullname, + authorMail, + record.getMaintainer(), + record.getMaintainerContact(), + record.getVersion(), + record.getDescription(), + record.getLicense(), + tags, + customFields, + resources, + setPublic); // TODO + + if(id != null){ + + logger.info("Product created! Id is " + id); + responseBean.setId(id); + status = Status.CREATED; + responseBean.setError(null); + + if(!groups.isEmpty()){ + logger.info("Launching thread for association to the list of groups " + groups); + new AssociationToGroupThread(groups, id, organization, username, catalogue).start(); + } + } + } + } + } + } + } + }catch(Exception e){ + logger.error("Failed to create fishery record", e); + responseBean.setError(e.getMessage()); + } + + return Response.status(status).entity(responseBean).build(); } diff --git a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/services/GrsfPublisherStockService.java b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/services/GrsfPublisherStockService.java index 7114f3d..c4c87bc 100644 --- a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/services/GrsfPublisherStockService.java +++ b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/services/GrsfPublisherStockService.java @@ -1,23 +1,34 @@ package org.gcube.data_catalogue.grsf_publish_ws.services; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import javax.servlet.ServletContext; import javax.ws.rs.Consumes; +import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.authorization.library.utils.Caller; import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.data_catalogue.grsf_publish_ws.json.input.Resource; import org.gcube.data_catalogue.grsf_publish_ws.json.input.StockRecord; import org.gcube.data_catalogue.grsf_publish_ws.json.output.ResponseCreationBean; +import org.gcube.data_catalogue.grsf_publish_ws.utils.AssociationToGroupThread; import org.gcube.data_catalogue.grsf_publish_ws.utils.HelperMethods; import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue; +import org.gcube.datacatalogue.ckanutillibrary.models.ResourceBean; +import org.gcube.datacatalogue.ckanutillibrary.models.RolesCkanGroupOrOrg; +import org.gcube.datacatalogue.ckanutillibrary.utils.UtilMethods; import org.slf4j.LoggerFactory; /** @@ -27,8 +38,19 @@ import org.slf4j.LoggerFactory; @Path("stock/") public class GrsfPublisherStockService { + // the context + @Context + ServletContext contextServlet; + // Logger private static final org.slf4j.Logger logger = LoggerFactory.getLogger(GrsfPublisherStockService.class); + + @GET + @Path("hello") + @Produces(MediaType.TEXT_PLAIN) + public Response hello(){ + return Response.ok("Hello.. Stock service is here").build(); + } @POST @Path("publish-product") @@ -41,40 +63,160 @@ public class GrsfPublisherStockService { String username = caller.getClient().getId(); String context = ScopeProvider.instance.get(); - logger.info("Incoming request for creating a stock record by user with id " + username); - + logger.info("Incoming request for creating a stock record = " + record); + logger.info("Request coming from user " + username + " in context " + context); + ResponseCreationBean responseBean = new ResponseCreationBean(); - Status status = Status.CREATED; + Status status = Status.INTERNAL_SERVER_ERROR; String id = ""; try{ - - DataCatalogue catalogue = HelperMethods.getDataCatalogueRunningInstance(context); - - // check the user has editor/admin role into the org TODO pending or accepted? - - // check the record has a name, at least - - // evaluate the tags of the product - - // evaluate the resources - - // evaluate tags and groups using reflection - List tags = new ArrayList(); - List groups = new ArrayList(); - - // create the product - //catalogue.createCKanDataset(apiKey, title, organizationNameOrId, author, authorMail, maintainer, maintainerMail, version, description, licenseId, tags, customFields, resources, setPublic) - + // determine the organization in which this product should be put + String contextInWhichPublish = HelperMethods.getContextFromStatus(record.getStatus(), contextServlet); + + if(contextInWhichPublish == null || !contextInWhichPublish.equals(context)){ + + // stop, this value must be defined + status = Status.BAD_REQUEST; + responseBean.setId(id); + throw new IllegalArgumentException("Status attribute is not defined or the Token you are using is not correct to perform such request!"); + + }else{ + + DataCatalogue catalogue = HelperMethods.getDataCatalogueRunningInstance(context); + if(catalogue == null){ + + status = Status.INTERNAL_SERVER_ERROR; + responseBean.setId(null); + throw new Exception("There was a problem while serving your request"); + + }else{ + + // check the user has editor/admin role into the org + String organization = HelperMethods.retrieveOrgNameFromScope(contextInWhichPublish); + if(catalogue.getRoleOfUserInOrganization(username, organization, catalogue.getApiKeyFromUsername(username)).equals(RolesCkanGroupOrOrg.MEMBER)){ + + status = Status.FORBIDDEN; + responseBean.setId(null); + throw new Exception("You are not authorized to create a product. Please check you have at least the editor role!"); + + } + + // check the record has a name, at least + String futureTitle = record.getStockName(); + if(!HelperMethods.isValid(futureTitle)){ + + status = Status.BAD_REQUEST; + responseBean.setId(null); + throw new Exception("The name requested for the product is not correct! It should contain only alphanumeric characters, and symbols like '.' or '_', '-'"); + + }else{ + + logger.debug("Checking if such name [" + futureTitle + "]doesn't exist yet..."); + String futureName = UtilMethods.fromProductTitleToName(futureTitle); + logger.info("Transformed name is " + futureName); + boolean alreadyExist = catalogue.existProductWithNameOrId(futureName); + + if(alreadyExist){ + + logger.debug("A product with name " + futureName + " already exists"); + responseBean.setId(null); + status = Status.BAD_REQUEST; + throw new Exception("Sorry but a product with such name already exists!"); + + }else{ + + // evaluate the tags of the product + List tags = new ArrayList(); + HelperMethods.getTags(tags, record); + + // evaluate the groups + List groups = new ArrayList(); + HelperMethods.getGroups(groups, record); + + // evaluate the custom fields + Map customFields = new HashMap(); + + if(record.getExtras() != null) + customFields = record.getExtras(); + + // automatically retrieve the other ones + HelperMethods.getExtras(customFields, record); + + // retrieve the user's email and fullname + String authorMail = HelperMethods.getUserEmail(context, SecurityTokenProvider.instance.get()); + String authorFullname = HelperMethods.getUserFullname(context, SecurityTokenProvider.instance.get()); + + if(authorMail == null || authorFullname == null){ + + logger.debug("Author fullname or mail missing, cannot continue"); + responseBean.setId(null); + status = Status.INTERNAL_SERVER_ERROR; + throw new Exception("Sorry but there was not possible to retrieve your fullname/email!"); + + }else{ + + // evaluate the resources TODO + List resourcesRecord = record.getResources(); + List resources = null; + if(resourcesRecord != null && !resourcesRecord.isEmpty()){ + resources = new ArrayList(); + for (Resource res : resourcesRecord) { + logger.debug("Trying to add resource " + res); + if(res.getName() != null && res.getUrl() != null) + resources.add(new ResourceBean(res.getUrl(), res.getName(), res.getDescription(), null, username, null, null)); + else + logger.warn("Unable to add resource because url or name is null"); + } + } + + + + // if confirmed, set to visible TODO anyway if it is confirmed we should another method + boolean setPublic = record.getStatus() == org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Status.Confirmed; + + // create the product + id = catalogue.createCKanDataset( + catalogue.getApiKeyFromUsername(username), + futureTitle, + organization, + authorFullname, + authorMail, + record.getMaintainer(), + record.getMaintainerContact(), + record.getVersion(), + record.getDescription(), + record.getLicense(), + tags, + customFields, + resources, + setPublic); // TODO + + if(id != null){ + + logger.info("Product created! Id is " + id); + responseBean.setId(id); + status = Status.CREATED; + responseBean.setError(null); + + if(!groups.isEmpty()){ + logger.info("Launching thread for association to the list of groups " + groups); + new AssociationToGroupThread(groups, id, organization, username, catalogue).start(); + } + } + } + } + } + } + } }catch(Exception e){ logger.error("Failed to create stock record", e); - status = Status.INTERNAL_SERVER_ERROR; - responseBean.setError(e.toString()); - responseBean.setId(id); + responseBean.setError(e.getMessage()); } - + return Response.status(status).entity(responseBean).build(); + } } diff --git a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/AssociationToGroupThread.java b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/AssociationToGroupThread.java index 82f75fc..24ab651 100644 --- a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/AssociationToGroupThread.java +++ b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/AssociationToGroupThread.java @@ -19,40 +19,51 @@ public class AssociationToGroupThread extends Thread { private String datasetId; private String username; private DataCatalogue catalogue; + private String organizationId; /** * @param groupTitle * @param datasetId * @param username + * @param organizationId * @param catalogue */ - public AssociationToGroupThread(List groupsTitles, String datasetId, + public AssociationToGroupThread(List groupsTitles, String datasetId, String organizationId, String username, DataCatalogue catalogue) { this.groupsTitles = groupsTitles; this.datasetId = datasetId; this.username = username; this.catalogue = catalogue; + this.organizationId = organizationId; } @Override public void run() { logger.info("Association thread started to put the dataset with id="+ datasetId + " into group with title(s) " + groupsTitles + " for user " + username); - - + + String userApiKey = catalogue.getApiKeyFromUsername(username); + + // retrieve the role to be assigned according the one the user has into the organization of the dataset + RolesCkanGroupOrOrg role = RolesCkanGroupOrOrg.valueOf(catalogue.getRoleOfUserInOrganization(username, organizationId, catalogue.getApiKeyFromUsername(username)).toUpperCase()); + for (String groupTitle : groupsTitles) { - - // check the user has enough privileges to associate it - if(catalogue.checkRoleIntoGroup(username, groupTitle, RolesCkanGroupOrOrg.MEMBER)){ - - logger.warn("The user " + username + " has not enough privileges to associate the dataset into group " + groupTitle); + + logger.debug("Setting role " + role + " into group " + groupTitle + " to user " + username); + boolean assigned = catalogue.checkRoleIntoGroup(username, groupTitle, role); + + if(!assigned){ + + logger.warn("The user " + username + " has not enough privileges to associate the dataset into group OR the group doesn't exist" + groupTitle); continue; - + } + else{ - boolean putIntoGroup = catalogue.assignDatasetToGroup(groupTitle, datasetId, catalogue.getApiKeyFromUsername(username)); - logger.info("Was product put into group? " + putIntoGroup); + boolean putIntoGroup = catalogue.assignDatasetToGroup(groupTitle, datasetId, userApiKey); + logger.info("Was product put into group " + groupTitle + "? " + putIntoGroup); + } } } diff --git a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/GcoreEndpointReaderSocialWS.java b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/GcoreEndpointReaderSocialWS.java new file mode 100644 index 0000000..b6f1154 --- /dev/null +++ b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/GcoreEndpointReaderSocialWS.java @@ -0,0 +1,73 @@ +/** + * + */ +package org.gcube.data_catalogue.grsf_publish_ws.utils; + +import static org.gcube.resources.discovery.icclient.ICFactory.client; +import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; + +import java.util.List; + +import org.gcube.common.resources.gcore.GCoreEndpoint; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.queries.api.SimpleQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GcoreEndpointReaderSocialWS { + + private static final String resource = "jersey-servlet"; + private static final String serviceName = "SocialNetworking"; + private static final String serviceClass = "Portal"; + + private static Logger logger = LoggerFactory.getLogger(GcoreEndpointReaderSocialWS.class); + + /** + * Instantiates a new gcore endpoint reader. + * + * @param scope the scope + * @throws Exception the exception + */ + public static String getUrlSocialWS(String scope) throws Exception { + + if(scope == null || scope.isEmpty()) + throw new IllegalArgumentException(); + + String oldScope = ScopeProvider.instance.get(); + + try{ + + logger.info("set scope "+scope); + ScopeProvider.instance.set(scope); + + 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()"); + + logger.debug("submitting quey "+query.toString()); + + 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: "+scope); + + String urlFound = endpoints.get(0); + if(urlFound==null) + throw new Exception("Endpoint:"+resource+", is null for serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+scope); + + logger.info("found entyname "+urlFound+" for ckanResource: "+resource); + + return urlFound; + + }catch(Exception e){ + String error = "An error occurred during GCoreEndpoint discovery, serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+scope +"."; + logger.error(error, e); + throw new Exception(error); + }finally{ + if(oldScope != null) + ScopeProvider.instance.set(oldScope); + } + } +} diff --git a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/HelperMethods.java b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/HelperMethods.java index f0520ab..e47f2d6 100644 --- a/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/HelperMethods.java +++ b/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/HelperMethods.java @@ -1,18 +1,29 @@ package org.gcube.data_catalogue.grsf_publish_ws.utils; import java.beans.PropertyDescriptor; +import java.io.BufferedReader; +import java.io.InputStreamReader; import java.lang.reflect.Field; import java.util.List; import java.util.Map; +import javax.servlet.ServletContext; + import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.CustomField; import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Group; import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Tag; import org.gcube.data_catalogue.grsf_publish_ws.json.input.Common; +import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Status; import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue; import org.gcube.datacatalogue.ckanutillibrary.DataCatalogueFactory; +import org.gcube.datacatalogue.ckanutillibrary.DataCatalogueImpl; 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.HttpGet; +import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.CloseableHttpClient; +import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.HttpClientBuilder; + /** * Helper methods @@ -22,6 +33,10 @@ public abstract class HelperMethods { private static final org.slf4j.Logger logger = LoggerFactory.getLogger(HelperMethods.class); + // to be retrieved from the web.xml + private static final String PENDING_CONTEX_KEY = "PendingContext"; + private static final String CONFIRMED_CONTEX_KEY = "ConfirmedContext"; + /** * Convert a group name to its id on ckan * @param origName @@ -32,8 +47,13 @@ public abstract class HelperMethods { if(origName == null) throw new IllegalArgumentException("origName cannot be null"); - return origName.trim().toLowerCase().replaceAll("[^A-Za-z0-9_.]", "_"); - + String modified = origName.trim().toLowerCase().replaceAll("[^A-Za-z0-9-]", "-"); + if(modified.startsWith("-")) + modified = modified.substring(1); + if(modified.endsWith("-")) + modified = modified.substring(0, modified.length() -1); + + return modified; } /** @@ -43,7 +63,14 @@ public abstract class HelperMethods { */ public static DataCatalogue getDataCatalogueRunningInstance(String scope) throws Exception{ - return DataCatalogueFactory.getFactory().getUtilsPerScope(scope); + try{ + DataCatalogueImpl instance = DataCatalogueFactory.getFactory().getUtilsPerScope(scope); + return instance; + }catch(Exception e){ + logger.error("Failed to instanciate data catalogue lib", e); + } + + return null; } @@ -73,7 +100,7 @@ public abstract class HelperMethods { } /** - * Retrieve the list of groups for this object + * Retrieve the list of groups' names for this object */ public static void getGroups(List groups, Common record){ Class current = record.getClass(); @@ -101,7 +128,7 @@ public abstract class HelperMethods { /** * Retrieve the list of extras for this object */ - public static void getExtras(Map extras, Common record){ + public static void getExtras(Map extras, Common record){ Class current = record.getClass(); do{ Field[] fields = current.getDeclaredFields(); @@ -124,4 +151,136 @@ public abstract class HelperMethods { while((current = current.getSuperclass())!=null); } + /** + * Retrieve the organization name in which the user wants to publish starting from the scope + * @param contextInWhichPublish + * @return + */ + public static String retrieveOrgNameFromScope(String scope) { + + String[] splittedScope = scope.split("/"); + return splittedScope[splittedScope.length - 1].toLowerCase(); + + } + + /** + * Return the context in which the user wants to publish by the status information + * @param status + * @param contextServlet + * @return + */ + public static String getContextFromStatus(Status status, ServletContext contextServlet) { + + String toReturn = null; + switch(status){ + case Confirmed : + toReturn = (String)contextServlet.getInitParameter(CONFIRMED_CONTEX_KEY); + break; + case Pending: + toReturn = (String)contextServlet.getInitParameter(PENDING_CONTEX_KEY); + break; + default: break; + + } + logger.debug("Context evaluated is " + toReturn); + return toReturn; + } + + /** + * Validate the name the product will have + * @param futureName + * @return + */ + public static boolean isValid(String futureName) { + + if(futureName == null || futureName.isEmpty()) + return false; + else{ + return futureName.matches("[\\sA-Za-z0-9_.-]+"); + } + } + + /** + * Retrieve the user's email given his/her username + * @param context + * @param token + * @return + * @throws Exception + */ + public static String getUserEmail(String context, String token){ + + try(CloseableHttpClient client = HttpClientBuilder.create().build();){ + + String baseUrl = GcoreEndpointReaderSocialWS.getUrlSocialWS(context); + logger.debug("Base url is " + baseUrl); + String url = baseUrl.replace("80", "") + "/users/getUserEmail?gcube-token=" + token; + logger.debug("Request url is " + url); + HttpGet getRequest = new HttpGet(url); + + HttpResponse response = client.execute(getRequest); + + if (response.getStatusLine().getStatusCode() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + + response.getStatusLine().getStatusCode()); + } + + BufferedReader br = new BufferedReader( + new InputStreamReader((response.getEntity().getContent()))); + + String email = ""; + String temp = null; + + while ((temp = br.readLine()) != null) { + email+= temp; + } + + return email; + }catch(Exception e){ + logger.error("error while performing post method " + e.toString()); + } + + return null; + } + + /** + * Retrieve the user's fullname given his/her username + * @param context + * @param token + * @return + * @throws Exception + */ + public static String getUserFullname(String context, String token){ + + try(CloseableHttpClient client = HttpClientBuilder.create().build();){ + + String baseUrl = GcoreEndpointReaderSocialWS.getUrlSocialWS(context); + logger.debug("Base url is " + baseUrl); + String url = baseUrl.replace("80", "") + "/users/getUserFullname?gcube-token=" + token; + logger.debug("Request url is " + url); + HttpGet getRequest = new HttpGet(url); + HttpResponse response = client.execute(getRequest); + + if (response.getStatusLine().getStatusCode() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + + response.getStatusLine().getStatusCode()); + } + + BufferedReader br = new BufferedReader( + new InputStreamReader((response.getEntity().getContent()))); + + String fullName = ""; + String temp = null; + + while ((temp = br.readLine()) != null) { + fullName+= temp; + } + + return fullName; + }catch(Exception e){ + logger.error("error while performing post method " + e.toString()); + } + + return null; + } + } diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 4c4b297..06ba4ab 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -4,7 +4,7 @@ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> - Restful Web Application + GRSF Restful Web App jersey-servlet org.glassfish.jersey.servlet.ServletContainer @@ -17,10 +17,24 @@ jersey-servlet - /* + /rest/* index.jsp + + + Scope of pending products + PendingContext + + /gcube/devNext/NextNext + + + + Scope of confirmed products + ConfirmedContext + + /gcube/devNext/NextNext + diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 0000000..528740f --- /dev/null +++ b/src/main/webapp/index.jsp @@ -0,0 +1,5 @@ + + +

The social networking web service is up and running!

+ + diff --git a/src/test/java/org/gcube/data_catalogue/grsf_publish_ws/JTests.java b/src/test/java/org/gcube/data_catalogue/grsf_publish_ws/JTests.java index 1dda0d7..5566289 100644 --- a/src/test/java/org/gcube/data_catalogue/grsf_publish_ws/JTests.java +++ b/src/test/java/org/gcube/data_catalogue/grsf_publish_ws/JTests.java @@ -2,7 +2,9 @@ package org.gcube.data_catalogue.grsf_publish_ws; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -14,6 +16,8 @@ import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.CustomField; import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Group; import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Tag; import org.gcube.data_catalogue.grsf_publish_ws.json.input.FisheryRecord; +import org.gcube.data_catalogue.grsf_publish_ws.json.input.StockRecord; +import org.gcube.data_catalogue.grsf_publish_ws.utils.HelperMethods; import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Abundance_Level; import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Source; import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Status; @@ -21,8 +25,15 @@ import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Type; import com.fasterxml.jackson.databind.ObjectMapper; +import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpResponse; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpGet; +import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.CloseableHttpClient; +import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.HttpClientBuilder; + public class JTests { + private static final String TEST_TOKEN = "d423aed7-e9e2-424a-b9e7-2bbbd151d9c4-98187548"; + //@Test public void test() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, IntrospectionException { @@ -105,29 +116,65 @@ public class JTests { //@Test public void testJSONMapping() throws IOException{ - FisheryRecord recordFishery = new FisheryRecord(); + StockRecord recordFishery = new StockRecord(); recordFishery.setType(Type.Fishing_Description); recordFishery.setDatabaseSources(Source.FIRMS); recordFishery.setAuthor("Costantino Perciante"); recordFishery.setMaintainer("Costantino Perciante"); recordFishery.setAuthorContact("costantino.perciante@isti.cnr.it"); recordFishery.setStatus(Status.Pending); - recordFishery.setVersion(new Long(10)); - HashMap extras = new HashMap(); - extras.put("test1", "testValue"); - extras.put("test2", "test2Value"); - extras.put("type", "Polygon"); - extras.put("coordinates", "[[[2.05827, 49.8625],[2.05827, 55.7447], [-6.41736, 55.7447], [-6.41736, 49.8625], [2.05827, 49.8625]]]"); - recordFishery.setExtras(extras); + recordFishery.setVersion(new Long(1)); ObjectMapper mapper = new ObjectMapper(); //Object to JSON in String String jsonInString = mapper.writeValueAsString(recordFishery); System.out.println(jsonInString); - + // JSON back to object - FisheryRecord converted = mapper.readValue(jsonInString, recordFishery.getClass()); + StockRecord converted = mapper.readValue(jsonInString, recordFishery.getClass()); System.out.println(converted); } + + //@Test + public void instanciateGCoreReader() throws Exception{ + + String baseUrl = org.gcube.data_catalogue.grsf_publish_ws.utils.GcoreEndpointReaderSocialWS.getUrlSocialWS("/gcube/devNext/NextNext"); + System.out.println("Base url is " + baseUrl); + + try(CloseableHttpClient client = HttpClientBuilder.create().build();){ + + String url = baseUrl.replace("80", "") + "/users/getUserEmail?gcube-token=" + TEST_TOKEN; + System.out.println("Request url is " + url); + HttpGet getRequest = new HttpGet(url); + HttpResponse response = client.execute(getRequest); + + if (response.getStatusLine().getStatusCode() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + + response.getStatusLine().getStatusCode()); + } + + BufferedReader br = new BufferedReader( + new InputStreamReader((response.getEntity().getContent()))); + + String output; + System.out.println("Output from Server .... \n"); + while ((output = br.readLine()) != null) { + System.out.println(output); + } + + System.out.println(response.toString()); + + }catch(Exception e){ + System.err.println("error while performing post method " + e.toString()); + } + } + + //@Test + public void testFromScopeToOrgName(){ + + System.out.println("Valid ? " + HelperMethods.isValid("this is not valid")); + // System.out.println(HelperMethods.retrieveOrgNameFromScope("/gcube/devNext/NextNext")); + + } }