From 228c8c57b90993cb02e2dceb95e4ef6393b8d55b Mon Sep 17 00:00:00 2001 From: Costantino Perciante Date: Fri, 31 Mar 2017 15:09:06 +0000 Subject: [PATCH] git-svn-id: https://svn.d4science.research-infrastructures.eu/gcube/trunk/data-catalogue/catalogue-ws@146504 82a268e6-3cf1-43bd-a215-b396298e98cf --- .classpath | 32 + .project | 42 + .settings/.jsdtscope | 13 + .settings/org.eclipse.core.resources.prefs | 5 + .settings/org.eclipse.jdt.core.prefs | 8 + .settings/org.eclipse.m2e.core.prefs | 4 + .settings/org.eclipse.wst.common.component | 10 + ...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 | 224 ++++ profile_test.xml | 179 ++++ .../catalogue/beans/resource/CustomField.java | 119 +++ .../catalogue/utils/CachesManager.java | 31 + .../catalogue/utils/CatalogueUtils.java | 976 ++++++++++++++++++ .../catalogue/utils/Constants.java | 79 ++ .../utils/GcoreEndpointReaderSNL.java | 68 ++ .../utils/PackageCreatePostActions.java | 109 ++ .../WritePostCatalogueManagerThread.java | 287 +++++ .../datacatalogue/catalogue/ws/Group.java | 112 ++ .../datacatalogue/catalogue/ws/Item.java | 153 +++ .../catalogue/ws/ItemProfile.java | 77 ++ .../datacatalogue/catalogue/ws/License.java | 31 + .../catalogue/ws/Organization.java | 112 ++ .../datacatalogue/catalogue/ws/Resource.java | 120 +++ .../datacatalogue/catalogue/ws/User.java | 74 ++ src/main/resources/ehcache.xml | 13 + src/main/webapp/WEB-INF/gcube-app.xml | 8 + src/main/webapp/WEB-INF/web.xml | 30 + src/main/webapp/index.jsp | 5 + src/test/java/JavaTests.java | 143 +++ src/test/java/TestJersey.java | 124 +++ 34 files changed, 3207 insertions(+) create mode 100644 .classpath create mode 100644 .project create mode 100644 .settings/.jsdtscope create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.m2e.core.prefs 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 pom.xml create mode 100644 profile_test.xml create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/beans/resource/CustomField.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/utils/CachesManager.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/utils/CatalogueUtils.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/utils/Constants.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/utils/GcoreEndpointReaderSNL.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/utils/PackageCreatePostActions.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/utils/WritePostCatalogueManagerThread.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/ws/Group.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/ws/Item.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/ws/ItemProfile.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/ws/License.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/ws/Organization.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/ws/Resource.java create mode 100644 src/main/java/org/gcube/datacatalogue/catalogue/ws/User.java create mode 100644 src/main/resources/ehcache.xml create mode 100644 src/main/webapp/WEB-INF/gcube-app.xml create mode 100644 src/main/webapp/WEB-INF/web.xml create mode 100644 src/main/webapp/index.jsp create mode 100644 src/test/java/JavaTests.java create mode 100644 src/test/java/TestJersey.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..38f832c --- /dev/null +++ b/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..9a0e00e --- /dev/null +++ b/.project @@ -0,0 +1,42 @@ + + + catalogue-ws + + + + + + org.eclipse.wst.jsdt.core.javascriptValidator + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + 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..b72a6a4 --- /dev/null +++ b/.settings/.jsdtscope @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..839d647 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..443e085 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +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.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..da1fa8a --- /dev/null +++ b/.settings/org.eclipse.wst.common.component @@ -0,0 +1,10 @@ + + + + + + + + + + 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..f7f0893 --- /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 new file mode 100644 index 0000000..9ce7dcc --- /dev/null +++ b/pom.xml @@ -0,0 +1,224 @@ + + 4.0.0 + + maven-parent + org.gcube.tools + 1.0.0 + + + + org.gcube.datacatalogue + catalogue-ws + war + 1.0.0-SNAPSHOT + catalogue-ws + + + This service allows other services to publish on the catalogue. + + + + + + org.gcube.distribution + maven-smartgears-bom + LATEST + pom + import + + + + + + UTF-8 + 2.25.1 + ${project.basedir}/distro + ${project.build.directory}/${project.build.finalName} + distro + UTF-8 + + + + + net.sf.ehcache + ehcache + 2.10.0 + + + org.gcube.resources.discovery + ic-client + provided + + + org.slf4j + slf4j-api + provided + + + org.gcube.data-catalogue + ckan-util-library + [2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT) + compile + + + org.gcube.core + common-encryption + provided + + + org.gcube.common + authorization-client + provided + + + org.gcube.common + home-library-jcr + [2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT) + compile + + + org.gcube.common + common-authorization + provided + + + org.glassfish.jersey.containers + + jersey-container-servlet-core + ${version.jersey} + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${version.jersey} + + + org.glassfish.jersey.media + jersey-media-json-processing + ${version.jersey} + + + org.glassfish.jersey.media + jersey-media-multipart + ${version.jersey} + + + org.glassfish.jersey.media + jersey-media-sse + ${version.jersey} + + + + + + + + javax.servlet + servlet-api + 3.0-alpha-1 + compile + + + + org.gcube.core + common-smartgears + provided + + + + + + + + + org.glassfish.jersey.test-framework.providers + jersey-test-framework-provider-grizzly2 + ${version.jersey} + test + + + de.grundid.opendatalab + geojson-jackson + 1.8 + + + + + + ${name} + + + org.apache.maven.plugins + maven-war-plugin + 2.1.1 + + + compile + + exploded + + + + + ${webappDirectory} + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.2 + + + ${distroDirectory}/descriptor.xml + + + + + servicearchive + install + + single + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-profile + install + + copy-resources + + + target + + + ${distroDirectory} + true + + profile.xml + + + + + + + + + + diff --git a/profile_test.xml b/profile_test.xml new file mode 100644 index 0000000..f47708f --- /dev/null +++ b/profile_test.xml @@ -0,0 +1,179 @@ + + + + + Developer Information + This section is about developer(s) information + + + + + Extras + Other information about the artifact + + + + + Artifact Information + Artifact's main information + + + + + Name + false + String + Developer's fullname + The fullname of the developer + + /^[a-z ,.'-]+$/i + + onFieldName_onValue + + + + Identifier + true + String + Developer's identifier + The identifier of the developer + + + + Artifact Name + String + The name of the artifact + onFieldName_onValue + + + + Programming language + true + String + The programming language used to create the artifact + + Java + C++ + C + Python + Scala + Javascript + + onValue + + + + Used Framework + false + String + The Framework used to create the artifact + + GWT + Bootstrap + GWT-Bootstrap + JQuery + AKKA + + onValue + + + + Maven Location + false + String + The maven location of the artifact + + https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,} + + + + + + First Release + true + Time + Date of the first release + + + + Size MB + true + Number + The size in megabyte of the artifact + + + + UsageMode + true + String + How the method is expected to be accessed + + Download + as-a-Service by SoBigData Infrastructure + + as-a-Service by third party infrastructure + + + + + + Input + false + Boolean + inputParametersType. See WPS + + + + output + false + Boolean + outputType. See WPS + + + + RelatedPaper + false + String + Insert a complete reference to an associated work. + + + + Field/Scope of use + true + String + + Any use + Non-commercial only + Research only + Non-commercial research only + Private use + Use for developing and providing a service + + + + + + Field/Scope of use + true + String + + Any use + Non-commercial only + Research only + Non-commercial research only + Private use + Use for developing and providing a service + + + + + + spatial + true + GeoJSON + Distribution Area + + + \ No newline at end of file diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/beans/resource/CustomField.java b/src/main/java/org/gcube/datacatalogue/catalogue/beans/resource/CustomField.java new file mode 100644 index 0000000..9b74fc6 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/beans/resource/CustomField.java @@ -0,0 +1,119 @@ +package org.gcube.datacatalogue.catalogue.beans.resource; + +import org.json.simple.JSONObject; + +/** + * A custom field. It also stores index of the category and of the metadata field associated. + * If they are missing, indexes are set to Integer.MAX_VALUE + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class CustomField implements Comparable{ + + private String key; + private String qualifiedKey; + private String value; + private int indexMetadataField = Integer.MAX_VALUE; + private int indexCategory = Integer.MAX_VALUE; + + public CustomField(JSONObject object) { + super(); + this.key = (String)object.get("key"); + this.qualifiedKey = key; + this.value = (String)object.get("value"); + if(key == null || value == null || key.isEmpty()) + throw new IllegalArgumentException("A custom field must have a key and a value! Provided object is " + object.toString()); + } + + /** + * @param key + * @param value + */ + public CustomField(String key, String value) { + super(); + this.key = key; + this.qualifiedKey = key; + this.value = value; + } + + /** + * @param key + * @param value + * @param indexMetadataField + * @param indexCategory + */ + public CustomField(String key, String value, int indexCategory, int indexMetadataField) { + super(); + this.key = key; + this.value = value; + this.indexMetadataField = indexMetadataField; + this.indexCategory = indexCategory; + this.qualifiedKey = key; + } + + public int getIndexMetadataField() { + return indexMetadataField; + } + + public String getQualifiedKey() { + return qualifiedKey; + } + + public void setQualifiedKey(String qualifiedKey) { + this.qualifiedKey = qualifiedKey; + } + + public void setIndexMetadataField(int indexMetadataField) { + this.indexMetadataField = indexMetadataField; + } + + public int getIndexCategory() { + return indexCategory; + } + + public void setIndexCategory(int indexCategory) { + this.indexCategory = indexCategory; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "CustomField [key=" + key + ", qualifiedKey=" + qualifiedKey + + ", value=" + value + ", indexMetadataField=" + + indexMetadataField + ", indexCategory=" + indexCategory + "]"; + } + + @Override + public int compareTo(CustomField o) { + + if(o.indexCategory < 0) + o.indexCategory = Integer.MAX_VALUE; + + if(this.indexCategory < 0) + this.indexCategory = Integer.MAX_VALUE; + + if(this.indexCategory == o.indexCategory){ + if(this.indexMetadataField == o.indexMetadataField) + return 0; + else + return this.indexMetadataField > o.indexMetadataField ? 1 : -1; + } + else + return this.indexCategory > o.indexCategory ? 1 : -1; + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/utils/CachesManager.java b/src/main/java/org/gcube/datacatalogue/catalogue/utils/CachesManager.java new file mode 100644 index 0000000..db3d5f1 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/utils/CachesManager.java @@ -0,0 +1,31 @@ +package org.gcube.datacatalogue.catalogue.utils; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; + +/** + * Handle caches via Ehcache + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class CachesManager { + + private static CacheManager cacheManager; + public static final CachesManager singleton = new CachesManager(); + + public static final String PROFILES_READERS_CACHE = "profile_readers"; + + private CachesManager(){ + cacheManager = CacheManager.newInstance(); + } + + public static Cache getCache(String name){ + return cacheManager.getCache(name); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + cacheManager.shutdown(); + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/utils/CatalogueUtils.java b/src/main/java/org/gcube/datacatalogue/catalogue/utils/CatalogueUtils.java new file mode 100644 index 0000000..351d4fd --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/utils/CatalogueUtils.java @@ -0,0 +1,976 @@ +package org.gcube.datacatalogue.catalogue.utils; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.Element; + +import org.apache.commons.lang.math.NumberUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +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.common.scope.impl.ScopeBean; +import org.gcube.common.scope.impl.ScopeBean.Type; +import org.gcube.datacatalogue.catalogue.beans.resource.CustomField; +import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogue; +import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogueFactory; +import org.gcube.datacatalogue.metadatadiscovery.DataCalogueMetadataFormatReader; +import org.gcube.datacatalogue.metadatadiscovery.bean.MetadataProfile; +import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.DataType; +import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataCategory; +import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataField; +import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataFormat; +import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataGrouping; +import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataTagging; +import org.geojson.GeoJsonObject; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Utils class and methods. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +@SuppressWarnings({"rawtypes","unchecked"}) +public class CatalogueUtils { + + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CatalogueUtils.class); + public static final String HELP_URL_GCUBE_CATALOGUE = "https://wiki.gcube-system.org/gcube/GCube_Data_Catalogue"; + public static final String HELP_KEY = "help"; + public static final String DATASET_KEY = "id"; + public static final String SUCCESS_KEY = "success"; + public static final String MESSAGE_ERROR_KEY = "message"; + public static final String RESULT_KEY = "result"; + public static final String EXTRAS_KEY = "extras"; + public static final String TAGS_KEY = "tags"; + public static final String GROUPS_KEY = "groups"; + public static final String LICENSE_KEY = "license_id"; + public static final String AUTHOR_KEY = "author"; + public static final String RESOURCES_KEY = "resources"; + public static final String AUTHOR_EMAIL_KEY = "author_email"; + public static final String METADATATYPE_KEY = "metadatatype"; + public static final String OWNER_ORG_KEY = "owner_org"; + public static final String TITLE_KEY = "title"; + public static final String VERSION_KEY = "version"; + public static final String SEPARATOR_MULTIPLE_VALUES_FIELD = ","; + public static final int MAX_TAG_CHARS = 100; + + + // ======================================================================= + // GET/PUT CATALOGUE + // ======================================================================= + + /** + * Retrieve an instance of the library for the scope + * @param scope if it is null it is evaluated from the session + * @return + * @throws Exception + */ + public static DataCatalogue getCatalogue(){ + + try{ + String context = ScopeProvider.instance.get(); + logger.debug("Discovering ckan instance into scope " + context); + return DataCatalogueFactory.getFactory().getUtilsPerScope(context); + }catch(Exception e){ + logger.error("Unable to lookup catalogue object here "); + } + + return null; + } + + /** + * Retrieve an instance of the library for the scope + * @param scope if it is null it is evaluated from the session + * @return + * @throws Exception + */ + public static boolean createGroupAsSysAdmin(String title, String groupName, String description) throws Exception{ + + return getCatalogue().createGroup(groupName, title, description) != null; + + } + + // ======================================================================= + // METADATA PROFILE STUFF + // ======================================================================= + + /** + * Returns the names of the metadata profiles in a given context + * @param context + * @return + * @throws Exception + */ + public static List getProfilesNames() throws Exception{ + + Cache profilesCache = CachesManager.getCache(CachesManager.PROFILES_READERS_CACHE); + String context = ScopeProvider.instance.get(); + List toReturn = new ArrayList(); + + DataCalogueMetadataFormatReader reader; + if(profilesCache.isKeyInCache(context)) + reader = (DataCalogueMetadataFormatReader) profilesCache.get(context).getObjectValue(); + else{ + reader = new DataCalogueMetadataFormatReader(); + profilesCache.put(new Element(context, reader)); + } + + List listProfiles = reader.getListOfMetadataProfiles(); + + if(listProfiles != null && !listProfiles.isEmpty()){ + for (MetadataProfile profile : listProfiles) { + toReturn.add(profile.getName()); + } + } + + return toReturn; + } + + /** + * Returns the source xml of the metadata profile (specified via name) in a given context + * @param context + * @return + * @throws Exception + */ + public static String getProfileSource(String profileName) throws Exception{ + + Cache profilesCache = CachesManager.getCache(CachesManager.PROFILES_READERS_CACHE); + String context = ScopeProvider.instance.get(); + + DataCalogueMetadataFormatReader reader; + if(profilesCache.isKeyInCache(context)) + reader = (DataCalogueMetadataFormatReader) profilesCache.get(context).getObjectValue(); + else{ + reader = new DataCalogueMetadataFormatReader(); + profilesCache.put(new Element(context, reader)); + } + + List listProfiles = reader.getListOfMetadataProfiles(); + String xmlToReturn = null; + + if(listProfiles != null && !listProfiles.isEmpty()){ + for (MetadataProfile profile : listProfiles) { + if(profile.getName().equals(profileName)){ + xmlToReturn = reader.getMetadataFormatForMetadataProfile(profile).getMetadataSource(); + break; + } + } + } + + return xmlToReturn; + } + + /** + * Returns the metadataform of the metadata profile (specified via name) in a given context + * @param context + * @return + * @throws Exception + */ + public static MetadataFormat getMetadataProfile(String profileName) throws Exception{ + + Cache profilesCache = CachesManager.getCache(CachesManager.PROFILES_READERS_CACHE); + String context = ScopeProvider.instance.get(); + + DataCalogueMetadataFormatReader reader; + if(profilesCache.isKeyInCache(context)) + reader = (DataCalogueMetadataFormatReader) profilesCache.get(context).getObjectValue(); + else{ + reader = new DataCalogueMetadataFormatReader(); + profilesCache.put(new Element(context, reader)); + } + + List listProfiles = reader.getListOfMetadataProfiles(); + + if(listProfiles != null && !listProfiles.isEmpty()){ + for (MetadataProfile profile : listProfiles) { + if(profile.getName().equals(profileName)){ + return reader.getMetadataFormatForMetadataProfile(profile); + } + } + } + + return null; + } + + // ======================================================================= + // JSON OBJECT MAPPINGS + // ======================================================================= + + /** + * Create a string representing an error message on failure + * @param errorMessage + * @return + */ + public static String createJSONOnFailure(String errorMessage){ + + JSONObject obj = new JSONObject(); + obj.put(HELP_KEY, HELP_URL_GCUBE_CATALOGUE); + obj.put(SUCCESS_KEY, false); + obj.put(MESSAGE_ERROR_KEY, errorMessage); + return obj.toJSONString(); + + } + + /** + * JSONObject containing minimum information to be set + * @return + */ + public static JSONObject createJSONObjectMin(boolean success, String errorMessage){ + + JSONObject obj = new JSONObject(); + obj.put(HELP_KEY, HELP_URL_GCUBE_CATALOGUE); + obj.put(SUCCESS_KEY, success); + if(errorMessage != null) + obj.put(MESSAGE_ERROR_KEY, errorMessage); + return obj; + + } + + // ======================================================================= + // DELEGATE OPS TO CKAN + // ======================================================================= + + /** + * Check resources have at least url/name + * @param json + * @param caller + * @throws Exception + */ + public static void checkResourcesInformation(String json, Caller caller) throws Exception { + + JSONParser parser = new JSONParser(); + JSONObject dataset = (JSONObject)parser.parse(json); + JSONArray resources = (JSONArray)dataset.get(RESOURCES_KEY); + + if(resources == null || resources.isEmpty()) + return; + else{ + + Iterator it = resources.iterator(); + while (it.hasNext()) { + JSONObject resource = (JSONObject) it.next(); + + String name = (String)resource.get("name"); + String url = (String)resource.get("url"); + + if(url == null || name == null || url.isEmpty() || name.isEmpty()) + throw new Exception("Resources must have at least a name and an url"); + + } + } + + } + + /** + * This method validates the incoming json, in this sense: + *
    + *
  • add author + *
  • add author email + *
  • check license (?) + *
