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")); + + } }