+ * @param json + * @param caller + * @return + * @throws Exception + */ + public static String checkBaseInformation(String json, Caller caller) throws Exception{ + + JSONParser parser = new JSONParser(); + JSONObject dataset = (JSONObject)parser.parse(json); + JSONObject profile = getUserProfile(); + + // check license + String licenseId = (String)dataset.get(LICENSE_KEY); + + if(licenseId == null || licenseId.isEmpty()) + throw new Exception("You must specify a license identifier to be attached to the item. License list can be retrieved invoking license methods"); + + // set author and author email + JSONObject profileValues = (JSONObject)profile.get(RESULT_KEY); + dataset.put(AUTHOR_KEY, profileValues.get(FULLNAME_IN_PROFILE_KEY)); + dataset.put(AUTHOR_EMAIL_KEY, profileValues.get(EMAIL_IN_PROFILE_KEY)); + + // version + String version = (String)dataset.get(VERSION_KEY); + if(version == null || version.isEmpty()){ + version = "1"; + dataset.put(VERSION_KEY, version); + } + + // owner organization must be specified if the token belongs to a VRE + ScopeBean scopeBean = new ScopeBean(ScopeProvider.instance.get()); + String ownerOrgFromScope = scopeBean.name(); + boolean isVREToken = scopeBean.is(Type.VRE); + String ownerOrg = (String)dataset.get(OWNER_ORG_KEY); + + if(isVREToken){ + dataset.put(OWNER_ORG_KEY, ownerOrgFromScope.toLowerCase().replace(" ", "_")); + }else if(ownerOrg == null || ownerOrg.isEmpty()) + throw new Exception("You must specify the field owner_org in which the item should be published!"); + + return dataset.toJSONString(); + + } + + /** + * This method validate the incoming json dataset wrt a metadata profile + * @param json + * @param caller + * @param profiles + * @return + * @throws Exception + */ + public static String validateAgainstProfile(String json, Caller caller, List profiles) throws Exception { + + JSONParser parser = new JSONParser(); + JSONObject obj = (JSONObject)parser.parse(json); + JSONArray extrasArrayOriginal = (JSONArray)obj.get(EXTRAS_KEY); + JSONArray groupsArrayOriginal = (JSONArray)obj.get(GROUPS_KEY); + JSONArray tagsArrayOriginal = (JSONArray)obj.get(TAGS_KEY); + + if(extrasArrayOriginal == null || extrasArrayOriginal.isEmpty()) + throw new Exception("'extras' field is missing in context where metadata profile(s) are defined!"); + + // get the metadata profile + CustomField metadataTypeCF = null; + List customFields = new ArrayList(); + Iterator iterator = extrasArrayOriginal.iterator(); + while (iterator.hasNext()) { + JSONObject object = (JSONObject) iterator.next(); + CustomField cf = new CustomField(object); + if(cf.getKey().equals(METADATATYPE_KEY)) + metadataTypeCF = cf; + else + customFields.add(cf); + } + + if(metadataTypeCF == null) + throw new Exception("'" + METADATATYPE_KEY + "' field is missing in context where metadata profile(s) are defined!"); + + if(groupsArrayOriginal == null) + groupsArrayOriginal = new JSONArray(); + + if(tagsArrayOriginal == null) + tagsArrayOriginal = new JSONArray(); + + // fetch the profile by metadatatype TODO EHCache here + MetadataFormat profile = null; + String profileNameMatched = null; + for (String profileName : profiles) { + + profile = getMetadataProfile(profileName); + if(profile.getMetadataType().equals(metadataTypeCF.getValue())){ + profileNameMatched = profileName; + break; + } + else + profile = null; + } + + if(profile == null) + throw new Exception("'" + METADATATYPE_KEY + "' specified as custom field doesn't match any of the profile defined in this context!"); + + else{ + + + JSONArray extrasArrayUpdated = new JSONArray(); + List metadataFields = profile.getMetadataFields(); + + if(metadataFields == null || metadataFields.isEmpty()) + extrasArrayUpdated.addAll(extrasArrayOriginal); + else{ + + List categories = profile.getMetadataCategories(); + List categoriesIds = new ArrayList(); + if(categories == null) + logger.warn("No category defined into profile " + profileNameMatched); + else{ + + for (MetadataCategory metadataCategory : categories) { + categoriesIds.add(metadataCategory.getId()); // save them later for matching with custom fields + JSONObject catJsonObj = new JSONObject(); + JSONObject value = new JSONObject(); + value.put("id", metadataCategory.getId()); + value.put("title", metadataCategory.getTitle()); + value.put("description", metadataCategory.getDescription()); + catJsonObj.put("key", metadataCategory.getCategoryQName()); + catJsonObj.put("value", value.toString()); + extrasArrayUpdated.add(catJsonObj); + } + + } + + // the list of already validated customFields + List validatedCustomFields = new ArrayList(customFields.size()); + + // keep track of mandatory (only) fields + Map fieldsMandatoryLowerBoundMap = new HashMap(); + Map numberFieldsSameKeyMap = new HashMap(); + + // now validate fields + int metadataIndex = 0; + for (MetadataField metadataField : metadataFields) { + + int categoryIdIndex = categoriesIds.indexOf(metadataField.getCategoryRef()); + logger.trace("Found index for this category (" + metadataField.getCategoryRef() + ")" + categoryIdIndex); + List validCFs = validateAgainstMetadataField( + metadataIndex, + categoryIdIndex, + customFields, + tagsArrayOriginal, + groupsArrayOriginal, + metadataField, + categories, + fieldsMandatoryLowerBoundMap, + numberFieldsSameKeyMap); + validatedCustomFields.addAll(validCFs); + metadataIndex++; + + } + + // check mandatory fields + Iterator> iteratorLowerBounds = fieldsMandatoryLowerBoundMap.entrySet().iterator(); + while (iteratorLowerBounds.hasNext()) { + Map.Entry entry = (Map.Entry) iteratorLowerBounds + .next(); + int lowerBound = entry.getValue(); + int inserted = numberFieldsSameKeyMap.get(entry.getKey()); + logger.trace("Field with key " + entry.getKey() + " has been found " + inserted + " times and its lower bound is " + lowerBound); + if(inserted < lowerBound) + throw new Exception("Field with key '" + entry.getKey() + "' is mandatory, but it's not present among the provided fields or its cardinality is not respected (" + lowerBound + ")."); + } + + // sort validated custom fields and add to the extrasArrayUpdated json array + Collections.sort(validatedCustomFields); + + logger.trace("****** SORTED LIST " + validatedCustomFields); + + // add missing fields with no match (append them at the end, since no metadataIndex or categoryIndex was defined for them) + for(CustomField cf : customFields) + validatedCustomFields.add(cf); + + for (CustomField customField : validatedCustomFields) { + JSONObject jsonObj = new JSONObject(); + jsonObj.put("key", customField.getQualifiedKey()); + jsonObj.put("value", customField.getValue()); + extrasArrayUpdated.add(jsonObj); + } + + // add metadatatype field + JSONObject metadataTypeJSON = new JSONObject(); + metadataTypeJSON.put("key", metadataTypeCF.getKey()); + metadataTypeJSON.put("value", metadataTypeCF.getValue()); + extrasArrayUpdated.add(metadataTypeJSON); + } + + obj.put(TAGS_KEY, tagsArrayOriginal); + obj.put(GROUPS_KEY, groupsArrayOriginal); + obj.put(EXTRAS_KEY, extrasArrayUpdated); + } + + return obj.toJSONString(); + } + + + /** + * Validate this field and generate a new value (or returns the same if there is nothing to update) + * @param metadataIndex + * @param categoryIndex + * @param customFields + * @param tagsArrayOriginal + * @param groupsArrayOriginal + * @param metadataField + * @param categories + * @param numberFieldsSameKeyMap + * @param fieldsMandatoryLowerBoundMap + * @return + * @throws Exception + */ + private static List validateAgainstMetadataField( + int metadataIndex, + int categoryIndex, + List customFields, + JSONArray tagsArrayOriginal, + JSONArray groupsArrayOriginal, + MetadataField metadataField, + List categories, Map fieldsMandatoryLowerBoundMap, Map numberFieldsSameKeyMap) throws Exception { + + List toReturn = new ArrayList(); + String metadataFieldName = metadataField.getFieldName(); + + // maintain status about: is this the first time this field is parsed? + int fieldsFoundWithThisKey = 0; + + Iterator iterator = customFields.iterator(); + while (iterator.hasNext()) { + CustomField cf = (CustomField) iterator.next(); + if(cf.getKey().equals(metadataFieldName)){ + + validate(cf, metadataField, (fieldsFoundWithThisKey == 0)); + fieldsFoundWithThisKey ++; + cf.setIndexCategory(categoryIndex); + cf.setIndexMetadataField(metadataIndex); + checkAsGroup(cf, metadataField, groupsArrayOriginal); + checkAsTag(cf, metadataField, tagsArrayOriginal); + toReturn.add(cf); + iterator.remove(); + + } + } + + + if(metadataField.getMandatory()){ + + int lowerBound = 1; + if(fieldsMandatoryLowerBoundMap.containsKey(metadataFieldName)) + lowerBound = fieldsMandatoryLowerBoundMap.get(metadataFieldName) + 1; + + fieldsMandatoryLowerBoundMap.put(metadataFieldName, lowerBound); + + int countPerFields = fieldsFoundWithThisKey; + if(numberFieldsSameKeyMap.containsKey(metadataFieldName)) + countPerFields = numberFieldsSameKeyMap.get(metadataFieldName) + countPerFields; + + numberFieldsSameKeyMap.put(metadataFieldName, countPerFields); + + } + + return toReturn; + + } + + /** + * Check if a tag must be generated + * @param fieldToValidate + * @param metadataField + * @param tagsArrayOriginal + */ + private static void checkAsTag(CustomField fieldToValidate, + MetadataField metadataField, JSONArray tagsArrayOriginal) { + MetadataTagging tagging = metadataField.getTagging(); + if(tagging != null && tagging.getCreate()){ + + String tag = ""; + + switch(tagging.getTaggingValue()){ + case onFieldName: + tag = fieldToValidate.getKey(); + break; + case onValue: + tag = fieldToValidate.getValue(); + break; + case onFieldName_onValue: + tag = fieldToValidate.getKey() + tagging.getSeparator() + fieldToValidate.getValue(); + break; + case onValue_onFieldName: + tag = fieldToValidate.getValue() + tagging.getSeparator() + fieldToValidate.getKey(); + break; + default: + return; + } + + tag = tag.substring(0, MAX_TAG_CHARS > tag.length() ? tag.length() : MAX_TAG_CHARS); + logger.debug("Tag is " + tag); + JSONObject tagJSON = new JSONObject(); + tagJSON.put("name", tag); + tagJSON.put("display_name", tag); + tagsArrayOriginal.add(tagJSON); + + } + + logger.trace("CURRENT TAGS " + tagsArrayOriginal); + + } + + /** + * Check if a group must be generated + * @param fieldToValidate + * @param metadataField + * @param groupsArrayOriginal + */ + private static void checkAsGroup(CustomField fieldToValidate, + MetadataField metadataField, JSONArray groupsArrayOriginal) { + + logger.debug("Custom field is " + fieldToValidate); + logger.debug("MetadataField field is " + metadataField); + logger.debug("JSONArray field is " + groupsArrayOriginal); + + MetadataGrouping grouping = metadataField.getGrouping(); + if(grouping != null && grouping.getCreate()){ + + final List groupNames = new ArrayList(); + + switch(grouping.getGroupingValue()){ + case onFieldName: + groupNames.add(fieldToValidate.getKey()); + break; + case onValue: + groupNames.add(fieldToValidate.getValue()); + break; + case onFieldName_onValue: + case onValue_onFieldName: + groupNames.add(fieldToValidate.getKey()); + groupNames.add(fieldToValidate.getValue()); + break; + default: + return; + } + + for (String title : groupNames) { + JSONObject group = new JSONObject(); + group.put("name", title); + groupsArrayOriginal.add(group); + } + + new Thread() { + @Override + public void run() { + for (String title : groupNames) + try { + createGroupAsSysAdmin(title, title, ""); + } catch (Exception e) { + logger.error("Failed to create group with title " + title, e); + } + } + }.start(); + + } + + } + + /** + * Validate the single field + * @param fieldToValidate + * @param metadataField + * @param isFirst + * @return + * @throws Exception + */ + private static void validate(CustomField fieldToValidate, + MetadataField metadataField, boolean isFirst) throws Exception { + + DataType dataType = metadataField.getDataType(); + String regex = metadataField.getValidator() != null ? metadataField.getValidator().getRegularExpression() : null; + boolean hasControlledVocabulary = metadataField.getVocabulary() != null; + boolean isMultiselection = hasControlledVocabulary ? metadataField.getVocabulary().isMultiSelection() : false; + + String value = fieldToValidate.getValue(); + String key = fieldToValidate.getKey(); + + if((value == null || value.isEmpty())) + if(metadataField.getMandatory() || hasControlledVocabulary) + throw new Exception("Mandatory field with name '" + key + "' doesn't have a value"); + else return; + + + if(!isMultiselection && !isFirst) + throw new Exception("Field with key '" + key + "' cannot be repeated more than once!"); + + switch(dataType){ + + case String: + case Text: + + if(regex != null && !value.matches(regex)) + throw new Exception("Field with key '" + key + "' doesn't match the provided regular expression (" + regex + ")!"); + + if(hasControlledVocabulary){ + + List valuesVocabulary = metadataField.getVocabulary().getVocabularyFields(); + + boolean match = false; + for (String valueVocabulary : valuesVocabulary) { + match = value.equals(valueVocabulary); + if(match) + break; + } + + if(!match) + throw new Exception("Field with key '" + key + " doesn't match any of the vocabulary's values!"); + + } + + break; + case Time: + + if(!isValidDate(value)) + throw new Exception("Field with key '" + key + "' doesn't seem a valid time!"); + + break; + case Time_Interval: + + String[] timeValues = value.split("/"); + for (int i = 0; i < timeValues.length; i++) { + String time = timeValues[i]; + if(!isValidDate(time)) + throw new Exception("Field with key '" + key + "' doesn't seem a valid time interval!"); + } + + break; + case Times_ListOf: + + String[] timeIntervals = value.split(","); + for (int i = 0; i < timeIntervals.length; i++) { + String[] timeIntervalValues = timeIntervals[i].split("/"); + for (i = 0; i < timeIntervalValues.length; i++) { + String time = timeIntervalValues[i]; + if(!isValidDate(time)) + throw new Exception("Field with key '" + key + "' doesn't seem a valid list of times!"); + } + } + + break; + case Boolean: + + if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + + }else + throw new Exception("Field with key '" + key + "' doesn't seem a valid boolean value!"); + + break; + case Number: + + if(!NumberUtils.isNumber(value)) + throw new Exception("Field's value with key '" + key + "' is not a valid number!"); + + break; + case GeoJSON: + + // validate + try{ + new ObjectMapper().readValue(fieldToValidate.getValue(), GeoJsonObject.class); + }catch(Exception e){ + throw new Exception("GeoJSON field with key '" + key + "' seems not valid!"); + } + + break; + default: + + //??? + + } + + // replace key by prepending the qualified name of the category, if needed + fieldToValidate.setQualifiedKey(metadataField.getCategoryFieldQName()); + + } + + private static final SimpleDateFormat DATE_SIMPLE = new SimpleDateFormat("yyyy-MM-dd"); + private static final SimpleDateFormat DATE_HOUR_MINUTES = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + /** + * Validate a time date against a formatter + * @param value + * @param formatter + * @return + */ + private static boolean isValidDate(String value) { + + try{ + DATE_HOUR_MINUTES.parse(value); + return true; + }catch(Exception e){ + logger.trace("failed to parse date with hours and minutes, trying the other one"); + try{ + DATE_SIMPLE.parse(value); + return true; + }catch(Exception e2){ + logger.warn("failed to parse date with simple format, returning false"); + return false; + } + } + + } + + /** + * Execute the get + * @param caller + * @param context + * @param method + * @param uriInfo + * @return + * @throws Exception + */ + public static String delegateGet(Caller caller, String context, String method, UriInfo uriInfo){ + + DataCatalogue catalogue = CatalogueUtils.getCatalogue(); + String username = caller.getClient().getId(); + + if(catalogue == null){ + String msg = "There is no catalogue instance in context " + context + " or a temporary problem arised."; + logger.warn(msg); + return CatalogueUtils.createJSONOnFailure(msg); + }else{ + try(CloseableHttpClient client = HttpClientBuilder.create().build();){ + + String authorization = catalogue.getApiKeyFromUsername(username); + String requestPath = catalogue.getCatalogueUrl().endsWith("/") ? catalogue.getCatalogueUrl() : catalogue.getCatalogueUrl() + "/"; + requestPath += method; + MultivaluedMap undecodedParams = uriInfo.getQueryParameters(false); + Iterator>> iterator = undecodedParams.entrySet().iterator(); + + while (iterator.hasNext()) { + Map.Entry> entry = (Map.Entry>) iterator + .next(); + + if(entry.getKey().equals("gcube-token")) + continue; + else{ + + List values = entry.getValue(); + for (String value : values) { + requestPath += entry.getKey() + "=" + value + "&"; + } + } + } + + if(requestPath.endsWith("&")) + requestPath = requestPath.substring(0, requestPath.length() - 1); + HttpGet request = new HttpGet(requestPath); + if(authorization != null) + request.addHeader(Constants.AUTH_CKAN_HEADER, authorization); + + logger.trace("******* REQUEST URL IS " + requestPath); + + HttpEntity entityRes = client.execute(request).getEntity(); + String json = EntityUtils.toString(entityRes); + + // substitute "help" field + JSONParser parser = new JSONParser(); + JSONObject obj = (JSONObject) parser.parse(json); + obj.put(HELP_KEY, HELP_URL_GCUBE_CATALOGUE); + return obj.toJSONString(); + + }catch(Exception e){ + logger.error("Failed to serve the request", e); + return CatalogueUtils.createJSONOnFailure("Failed to serve the request: " + e.getMessage()); + } + } + + } + + /** + * Execute the post + * @param caller + * @param context + * @param groupShow + * @param json + * @param uriInfo + * @throws Exception + */ + public static String delegatePost(Caller caller, String context, + String method, String json, UriInfo uriInfo){ + + String username = caller.getClient().getId(); + DataCatalogue catalogue = CatalogueUtils.getCatalogue(); + + if(catalogue == null){ + String msg = "There is no catalogue instance in context " + context + " or a temporary problem arised."; + logger.warn(msg); + return CatalogueUtils.createJSONOnFailure(msg); + }else{ + + try(CloseableHttpClient client = HttpClientBuilder.create().build();){ + + String authorization = catalogue.getApiKeyFromUsername(username); + String requestPath = catalogue.getCatalogueUrl().endsWith("/") ? catalogue.getCatalogueUrl() : catalogue.getCatalogueUrl() + "/"; + requestPath += method + "?"; + + MultivaluedMap undecodedParams = uriInfo.getQueryParameters(false); + Iterator>> iterator = undecodedParams.entrySet().iterator(); + + while (iterator.hasNext()) { + Map.Entry> entry = (Map.Entry>) iterator + .next(); + + if(entry.getKey().equals("gcube-token")) + continue; + else{ + + List values = entry.getValue(); + for (String value : values) { + requestPath += entry.getKey() + "=" + value + "&"; + } + } + } + + if(requestPath.endsWith("&")) + requestPath = requestPath.substring(0, requestPath.length() - 1); + + logger.trace("POST request url is going to be " + requestPath); + + HttpPost request = new HttpPost(requestPath); + request.addHeader(Constants.AUTH_CKAN_HEADER, authorization); + logger.trace("Sending json to CKAN is " + json); + StringEntity params = new StringEntity(json, ContentType.APPLICATION_JSON); + request.setEntity(params); + HttpEntity entityRes = client.execute(request).getEntity(); + String jsonRes = EntityUtils.toString(entityRes); + + logger.trace("Result from CKAN is " + jsonRes); + + // substitute "help" field + JSONParser parser = new JSONParser(); + JSONObject obj = (JSONObject) parser.parse(jsonRes); + obj.put(HELP_KEY, HELP_URL_GCUBE_CATALOGUE); + return obj.toJSONString(); + + }catch(Exception e){ + logger.error("Failed to serve the request", e); + return CatalogueUtils.createJSONOnFailure("Failed to serve the request: " + e.getMessage()); + } + } + + } + + // ======================================================================= + // SOCIAL FACILITIES + // ======================================================================= + + public static final String EMAIL_IN_PROFILE_KEY = "email"; + public static final String FULLNAME_IN_PROFILE_KEY = "fullname"; + + /** + * Execute the GET http request at this url, and return the result as string + * @return + * @throws Exception + */ + public static JSONObject getUserProfile() throws Exception{ + + GcoreEndpointReaderSNL socialService = new GcoreEndpointReaderSNL(); + String socialServiceUrl = socialService.getServiceBasePath(); + String url = socialServiceUrl + "2/users/get-profile"; + try(CloseableHttpClient client = HttpClientBuilder.create().build();){ + HttpGet getRequest = new HttpGet(url + "?gcube-token=" + SecurityTokenProvider.instance.get()); + HttpResponse response = client.execute(getRequest); + JSONParser parser = new JSONParser(); + return (JSONObject)parser.parse(EntityUtils.toString(response.getEntity())); + }catch(Exception e){ + logger.error("error while performing get method " + e.toString()); + throw e; + } + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/utils/Constants.java b/src/main/java/org/gcube/datacatalogue/catalogue/utils/Constants.java new file mode 100644 index 0000000..28d821e --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/utils/Constants.java @@ -0,0 +1,79 @@ +package org.gcube.datacatalogue.catalogue.utils; + +/** + * Constants used within this service. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class Constants { + + // methods + public final static String SHOW_METHOD = "show"; + public final static String CREATE_METHOD = "create"; + public final static String UPDATE_METHOD = "update"; + public final static String PURGE_METHOD = "purge"; + public final static String DELETE_METHOD = "delete"; + public final static String PATCH_METHOD = "patch"; + + // this service's resources + public final static String USERS = "api/user"; + public final static String ORGANIZATIONS = "api/organizations"; + public final static String GROUPS = "api/groups"; + public final static String ITEMS = "api/items"; + public static final String PROFILES = "api/profiles"; + public final static String RESOURCES = "api/resources"; + public final static String LICENSES = "api/licenses"; + public static final String LIST_METHOD = "api/list"; + + // api rest path CKAN + public final static String CKAN_API_PATH = "/api/3/action/"; + + // ckan header authorization + public final static String AUTH_CKAN_HEADER = "Authorization"; + + // ckan Groups subpaths methods (to be appended to the catalogue url) + public final static String GROUP_SHOW = CKAN_API_PATH + "group_show?"; + public final static String GROUP_CREATE = CKAN_API_PATH + "group_create"; + public final static String GROUP_DELETE = CKAN_API_PATH + "group_delete"; + public final static String GROUP_PURGE = CKAN_API_PATH + "group_purge"; + public final static String GROUP_UPDATE = CKAN_API_PATH + "group_patch"; + public final static String GROUP_PATCH = CKAN_API_PATH + "group_patch"; + public final static String GROUP_LIST = CKAN_API_PATH + "group_list"; + + // ckan Organizations subpaths methods (to be appended to the catalogue url) + public final static String ORGANIZATION_SHOW = CKAN_API_PATH + "organization_show?"; + public final static String ORGANIZATION_CREATE = CKAN_API_PATH + "organization_create"; + public final static String ORGANIZATION_DELETE = CKAN_API_PATH + "organization_delete"; + public final static String ORGANIZATION_PURGE = CKAN_API_PATH + "organization_purge"; + public final static String ORGANIZATION_PATCH = CKAN_API_PATH + "organization_patch"; + public final static String ORGANIZATION_UPDATE = CKAN_API_PATH + "organization_update"; + public final static String ORGANIZATION_LIST = CKAN_API_PATH + "organization_list"; + + // ckan User subpaths methods (to be appended to the catalogue url) + public final static String USER_SHOW = CKAN_API_PATH + "user_show?"; + public final static String USER_CREATE = CKAN_API_PATH + "user_create"; + public final static String USER_DELETE = CKAN_API_PATH + "user_delete"; + public final static String USER_UPDATE = CKAN_API_PATH + "user_update"; + + // ckan Dataset/package subpath methods (to be appended to the catalogue url) + public final static String ITEM_SHOW = CKAN_API_PATH + "package_show?"; + public final static String ITEM_CREATE = CKAN_API_PATH + "package_create"; + public final static String ITEM_DELETE = CKAN_API_PATH + "package_delete"; + public final static String ITEM_PURGE = CKAN_API_PATH + "dataset_purge"; + public final static String ITEM_PATCH = CKAN_API_PATH + "package_patch"; + public final static String ITEM_UPDATE = CKAN_API_PATH + "package_update"; + + // ckan Resource subpath methods (to be appended to the catalogue url) + public final static String RESOURCE_SHOW = CKAN_API_PATH + "resource_show?"; + public final static String RESOURCE_CREATE = CKAN_API_PATH + "resource_create"; + public final static String RESOURCE_DELETE = CKAN_API_PATH + "resource_delete"; + public final static String RESOURCE_UPDATE = CKAN_API_PATH + "resource_update"; + public final static String RESOURCE_PATCH = CKAN_API_PATH + "resource_patch"; + + // licenses + public final static String LICENSES_SHOW = CKAN_API_PATH + "license_list?"; + + // ckan capabilities + public static final String PROFILES_NAMES_SHOW = "profile_names/"; + public static final String PROFILE_SHOW = "profile/"; + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/utils/GcoreEndpointReaderSNL.java b/src/main/java/org/gcube/datacatalogue/catalogue/utils/GcoreEndpointReaderSNL.java new file mode 100644 index 0000000..28a6a44 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/utils/GcoreEndpointReaderSNL.java @@ -0,0 +1,68 @@ +/** + * + */ +package org.gcube.datacatalogue.catalogue.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 GcoreEndpointReaderSNL { + + 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(GcoreEndpointReaderSNL.class); + private String serviceBasePath; + + /** + * Discover the gcore endpoint for the social networking service. + * @param scope the scope + * @throws Exception the exception + */ + public GcoreEndpointReaderSNL() throws Exception { + + String currentScope = ScopeProvider.instance.get(); + + try{ + SimpleQuery query = queryFor(GCoreEndpoint.class); + query.addCondition(String.format("$resource/Profile/ServiceClass/text() eq '%s'",serviceClass)); + query.addCondition("$resource/Profile/DeploymentData/Status/text() eq 'ready'"); + query.addCondition(String.format("$resource/Profile/ServiceName/text() eq '%s'",serviceName)); + query.setResult("$resource/Profile/AccessPoint/RunningInstanceInterfaces//Endpoint[@EntryName/string() eq \""+resource+"\"]/text()"); + + DiscoveryClient client = client(); + List endpoints = client.submit(query); + if (endpoints == null || endpoints.isEmpty()) + throw new Exception("Cannot retrieve the GCoreEndpoint serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+currentScope); + + this.serviceBasePath = endpoints.get(0); + + if(serviceBasePath==null) + throw new Exception("Endpoint:"+resource+", is null for serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+currentScope); + + serviceBasePath = serviceBasePath.endsWith("/") ? serviceBasePath : serviceBasePath + "/"; + + }catch(Exception e){ + String error = "An error occurred during GCoreEndpoint discovery, serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+currentScope +"."; + logger.error(error, e); + throw new Exception(error); + } + } + + /** + * @return the ResourceEntyName + */ + public String getServiceBasePath() { + return serviceBasePath; + } +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/utils/PackageCreatePostActions.java b/src/main/java/org/gcube/datacatalogue/catalogue/utils/PackageCreatePostActions.java new file mode 100644 index 0000000..77eff6e --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/utils/PackageCreatePostActions.java @@ -0,0 +1,109 @@ +package org.gcube.datacatalogue.catalogue.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogue; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Actions to performa after a package has been correctly created on ckan. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class PackageCreatePostActions extends Thread { + + private String packageId; + private String context; + private String token; + private String username; + private JSONArray tags; + private String title; + private static final String ITEM_URL = "Item URL"; + private static Logger logger = LoggerFactory.getLogger(PackageCreatePostActions.class); + + /** + * @param packageId + * @param context + * @param tags + * @param title + */ + public PackageCreatePostActions( + String username, + String packageId, + String context, + String token, + JSONArray tags, + String title) { + super(); + this.packageId = packageId; + this.context = context; + this.token = token; + this.username = username; + this.tags = tags; + this.title = title; + } + + @Override + public void run() { + + try{ + + ScopeProvider.instance.set(context); + SecurityTokenProvider.instance.set(token); + + DataCatalogue utils = CatalogueUtils.getCatalogue(); + String apiKey = utils.getApiKeyFromUsername(username); + utils.setSearchableField(packageId, true); + + // add also this information as custom field + String datasetUrl = utils.getUnencryptedUrlFromDatasetIdOrName(packageId); + Map> addField = new HashMap>(); + addField.put(ITEM_URL, Arrays.asList(datasetUrl)); + utils.patchProductCustomFields(packageId, apiKey, addField); + + JSONObject profile = CatalogueUtils.getUserProfile(); + + // set author and author email + JSONObject profileValues = (JSONObject)profile.get(CatalogueUtils.RESULT_KEY); + String fullnameUser = (String) profileValues.get(CatalogueUtils.FULLNAME_IN_PROFILE_KEY); + + + List tagsList = null; + + if(tags != null){ + tagsList = new ArrayList(); + for(int i = 0; i < tags.size(); i++){ + JSONObject obj = (JSONObject)(tags.get(i)); + tagsList.add((String)(obj.get("display_name"))); + } + } + + // write notification post + WritePostCatalogueManagerThread threadWritePost = + new WritePostCatalogueManagerThread( + username, + context, + title, + datasetUrl, + false, // send notification to other people + tagsList, + fullnameUser, + token + ); + threadWritePost.start(); + + }catch(Exception e){ + logger.error("Error while executing post creation actions", e); + } + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/utils/WritePostCatalogueManagerThread.java b/src/main/java/org/gcube/datacatalogue/catalogue/utils/WritePostCatalogueManagerThread.java new file mode 100644 index 0000000..ec70e4d --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/utils/WritePostCatalogueManagerThread.java @@ -0,0 +1,287 @@ +package org.gcube.datacatalogue.catalogue.utils; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.List; + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.scope.api.ScopeProvider; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Let the Product Catalogue Manager write a post in a VRE and alert there is a new product + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class WritePostCatalogueManagerThread extends Thread { + + private static final String APPLICATION_ID_CATALOGUE_MANAGER = "org.gcube.datacatalogue.ProductCatalogue"; + private static final String NOTIFICATION_MESSAGE = "Dear members,
The item '$PRODUCT_TITLE' has been just published by $USER_FULLNAME.
You can find it here: $PRODUCT_URL
"; + private static final String SOCIAL_SERVICE_APPLICATION_TOKEN = "/2/tokens/generate-application-token"; + private static final String SOCIAL_SERVICE_WRITE_APPLICATION_POST = "/2/posts/write-post-app"; + private static final String MEDIATYPE_JSON = "application/json"; + private static Logger logger = LoggerFactory.getLogger(WritePostCatalogueManagerThread.class); + private String username; + private String scope; + private String productTitle; + private String productUrl; + private boolean enableNotification; + private List hashtags; + private String userFullName; + private String token; + + /** + * @param token + * @param scope + * @param productTitle + * @param productUrl + * @param enableNotification + * @param hashtags + * @param userFullName + */ + public WritePostCatalogueManagerThread( + String username, String scope, + String productTitle, String productUrl, boolean enableNotification, + List hashtags, String userFullName, String token) { + super(); + this.username = username; + this.scope = scope; + this.productTitle = productTitle; + this.productUrl = productUrl; + this.enableNotification = enableNotification; + this.hashtags = hashtags; + this.userFullName = userFullName; + this.token = token; + } + + @Override + public void run() { + + try{ + + SecurityTokenProvider.instance.set(token); + + logger.info("Started request to write application post " + + "for new product created. Scope is " + scope + " and " + + "token is " + token.substring(0, 10) + "****************"); + + // set token and scope + ScopeProvider.instance.set(scope); + SecurityTokenProvider.instance.set(token); + + // write + writeProductPost( + productTitle, + productUrl, + userFullName, + hashtags, + enableNotification + ); + + }catch(Exception e){ + logger.error("Failed to write the post because of the following error ", e); + }finally{ + SecurityTokenProvider.instance.reset(); + ScopeProvider.instance.reset(); + } + } + + /** + * Send notification to vre members about the created product by writing a post. + * @param productName the title of the product + * @param productUrl the url of the product + * @param hashtags a list of product's hashtags + * @throws Exception + */ + private static void writeProductPost(String productName, String productUrl, String userFullname, List hashtags, boolean enablePostNotification) throws Exception{ + + // discover service endpoint for the social networking library + String currentScope = ScopeProvider.instance.get(); + String tokenUser = SecurityTokenProvider.instance.get(); + + logger.info("Current scope for writeProductPost is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************"); + GcoreEndpointReaderSNL socialService = new GcoreEndpointReaderSNL(); + String basePath = socialService.getServiceBasePath(); + + if(basePath == null){ + + logger.error("Unable to write a post because there is no social networking service available"); + + }else{ + + // check base path form + basePath = basePath.endsWith("/") ? basePath : basePath + "/"; + + try(CloseableHttpClient client = HttpClientBuilder.create().build();){ + + String pathTokenApp = basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser; + String tokenApp = requireAppToken(client, pathTokenApp); + if(tokenApp != null){ + String pathWritePost = basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + tokenApp; + writePost(client, pathWritePost, productName, productUrl, userFullname, hashtags, enablePostNotification); + } + + }catch(Exception e){ + logger.error("Failed to create a post", e); + } + } + } + + /** + * Require the application token + * @param tokenUser + * @param basePath + * @param client + * @return + */ + private static String requireAppToken(CloseableHttpClient client, String path){ + + String token = null; + try{ + + HttpResponse response = performRequest(client, path, "{\"app_id\":\"" + APPLICATION_ID_CATALOGUE_MANAGER + "\"}"); + + int statusTokenGenerate = response.getStatusLine().getStatusCode(); + + if(statusTokenGenerate == HttpURLConnection.HTTP_CREATED){ + + // extract token + JSONObject obj = getJSONObject(response); + if(((Boolean) obj.get("success"))) + token = (String)obj.get("result"); + else + return null; + + }else if(statusTokenGenerate == HttpURLConnection.HTTP_MOVED_TEMP + || statusTokenGenerate == HttpURLConnection.HTTP_MOVED_PERM + || statusTokenGenerate == HttpURLConnection.HTTP_SEE_OTHER){ + + // re-execute + Header[] locations = response.getHeaders("Location"); + Header lastLocation = locations[locations.length - 1]; + String realLocation = lastLocation.getValue(); + logger.debug("New location is " + realLocation); + token = requireAppToken(client, realLocation); + + }else + return null; + + }catch(Exception e){ + logger.error("Failed to retrieve application token", e); + } + + logger.info("Returning app token " + (token != null ? token.substring(0, 10) + "*************************" : null)); + return token; + } + + /** + * Write post request + * @param client + * @param applicationToken + * @param productName + * @param productUrl + * @param userFullname + * @param hashtags + */ + private static void writePost(CloseableHttpClient client, String path, String productName, String productUrl, String userFullname, List hashtags, + boolean enablePostNotification) { + + try{ + + // replace + String message = NOTIFICATION_MESSAGE.replace("$PRODUCT_TITLE", productName).replace("$PRODUCT_URL", productUrl).replace("$USER_FULLNAME", userFullname); + + if(hashtags != null && !hashtags.isEmpty()) + for (String hashtag : hashtags) { + String modifiedHashtag = hashtag.replaceAll(" ", "_").replace("_+", "_"); + if(modifiedHashtag.endsWith("_")) + modifiedHashtag = modifiedHashtag.substring(0, modifiedHashtag.length() - 1); + message += " #" + modifiedHashtag; // ckan accepts tag with empty spaces, we don't + } + + logger.info("The post that is going to be written is -> " + message); + + HttpResponse response = performRequest(client, path, "{\"text\":\"" + message + "\", \"enable_notification\" : "+ enablePostNotification+ "}"); + int statusWritePost = response.getStatusLine().getStatusCode(); + + if(statusWritePost == HttpURLConnection.HTTP_CREATED){ + + // extract token + JSONObject obj = getJSONObject(response); + if(((Boolean) obj.get("success"))) + logger.info("Post written"); + else + logger.info("Failed to write the post " + obj.get("message")); + + }else if(statusWritePost == HttpURLConnection.HTTP_MOVED_TEMP + || statusWritePost == HttpURLConnection.HTTP_MOVED_PERM + || statusWritePost == HttpURLConnection.HTTP_SEE_OTHER){ + + // re-execute + Header[] locations = response.getHeaders("Location"); + Header lastLocation = locations[locations.length - 1]; + String realLocation = lastLocation.getValue(); + logger.debug("New location is " + realLocation); + writePost(client, realLocation, productName, productUrl, userFullname, hashtags, enablePostNotification); + + }else + throw new RuntimeException("Failed to write the post"); + + }catch(Exception e){ + logger.error("Failed to retrieve application token", e); + } + + } + + /** + * Convert the json response to a map + * @param response + * @return + */ + private static JSONObject getJSONObject(HttpResponse response){ + + JSONObject toReturn = null; + HttpEntity entity = response.getEntity(); + + if (entity != null) { + try { + String jsonString = EntityUtils.toString(response.getEntity()); + JSONParser parser = new JSONParser(); + toReturn = (JSONObject)parser.parse(jsonString); + }catch(Exception e){ + logger.error("Failed to read json object", e); + } + } + + logger.debug("Returning " + toReturn.toJSONString()); + return toReturn; + } + + /** + * Perform an http request post request with json entity + * @throws IOException + * @throws ClientProtocolException + */ + private static HttpResponse performRequest(CloseableHttpClient client, String path, String entity) throws ClientProtocolException, IOException{ + + HttpPost request = new HttpPost(path); + StringEntity stringEntity = new StringEntity(entity); + stringEntity.setContentType(MEDIATYPE_JSON); + request.setEntity(stringEntity); + return client.execute(request); + + } + +} \ No newline at end of file diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/ws/Group.java b/src/main/java/org/gcube/datacatalogue/catalogue/ws/Group.java new file mode 100644 index 0000000..f7b00a9 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/ws/Group.java @@ -0,0 +1,112 @@ +package org.gcube.datacatalogue.catalogue.ws; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +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.UriInfo; + +import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.authorization.library.utils.Caller; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.catalogue.utils.CatalogueUtils; +import org.gcube.datacatalogue.catalogue.utils.Constants; + + +@Path(Constants.GROUPS) +public class Group { + + @GET + @Path(Constants.SHOW_METHOD) + @Produces(MediaType.APPLICATION_JSON) + public String show(@Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.group_show + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegateGet(caller, context, Constants.GROUP_SHOW, uriInfo); + + } + + @GET + @Path(Constants.LIST_METHOD) + @Produces(MediaType.APPLICATION_JSON) + public String organizationList(@Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_show + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegateGet(caller, context, Constants.GROUP_LIST, uriInfo); + + } + + @POST + @Path(Constants.CREATE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String create(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.group_create + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.GROUP_CREATE, json, uriInfo); + + } + + @DELETE + @Path(Constants.DELETE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String delete(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.group_delete + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.GROUP_DELETE, json, uriInfo); + + } + + @DELETE + @Path(Constants.PURGE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String purge(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.group_purge + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.GROUP_PURGE, json, uriInfo); + + } + + @POST + @Path(Constants.UPDATE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String update(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.group_update + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.GROUP_UPDATE, json, uriInfo); + + } + + @POST + @Path(Constants.PATCH_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String patch(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.group_patch + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.GROUP_PATCH, json, uriInfo); + + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/ws/Item.java b/src/main/java/org/gcube/datacatalogue/catalogue/ws/Item.java new file mode 100644 index 0000000..1b3f5bc --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/ws/Item.java @@ -0,0 +1,153 @@ +package org.gcube.datacatalogue.catalogue.ws; + +import java.util.List; + +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.UriInfo; + +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.datacatalogue.catalogue.utils.CatalogueUtils; +import org.gcube.datacatalogue.catalogue.utils.Constants; +import org.gcube.datacatalogue.catalogue.utils.PackageCreatePostActions; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.slf4j.LoggerFactory; + +@Path(Constants.ITEMS) +public class Item { + + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Item.class); + + @GET + @Path(Constants.SHOW_METHOD) + @Produces(MediaType.APPLICATION_JSON) + public String show(@Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.package_show + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegateGet(caller, context, Constants.ITEM_SHOW, uriInfo); + + } + + @POST + @Path(Constants.CREATE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String create(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.package_create + Caller caller = AuthorizationProvider.instance.get(); + String username = caller.getClient().getId(); + String context = ScopeProvider.instance.get(); + + try{ + + // check base information (and set them if needed) + json = CatalogueUtils.checkBaseInformation(json, caller); + + // check resources information (name and url must be there) + CatalogueUtils.checkResourcesInformation(json, caller); + + // Check if there are profiles here + List profiles = CatalogueUtils.getProfilesNames(); + + if(profiles != null && !profiles.isEmpty()) + json = CatalogueUtils.validateAgainstProfile(json, caller, profiles); + + JSONParser parser = new JSONParser(); + JSONObject obj = (JSONObject)parser.parse(CatalogueUtils.delegatePost(caller, context, Constants.ITEM_CREATE, json, uriInfo)); + + // after creation, if it was ok ... + if((boolean)obj.get(CatalogueUtils.SUCCESS_KEY)) + new PackageCreatePostActions( + username, + (String)(((JSONObject)obj.get(CatalogueUtils.RESULT_KEY)).get(CatalogueUtils.DATASET_KEY)), + context, + SecurityTokenProvider.instance.get(), + (JSONArray)((JSONObject)obj.get(CatalogueUtils.RESULT_KEY)).get(CatalogueUtils.TAGS_KEY), + (String)(((JSONObject)obj.get(CatalogueUtils.RESULT_KEY)).get(CatalogueUtils.TITLE_KEY)) + ).start(); + + return obj.toJSONString(); + + }catch(Exception e){ + logger.error("Something went wrong... ", e); + return CatalogueUtils.createJSONOnFailure(e.toString()); + } + + } + + @POST + @Path(Constants.DELETE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String delete(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.package_delete + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.ITEM_DELETE, json, uriInfo); + + } + + @POST + @Path(Constants.PURGE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String purge(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.dataset_purge + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.ITEM_PURGE, json, uriInfo); + + } + + // @POST + // @Path(Constants.UPDATE_METHOD) + // @Consumes(MediaType.APPLICATION_JSON) + // @Produces(MediaType.APPLICATION_JSON) + // public String update(String json){ + // + // + // // 1) Check if there are profiles here + // // 2) If there are profiles: match the record against them + // // 3) Else submit it + // + // // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.package_update + // Caller caller = AuthorizationProvider.instance.get(); + // String context = ScopeProvider.instance.get(); + // return CatalogueUtils.delegatePost(caller, context, Constants.ITEM_UPDATE, json); + // + // } + + // @POST + // @Path(Constants.PATCH_METHOD) + // @Consumes(MediaType.APPLICATION_JSON) + // @Produces(MediaType.APPLICATION_JSON) + // public String patch(String json){ + // + // + // // 1) Check if there are profiles here + // // 2) If there are profiles: match the record against them + // // 3) Else submit it + // + // // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.package_patch + // Caller caller = AuthorizationProvider.instance.get(); + // String context = ScopeProvider.instance.get(); + // return CatalogueUtils.delegatePost(caller, context, Constants.ITEM_PATCH, json); + // + // } + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/ws/ItemProfile.java b/src/main/java/org/gcube/datacatalogue/catalogue/ws/ItemProfile.java new file mode 100644 index 0000000..a54c92b --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/ws/ItemProfile.java @@ -0,0 +1,77 @@ +package org.gcube.datacatalogue.catalogue.ws; + +import java.util.List; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.catalogue.utils.CatalogueUtils; +import org.gcube.datacatalogue.catalogue.utils.Constants; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.slf4j.LoggerFactory; + +@Path(Constants.PROFILES) +public class ItemProfile { + + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ItemProfile.class); + + public final static String PROFILES_GENERIC_TYPE = "DataCatalogueMetadata"; + public final static int PRETTY_PRINT_INDENT_FACTOR = 4; + + @SuppressWarnings("unchecked") + @GET + @Path(Constants.PROFILES_NAMES_SHOW) + @Produces(MediaType.APPLICATION_JSON) + public String showNames(){ + + String context = ScopeProvider.instance.get(); + logger.debug("Incoming request for context " + context); + + // get the names as list + JSONObject json = CatalogueUtils.createJSONObjectMin(true, null); + + try{ + List names = CatalogueUtils.getProfilesNames(); + JSONArray array = new JSONArray(); + for (String elem : names) { + try{ + array.add(elem); + }catch(Exception e){ + } + } + json.put(CatalogueUtils.RESULT_KEY, array); + }catch(Exception e){ + json = CatalogueUtils.createJSONObjectMin(false, e.getMessage()); + } + + return json.toJSONString(); + + } + + @GET + @Path(Constants.PROFILE_SHOW) + @Produces({MediaType.APPLICATION_XML, /*MediaType.APPLICATION_JSON*/}) + public String showSource( + //@DefaultValue(MediaType.APPLICATION_XML) @HeaderParam("Accept") String accept, + @QueryParam("profile-name") String profileName) throws Exception{ + + String context = ScopeProvider.instance.get(); + logger.debug("Incoming request for context/name " + context+ "/" + profileName); + + return CatalogueUtils.getProfileSource(profileName); + + // TODO Check how this mapping xml-> json works + /*if(accept.equals(MediaType.APPLICATION_JSON)){ + org.json.JSONObject xmlJSONObj = XML.toJSONObject(content); + return xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR); + + }*/ + } + + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/ws/License.java b/src/main/java/org/gcube/datacatalogue/catalogue/ws/License.java new file mode 100644 index 0000000..a692a26 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/ws/License.java @@ -0,0 +1,31 @@ +package org.gcube.datacatalogue.catalogue.ws; + +import javax.ws.rs.GET; +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.UriInfo; + +import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.authorization.library.utils.Caller; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.catalogue.utils.CatalogueUtils; +import org.gcube.datacatalogue.catalogue.utils.Constants; + + +@Path(Constants.LICENSES) +public class License { + + @GET + @Produces(MediaType.APPLICATION_JSON) + public String show(@Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.license_list + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegateGet(caller, context, Constants.LICENSES_SHOW, uriInfo); + + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/ws/Organization.java b/src/main/java/org/gcube/datacatalogue/catalogue/ws/Organization.java new file mode 100644 index 0000000..be2554c --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/ws/Organization.java @@ -0,0 +1,112 @@ +package org.gcube.datacatalogue.catalogue.ws; + +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.UriInfo; + +import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.authorization.library.utils.Caller; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.catalogue.utils.CatalogueUtils; +import org.gcube.datacatalogue.catalogue.utils.Constants; + + +@Path(Constants.ORGANIZATIONS) +public class Organization { + + + @GET + @Path(Constants.SHOW_METHOD) + @Produces(MediaType.APPLICATION_JSON) + public String show(@Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_show + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegateGet(caller, context, Constants.ORGANIZATION_SHOW, uriInfo); + + } + + @GET + @Path(Constants.LIST_METHOD) + @Produces(MediaType.APPLICATION_JSON) + public String organizationList(@Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_show + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegateGet(caller, context, Constants.ORGANIZATION_LIST, uriInfo); + + } + + @POST + @Path(Constants.CREATE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String create(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_create + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.ORGANIZATION_CREATE, json, uriInfo); + + } + + @POST + @Path(Constants.DELETE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String delete(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_delete + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.ORGANIZATION_DELETE, json, uriInfo); + + } + + @POST + @Path(Constants.PURGE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String purge(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_create + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.ORGANIZATION_PURGE, json, uriInfo); + + } + + @POST + @Path(Constants.UPDATE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String update(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_update + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.ORGANIZATION_UPDATE, json, uriInfo); + + } + + @POST + @Path(Constants.PATCH_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String patch(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_patch + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.ORGANIZATION_PATCH, json, uriInfo); + + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/ws/Resource.java b/src/main/java/org/gcube/datacatalogue/catalogue/ws/Resource.java new file mode 100644 index 0000000..e92760c --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/ws/Resource.java @@ -0,0 +1,120 @@ +package org.gcube.datacatalogue.catalogue.ws; + + +import java.util.List; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +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.UriInfo; + +import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.authorization.library.utils.Caller; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.catalogue.utils.CatalogueUtils; +import org.gcube.datacatalogue.catalogue.utils.Constants; +import org.glassfish.jersey.media.multipart.BodyPart; +import org.glassfish.jersey.media.multipart.FormDataBodyPart; +import org.glassfish.jersey.media.multipart.FormDataMultiPart; + +@Path(Constants.RESOURCES) +public class Resource { + + @GET + @Path(Constants.SHOW_METHOD) + @Produces(MediaType.APPLICATION_JSON) + public String show(@Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.resource_show + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegateGet(caller, context, Constants.RESOURCE_SHOW, uriInfo); + + } + + @POST + @Path(Constants.CREATE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String create(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.create.resource_create + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.RESOURCE_CREATE, json, uriInfo); + + } + + @POST + @Path(Constants.CREATE_METHOD) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Produces(MediaType.APPLICATION_JSON) + public String create( + FormDataMultiPart multiPart, @Context UriInfo uriInfo + ){ + + // see also multipart https://www.mkyong.com/webservices/jax-rs/file-upload-example-in-jersey/ + + List bodyParts = multiPart.getBodyParts(); + for (BodyPart bodyPart : bodyParts) { + System.out.println("Body name is " + bodyPart.getContentDisposition().getFileName()); + } + + Map> fields = multiPart.getFields(); + System.out.println(fields); + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.create.resource_create + // see also multipart https://www.mkyong.com/webservices/jax-rs/file-upload-example-in-jersey/ + /*Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.RESOURCE_CREATE, json, uriInfo);*/ + return "{\"test\": \"ok\"}"; + + } + + @DELETE + @Path(Constants.DELETE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String delete(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.create.resource_delete + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.RESOURCE_DELETE, json, uriInfo); + + } + + @POST + @Path(Constants.UPDATE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String update(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.update.resource_update + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.RESOURCE_UPDATE, json, uriInfo); + + } + + @POST + @Path(Constants.PATCH_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String patch(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.update.resource_patch + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.RESOURCE_PATCH, json, uriInfo); + + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/catalogue/ws/User.java b/src/main/java/org/gcube/datacatalogue/catalogue/ws/User.java new file mode 100644 index 0000000..5034f0a --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/catalogue/ws/User.java @@ -0,0 +1,74 @@ +package org.gcube.datacatalogue.catalogue.ws; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +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.UriInfo; + +import org.gcube.common.authorization.library.provider.AuthorizationProvider; +import org.gcube.common.authorization.library.utils.Caller; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.catalogue.utils.CatalogueUtils; +import org.gcube.datacatalogue.catalogue.utils.Constants; + + +@Path(Constants.USERS) +public class User { + + @GET + @Path(Constants.SHOW_METHOD) + @Produces(MediaType.TEXT_PLAIN) + public String show(@Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.user_show + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegateGet(caller, context, Constants.USER_SHOW, uriInfo); + + } + + @POST + @Path(Constants.CREATE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String create(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.user_create + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.USER_CREATE, json, uriInfo); + + } + + @DELETE + @Path(Constants.DELETE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String delete(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.user_delete + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.USER_DELETE, json, uriInfo); + + } + + @POST + @Path(Constants.UPDATE_METHOD) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public String update(String json, @Context UriInfo uriInfo){ + + // see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.user_update + Caller caller = AuthorizationProvider.instance.get(); + String context = ScopeProvider.instance.get(); + return CatalogueUtils.delegatePost(caller, context, Constants.USER_UPDATE, json, uriInfo); + + } + +} diff --git a/src/main/resources/ehcache.xml b/src/main/resources/ehcache.xml new file mode 100644 index 0000000..afe7c90 --- /dev/null +++ b/src/main/resources/ehcache.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/gcube-app.xml b/src/main/webapp/WEB-INF/gcube-app.xml new file mode 100644 index 0000000..e2ed6e3 --- /dev/null +++ b/src/main/webapp/WEB-INF/gcube-app.xml @@ -0,0 +1,8 @@ + + Catalogue-WS + Data-Catalogue + 1.0.0-SNAPSHOT + Data Catalogue Service + + /rest/ + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..fd1f25d --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,30 @@ + + + + Catalogue Web Service + + jersey-servlet + org.glassfish.jersey.servlet.ServletContainer + + jersey.config.server.provider.packages + org.gcube.datacatalogue.catalogue.ws + + + jersey.config.server.provider.classnames + org.glassfish.jersey.media.multipart.MultiPartFeature + + 1 + + + + jersey-servlet + /rest/* + + + + index.jsp + + diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 0000000..bda17e1 --- /dev/null +++ b/src/main/webapp/index.jsp @@ -0,0 +1,5 @@ + + +

gCube Catalogue WS is up and running!

+ + diff --git a/src/test/java/JavaTests.java b/src/test/java/JavaTests.java new file mode 100644 index 0000000..c457865 --- /dev/null +++ b/src/test/java/JavaTests.java @@ -0,0 +1,143 @@ +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.Element; + +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.catalogue.beans.resource.CustomField; +import org.gcube.datacatalogue.catalogue.utils.CachesManager; +import org.gcube.datacatalogue.catalogue.utils.CatalogueUtils; +import org.gcube.datacatalogue.metadatadiscovery.DataCalogueMetadataFormatReader; +import org.gcube.datacatalogue.metadatadiscovery.bean.MetadataProfile; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + + + +public class JavaTests { + + //@Test + public void testGetMetadataNames() throws Exception { + + ScopeProvider.instance.set("/gcube/devNext/NextNext"); + DataCalogueMetadataFormatReader reader = new DataCalogueMetadataFormatReader(); + List listProfiles = reader.getListOfMetadataProfiles(); + System.out.println("Result is " + listProfiles); + + } + + //@Test + public void testJSONValueCategory(){ + JSONObject catJsonObj = new JSONObject(); + JSONObject value = new JSONObject(); + value.put("id", "aaaaa"); + value.put("title", "this is the title"); + value.put("description", "this is the description"); + catJsonObj.put("qualified_name", value); // TODO check + System.out.println(catJsonObj); + } + + //@Test + public void testDate(){ + + SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd"); + SimpleDateFormat formatter2 = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + String value = "2005-03-01"; + + try{ + + Date d = formatter2.parse(value); + System.out.println("F1 is " + d.toLocaleString()); + if(d == null) + d = formatter2.parse(value); + + }catch(Exception e){ + System.err.println("failed to parse date " + e); + } + + } + + + //@Test + public void testSorting(){ + + List toSort = new ArrayList(); + + toSort.add(new CustomField("C", "value1", Integer.MAX_VALUE, 7)); + toSort.add(new CustomField("E", "value1", Integer.MAX_VALUE, 6)); + toSort.add(new CustomField("X", "value1", Integer.MAX_VALUE, 3)); + toSort.add(new CustomField("Z", "value1", 0, 3)); + toSort.add(new CustomField("A", "value2", 1, 2)); + toSort.add(new CustomField("D", "value1", 2, 4)); + toSort.add(new CustomField("B", "value1", 2, 1)); + + Collections.sort(toSort); + + System.out.println("Sorted list " + toSort); + } + + //@Test + public void testJsonArray(){ + + JSONArray tagsArrayOriginal = new JSONArray(); + String tag = ""; + tagsArrayOriginal.add(tag); + + System.out.println(tagsArrayOriginal); + + } + + //@Test + public void testEHCache() throws Exception{ + + ScopeProvider.instance.set("/gcube/devNext/NextNext"); + + Cache profilesCache = CachesManager.getCache(CachesManager.PROFILES_READERS_CACHE); + String context = ScopeProvider.instance.get(); + List toReturn = new ArrayList(); + + DataCalogueMetadataFormatReader reader; + if(profilesCache.isKeyInCache(context)) + reader = (DataCalogueMetadataFormatReader) profilesCache.get(context).getObjectValue(); + else{ + reader = new DataCalogueMetadataFormatReader(); + profilesCache.put(new Element(context, reader)); + } + + Thread.sleep(1000 * 60); + } + + //@Test + public void testJSONObj() throws Exception{ + + JSONObject obj = new JSONObject(); + obj.put("c", "d"); + modifyJSON(obj); + System.out.println(obj); + } + + private static void modifyJSON(JSONObject obj){ + + obj.put("a", "b"); + + } + + //@Test + public void testJSONParser() throws ParseException{ + + String jsonString = "{\"help\": \"https://ckan-d-d4s.d4science.org/api/3/action/help_show?name=package_create\", \"success\": true, \"result\": {\"license_title\": \"License Not Specified\", \"maintainer\": \"Giancarlo Panichi\", \"relationships_as_object\": [], \"private\": false, \"maintainer_email\": \"giancarlo.panichi@isti.cnr.it\", \"num_tags\": 2, \"id\": \"e39990ac-c644-49f4-a8a2-6774c2e69b59\", \"metadata_created\": \"2017-03-31T12:07:23.836973\", \"metadata_modified\": \"2017-03-31T12:07:23.836983\", \"author\": \"Costantino Perciante\", \"author_email\": \"costantino.perciante@isti.cnr.it\", \"state\": \"active\", \"version\": \"1\", \"creator_user_id\": \"ab8189ff-6b07-4963-b36a-c83d53d7e49b\", \"type\": \"dataset\", \"resources\": [{\"mimetype\": null, \"cache_url\": null, \"hash\": \"\", \"description\": \"\", \"name\": \"resource A\", \"format\": \"\", \"url\": \"http://www.test.it\", \"datastore_active\": false, \"cache_last_updated\": null, \"package_id\": \"e39990ac-c644-49f4-a8a2-6774c2e69b59\", \"created\": \"2017-03-31T14:07:23.943698\", \"state\": \"active\", \"mimetype_inner\": null, \"last_modified\": null, \"position\": 0, \"revision_id\": \"687a9a8e-4450-4904-9f54-603e6dc71d54\", \"url_type\": null, \"id\": \"fb0c2e90-bc5e-4028-ae61-8cc799814c4d\", \"resource_type\": null, \"size\": null}], \"num_resources\": 1, \"tags\": [{\"vocabulary_id\": null, \"state\": \"active\", \"display_name\": \"Artifact Name-catalogue-ws\", \"id\": \"5b287a15-ff52-429a-8f24-d5f9a4cc0f42\", \"name\": \"Artifact Name-catalogue-ws\"}, {\"vocabulary_id\": null, \"state\": \"active\", \"display_name\": \"Java\", \"id\": \"ad62fb1a-02f7-4ee5-96f8-50c919866552\", \"name\": \"Java\"}], \"groups\": [], \"license_id\": \"notspecified\", \"relationships_as_subject\": [], \"organization\": {\"description\": \"VRE nextnext\", \"created\": \"2016-06-21T17:29:47.479879\", \"title\": \"NextNext\", \"name\": \"nextnext\", \"is_organization\": true, \"state\": \"active\", \"image_url\": \"\", \"revision_id\": \"12264679-247a-4c40-945d-38607c006d2a\", \"type\": \"organization\", \"id\": \"131d8f52-2566-458b-8bc4-04fb57f2580d\", \"approval_status\": \"approved\"}, \"name\": \"test-web-service-3\", \"isopen\": false, \"url\": null, \"notes\": null, \"owner_org\": \"131d8f52-2566-458b-8bc4-04fb57f2580d\", \"extras\": [{\"key\": \"Field/Scope of use\", \"value\": \"Any use\"}, {\"key\": \"UsageMode\", \"value\": \"Download\"}, {\"key\": \"categoryref:artifact_information:Artifact Name\", \"value\": \"catalogue-ws\"}, {\"key\": \"categoryref:artifact_information:Maven Location\", \"value\": \"http://maven.research-infrastructures.eu/nexus/index.html\"}, {\"key\": \"categoryref:artifact_information:Programming language\", \"value\": \"Java\"}, {\"key\": \"categoryref:developer_information:Identifier\", \"value\": \"costantino.perciante\"}, {\"key\": \"categoryref:extra_information:First Release\", \"value\": \"2017-04-03\"}, {\"key\": \"categoryref:extra_information:Size MB\", \"value\": \"2\"}, {\"key\": \"metadatacategory:artifact_information\", \"value\": \"{\\\"id\\\":\\\"artifact_information\\\",\\\"title\\\":\\\"Artifact Information\\\",\\\"description\\\":\\\"Artifact's main information \\\\t\\\\t\\\"}\"}, {\"key\": \"metadatacategory:developer_information\", \"value\": \"{\\\"id\\\":\\\"developer_information\\\",\\\"title\\\":\\\"Developer Information\\\",\\\"description\\\":\\\"This section is about developer(s) information \\\\t\\\\t\\\"}\"}, {\"key\": \"metadatacategory:extra_information\", \"value\": \"{\\\"id\\\":\\\"extra_information\\\",\\\"title\\\":\\\"Extras\\\",\\\"description\\\":\\\"Other information about the artifact \\\\t\\\\t\\\"}\"}, {\"key\": \"metadatatype\", \"value\": \"software_type\"}, {\"key\": \"spatial\", \"value\": \"{\\\"type\\\": \\\"Point\\\",\\\"coordinates\\\": [-3.145, 53.078]}\"}], \"title\": \"title test from web service-3\", \"revision_id\": \"687a9a8e-4450-4904-9f54-603e6dc71d54\"}}"; + + JSONParser parser = new JSONParser(); + JSONObject obj = (JSONObject)parser.parse(jsonString); + JSONArray tags = (JSONArray)((JSONObject)obj.get(CatalogueUtils.RESULT_KEY)).get(CatalogueUtils.TAGS_KEY); + System.out.println("Tags are " + tags); + } + +} diff --git a/src/test/java/TestJersey.java b/src/test/java/TestJersey.java new file mode 100644 index 0000000..4e123ca --- /dev/null +++ b/src/test/java/TestJersey.java @@ -0,0 +1,124 @@ +import java.io.File; +import java.io.IOException; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.http.ParseException; +import org.gcube.datacatalogue.catalogue.ws.ItemProfile; +import org.gcube.datacatalogue.catalogue.ws.Resource; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.media.multipart.FormDataMultiPart; +import org.glassfish.jersey.media.multipart.MultiPart; +import org.glassfish.jersey.media.multipart.MultiPartFeature; +import org.glassfish.jersey.media.multipart.file.FileDataBodyPart; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.glassfish.jersey.test.TestProperties; +import org.json.simple.JSONObject; +import org.slf4j.LoggerFactory; + +public class TestJersey extends JerseyTest { + + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(TestJersey.class); + + //@Override + protected Application configure() { + logger.info("Configuring service..."); + forceSet(TestProperties.CONTAINER_PORT, "0"); + final ResourceConfig resourceConfig = new ResourceConfig(Resource.class, ItemProfile.class); + resourceConfig.register(MultiPartFeature.class); + return resourceConfig; + } + + //@Override + public void configureClient(ClientConfig config) { + logger.info("Configuring client..."); + config.register(MultiPartFeature.class); + } + + //@Test + @SuppressWarnings("unchecked") + public void test() throws ParseException, IOException { + + JSONObject obj = new JSONObject(); + obj.put("test", "value"); + final JSONObject createResource = target("api/resources/create/") + .request() + .accept(MediaType.APPLICATION_JSON) + .post(Entity.json(obj), JSONObject.class); + logger.info(createResource.toJSONString()); + } + + //@Test + public void testFile() throws ParseException, IOException { + + + FileDataBodyPart fileDataBodyPart = new FileDataBodyPart("uploadFile", + new File("/Users/costantinoperciante/Desktop/rilascio_tess.doc")); + + final MultiPart multipart = new FormDataMultiPart() + .field("foo", "bar") + .bodyPart(fileDataBodyPart); + + final Response createResource = + target("api/resources/create/") + .request() + .post(Entity.entity(multipart, multipart.getMediaType())); + logger.info(createResource.toString()); + // + multipart.close(); + } + + //@Test + public void testProfilesNames() throws ParseException, IOException { + + + FileDataBodyPart fileDataBodyPart = new FileDataBodyPart("uploadFile", + new File("/Users/costantinoperciante/Desktop/rilascio_tess.doc")); + + final MultiPart multipart = new FormDataMultiPart() + .field("foo", "bar") + .bodyPart(fileDataBodyPart); + + final Response createResource = + target("api/resources/create/") + .request() + .post(Entity.entity(multipart, multipart.getMediaType())); + logger.info(createResource.toString()); + // + multipart.close(); + } + + //@Test + public void testProfileNames() throws ParseException, IOException { + + final String profiles = + target("api/profiles/profile_names/") + .queryParam("context", "/gcube/devNext/NextNext") + .request() + .get(String.class); + + logger.info("Response is " + profiles); + + } + + //@Test + public void testProfileByName() throws ParseException, IOException { + + final String profiles = + target("api/profiles/profile/") + .queryParam("context", "/gcube/devNext/NextNext") + .queryParam("name", "SoBigData.eu: Dataset Metadata NextNext") + .request(MediaType.APPLICATION_JSON) + .get(String.class); + + logger.info("Response is " + profiles); + + } + + + +}