diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a48a60f8a --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) +!/.mvn/wrapper/maven-wrapper.jar + +#logs ws +logs/* + +#target +phis2-ws/target/ diff --git a/phis2-ws/licenseheader.txt b/phis2-ws/licenseheader.txt new file mode 100644 index 000000000..674d69ab0 --- /dev/null +++ b/phis2-ws/licenseheader.txt @@ -0,0 +1,11 @@ +//********************************************************************************************** +// XXX.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: XX 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: XX, 2017 +// Subject: XX +//*********************************************************************************************** diff --git a/phis2-ws/nb-configuration.xml b/phis2-ws/nb-configuration.xml new file mode 100644 index 000000000..15c52c535 --- /dev/null +++ b/phis2-ws/nb-configuration.xml @@ -0,0 +1,22 @@ + + + + + + 1.7-web + Tomcat + ide + none + ${project.basedir}/licenseheader.txt + + diff --git a/phis2-ws/pom.xml b/phis2-ws/pom.xml new file mode 100644 index 000000000..2b97bc7f6 --- /dev/null +++ b/phis2-ws/pom.xml @@ -0,0 +1,216 @@ + + + 4.0.0 + + com.mycompany + phis2ws + v0.1 + war + phis2ws + + + ${project.build.directory}/endorsed + UTF-8 + 1.1.7 + + + + + + spring-milestones + http://repo.spring.io/libs-milestone/ + + + + + + org.apache.tomcat + tomcat-jdbc + 9.0.0.M6 + jar + + + org.mongodb + mongodb-driver + 3.2.2 + jar + + + org.eclipse.rdf4j + rdf4j-runtime + 2.2 + + + org.eclipse.rdf4j + rdf4j-repository-api + 2.2 + + + com.jcraft + jsch + 0.1.53 + jar + + + com.google.code.gson + gson + 2.7 + jar + + + com.nimbusds + nimbus-jose-jwt + 4.22 + jar + + + commons-codec + commons-codec + 1.10 + jar + + + com.twmacinta + fast-md5 + 2.7.1 + jar + + + javax + javaee-web-api + 7.0 + provided + + + org.glassfish.jersey.core + jersey-server + 2.25 + + + org.glassfish.jersey.core + jersey-client + 2.25 + + + org.glassfish.jersey.core + jersey-common + 2.25 + + + org.glassfish.jersey.containers + jersey-container-servlet + 2.25 + + + io.swagger + swagger-jersey2-jaxrs + 1.5.13 + + + org.glassfish.jersey.media + jersey-media-moxy + 2.25 + + + + ch.qos.logback + logback-classic + ${logback-version} + + + ch.qos.logback + logback-core + ${logback-version} + + + + org.slf4j + slf4j-api + 1.7.21 + + + + org.codehaus.janino + janino + 2.5.16 + + + org.postgis + postgis-jdbc + 1.3.3 + + + + org.postgresql + postgresql + 9.4.1212.jre7 + + + commons-logging + commons-logging + 1.1.1 + + + + com.fasterxml.jackson.core + jackson-annotations + 2.9.0.pr1 + + + joda-time + joda-time + 2.9.9 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.7 + 1.7 + + ${endorsed.dir} + + + + + org.apache.maven.plugins + maven-war-plugin + 2.3 + + false + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.6 + + + validate + + copy + + + ${endorsed.dir} + true + + + javax + javaee-endorsed-api + 7.0 + jar + + + + + + + + + diff --git a/phis2-ws/src/main/java/phis2ws/service/ApplicationInitConfig.java b/phis2-ws/src/main/java/phis2ws/service/ApplicationInitConfig.java new file mode 100644 index 000000000..cc4c9a201 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/ApplicationInitConfig.java @@ -0,0 +1,96 @@ +//********************************************************************************************** +// ApplicationInitConfig.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: january 2017 +// Contact:morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: January, 2017 +// Subject: Configuration of the webservice +//*********************************************************************************************** + +package phis2ws.service; + +import io.swagger.jaxrs.config.BeanConfig; +import java.io.File; +import javax.annotation.PostConstruct; +import javax.inject.Singleton; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import org.glassfish.hk2.api.InjectionResolver; +import org.glassfish.hk2.api.TypeLiteral; +import org.glassfish.hk2.utilities.binding.AbstractBinder; +import org.glassfish.jersey.server.ResourceConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.authentication.TokenManager; +import phis2ws.service.dao.phis.SessionDaoPhisBrapi; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionFactory; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.injection.SessionInjectResolver; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.ResponseFormPOST; + +@ApplicationPath("/rest") +public class ApplicationInitConfig extends ResourceConfig { + + final static String PROPERTY_FILE_NAME = "service"; + final static Logger logger = LoggerFactory.getLogger(ApplicationInitConfig.class); + + + public ApplicationInitConfig() { + packages("io.swagger.jaxrs.listing;" + + "wsphisfield.service.resources;" + + "wsphisfield.service.json;" + + "wsphisfield.service.resources.request.filters"); + + //Swagger + BeanConfig beanConfig = new BeanConfig(); + beanConfig.setVersion("1.0.2"); + beanConfig.setSchemes(new String[]{"http"}); + + beanConfig.setHost(PropertiesFileManager.getConfigFileProperty("service", "host")); + beanConfig.setBasePath(PropertiesFileManager.getConfigFileProperty("service", "basePath")); + + beanConfig.setResourcePackage("wsphisfield.service.resources"); + beanConfig.setScan(true); + // Annotation SessionInject pour obtenir la session en cours et l'utilisateur + // Liaison entre le createur d'objet a partir de la requete du client et l'application +// @see https://jersey.java.net/documentation/latest/ioc.html + register(new AbstractBinder() { + @Override + protected void configure() { + // cree la session a partir du sessionId reçu + bindFactory(SessionFactory.class).to(Session.class); + // Injection de la session grace au type definit dans SessionInjectResolver + bind(SessionInjectResolver.class) + .to(new TypeLiteral>() { + }) + .in(Singleton.class); + } + }); + } + + @PostConstruct + public static void initialize() { + final String logDirectory = PropertiesFileManager.getConfigFileProperty("service", "logDirectory"); + if (logDirectory != null) { + File logDir = new File(logDirectory); + if (!logDir.isDirectory()) { + if (!logDir.mkdirs()) { + logger.error("Can't create log directory"); + throw new WebApplicationException( + Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ResponseFormPOST(new Status("Can't create log directory", StatusCodeMsg.ERR, null))).build()); + } + } + } + TokenManager.Instance(); + SessionDaoPhisBrapi sessionDao = new SessionDaoPhisBrapi(); +// sessionDao.reloadActiveSession(); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/PropertiesFileManager.java b/phis2-ws/src/main/java/phis2ws/service/PropertiesFileManager.java new file mode 100644 index 000000000..91e7f77f0 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/PropertiesFileManager.java @@ -0,0 +1,244 @@ +//********************************************************************************************** +// PropertiesFileManager.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr, +// morgane.vidal@inra.fr +// Last modification date: January, 2017 +// Subject: Read properties file +//*********************************************************************************************** +package phis2ws.service; + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Objects; +import java.util.Properties; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import org.apache.tomcat.jdbc.pool.PoolProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Gère les méthodes appelant tous types de propriétés à partir de fichier de + * configuration Les fichiers sont disponible dans /src/main/resources par + * défaut dans un projet Maven + * + * @date 05/2016 + * @author Arnaud CHARLEROY + */ +public class PropertiesFileManager { + + final static Logger logger = LoggerFactory.getLogger(PropertiesFileManager.class.getName()); + + /** + * Lit le fichier de configuration et retourne un objet Proprietes + * + * @param fileName nom du fichier à lire + * @return null | Properties + */ + public static Properties parseFile(String fileName) { + InputStream inputStream = null; + final Properties props = new Properties(); + //property is in /src/main/resources By default in maven project + + final String filePath = "/" + fileName + ".properties"; +// System.err.println(filePath); + + try { + inputStream = PropertiesFileManager.class.getResourceAsStream(filePath); + props.load(inputStream); + } catch (IOException | NullPointerException ex) { + logger.error(ex.getMessage(), ex); + // Si les paramètres ne sont pas récupérés le web service propage une exception INTERNAL_SERVER_ERROR + throw new WebApplicationException(Response + .status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error : Cannot find " + fileName + " configuration file for the wanted database\n" + ex.getMessage()).build()); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ex) { + logger.error(ex.getMessage(), ex); + } + } + } + return props; + } + + public static RSAPublicKey parseBinaryPublicKey(String fileName) { + //property is in /src/main/resources By default in maven project + RSAPublicKey generatedRSAPublicKey = null; + DataInputStream dataInputStream = null; + try { + URL resource = PropertiesFileManager.class.getResource("/" + fileName + ".der"); + File publicKeyFile = new File(resource.getPath()); + dataInputStream = new DataInputStream(new FileInputStream(publicKeyFile)); + byte[] keyBytes = new byte[(int) publicKeyFile.length()]; + dataInputStream.readFully(keyBytes); + dataInputStream.close(); + + X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); + KeyFactory kf = KeyFactory.getInstance("RSA"); + generatedRSAPublicKey = (RSAPublicKey) kf.generatePublic(spec); + + } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException ex) { + logger.error(ex.getMessage(), ex); + // Si les paramètres ne sont pas récupérés le web service propage une exception INTERNAL_SERVER_ERROR + throw new WebApplicationException(Response + .status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Can't process JWT Token" + ex.getMessage()).build()); + } finally { + if (dataInputStream != null) { + try { + dataInputStream.close(); + } catch (IOException ex) { + logger.error(ex.getMessage(), ex); + } + } + } + return generatedRSAPublicKey; + } + + /** + * Lit un fichier de configuration et retourne d'un attribut spécifique + * + * @param fileName nom du fichier à lire + * @param prop nom de lattribut + * @return null | Properties + */ + public static String getConfigFileProperty(String fileName, String prop) { + try { + final Properties sqlProps = parseFile(fileName); + final StringBuilder strBuilder = new StringBuilder(); + strBuilder.append(sqlProps.getProperty(prop)); + return strBuilder.toString(); + } catch (Exception ex) { + logger.error(ex.getMessage(), ex); + ex.printStackTrace(); + // Si les paramètres ne sont pas récupérés le web service propage une exception INTERNAL_SERVER_ERROR + throw new WebApplicationException(Response + .status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error : Cannot find properties in the configuration file " + fileName + " for the wanted database\n" + ex.getMessage()).build()); + } + } + + /** + * Lit un fichier de configuration et retourne l'url pour une base de donnée + * SQL contenant le nom, l'utilisateur et le serveur + * + * @param fileName nom du fichier à lire + * @return null | Properties + */ + public static String getSQLConnectionUrl(String fileName) { + try { + final Properties sqlProps = parseFile(fileName); + final StringBuilder strBuilder = new StringBuilder(); + strBuilder.append(sqlProps.getProperty("url")) + .append("?") + .append("user=").append(sqlProps.getProperty("username")) + .append("&") + .append("password=").append(sqlProps.getProperty("password")); + return strBuilder.toString(); + } catch (Exception ex) { + logger.error(ex.getMessage(), ex); + // Si les paramètres ne sont pas récupérés le web service propage une exception INTERNAL_SERVER_ERROR + throw new WebApplicationException(Response + .status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error : Cannot find " + fileName + " configuration file for the wanted database\n" + ex.getMessage()).build()); + } + } + + /** + * Lit un fichier de configuration et un objet PoolPropreties permettant de + * configurer un jeu de connexion de base de donnée relationelle + * + * @see https://tomcat.apache.org/tomcat-8.0-doc/jdbc-pool.html + * @param fileName nom du fichier à lire + * @return null | Properties + */ + public static PoolProperties getSQLPoolDataSourceProperties(String fileName) { + try { + final PoolProperties p = new PoolProperties(); + // minimal configuration + p.setUrl(getConfigFileProperty(fileName, "url")); + p.setDriverClassName(getConfigFileProperty(fileName, "driver")); + p.setUsername(getConfigFileProperty(fileName, "username")); + p.setPassword(getConfigFileProperty(fileName, "password")); + // Optional + if (!Objects.equals(getConfigFileProperty(fileName, "jmxEnabled"), "null")) { + p.setJmxEnabled(Boolean.valueOf(getConfigFileProperty(fileName, "jmxEnabled"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "defaultAutoCommit"), "null")) { + p.setDefaultAutoCommit(Boolean.valueOf(getConfigFileProperty(fileName, "defaultAutoCommit"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "testWhileIdle"), "null")) { + p.setTestWhileIdle(Boolean.valueOf(getConfigFileProperty(fileName, "testWhileIdle"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "testOnBorrow"), "null")) { + p.setTestOnBorrow(Boolean.valueOf(getConfigFileProperty(fileName, "testOnBorrow"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "validationQuery"), "null")) { + p.setValidationQuery(getConfigFileProperty(fileName, "validationQuery")); + } + if (!Objects.equals(getConfigFileProperty(fileName, "testOnReturn"), "null")) { + p.setTestOnReturn(Boolean.valueOf(getConfigFileProperty(fileName, "testOnReturn"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "validationInterval"), "null")) { + p.setValidationInterval(Long.valueOf(getConfigFileProperty(fileName, "validationInterval"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "timeBetweenEvictionRunsMillis"), "null")) { + p.setTimeBetweenEvictionRunsMillis(Integer.valueOf(getConfigFileProperty(fileName, "timeBetweenEvictionRunsMillis"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "maxActive"), "null")) { + p.setMaxActive(Integer.valueOf(getConfigFileProperty(fileName, "maxActive"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "minIdle"), "null")) { + p.setMinIdle(Integer.valueOf(getConfigFileProperty(fileName, "minIdle"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "maxIdle"), "null")) { + p.setMaxIdle(Integer.valueOf(getConfigFileProperty(fileName, "maxIdle"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "initialSize"), "null")) { + p.setInitialSize(Integer.valueOf(getConfigFileProperty(fileName, "initialSize"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "maxWait"), "null")) { + p.setMaxWait(Integer.valueOf(getConfigFileProperty(fileName, "maxWait"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "removeAbandonedTimeout"), "null")) { + p.setRemoveAbandonedTimeout(Integer.valueOf(getConfigFileProperty(fileName, "removeAbandonedTimeout"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "maxAge"), "null")) { + p.setMaxAge(Long.valueOf(getConfigFileProperty(fileName, "maxAge"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "logAbandoned"), "null")) { + p.setLogAbandoned(Boolean.valueOf(getConfigFileProperty(fileName, "logAbandoned"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "removeAbandoned"), "null")) { + p.setRemoveAbandoned(Boolean.valueOf(getConfigFileProperty(fileName, "removeAbandoned"))); + } + if (!Objects.equals(getConfigFileProperty(fileName, "jdbcInterceptors"), "null")) { + p.setJdbcInterceptors(getConfigFileProperty(fileName, "jdbcInterceptors")); + } + + return p; + + } catch (Exception ex) { + logger.error("Error : Cannot find " + fileName + " configuration file \n", ex); + return null; + } + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/authentication/DataTable.java b/phis2-ws/src/main/java/phis2ws/service/authentication/DataTable.java new file mode 100644 index 000000000..f179671ff --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/authentication/DataTable.java @@ -0,0 +1,141 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.authentication; + +/** + * + * @author cherimon + * @Update AC 05/16 Mise à jour de la connexion aux données + */ +public class DataTable { + + private String instanceNumber, observationVariableId, observationVariableDbId, season, observationValue, observationTimeStamp, collectionFacilityLabel, collector; + + public DataTable() { + } + + public DataTable(String instanceNumber, String observationVariableId, String observationVariableDbId, String season, String observationValue, String observationTimeStamp, String collectionFacilityLabel, String collector) { + this.instanceNumber = instanceNumber; + this.observationVariableId = observationVariableId; + this.observationVariableDbId = observationVariableDbId; + this.season = season; + this.observationValue = observationValue; + this.observationTimeStamp = observationTimeStamp; + this.collectionFacilityLabel = collectionFacilityLabel; + this.collector = collector; + } + + public void setInstanceNumber(String instanceNumber) { + if (instanceNumber != null) { + this.instanceNumber = instanceNumber; + } else { + this.instanceNumber = ""; + } + } + + public void setObservationVariableId(String observationVariableId) { + if (observationVariableId != null) { + this.observationVariableId = observationVariableId; + } else { + this.observationVariableId = ""; + } + } + + public void setObservationVariableDbId(String observationVariableDbId) { + if (observationVariableDbId != null) { + this.observationVariableDbId = observationVariableDbId; + } else { + this.observationVariableDbId = ""; + } + } + + public void setSeason(String season) { + if (season != null) { + this.season = season; + } else { + this.season = ""; + } + } + + public void setObservationValue(String observationValue) { + if (observationValue != null) { + this.observationValue = observationValue; + } else { + this.observationValue = ""; + } + } + + public void setObservationTimeStamp(String observationTimeStamp) { + if (observationTimeStamp != null) { + this.observationTimeStamp = observationTimeStamp; + } else { + this.observationTimeStamp = ""; + } + } + + public void setCollectionFacilityLabel(String collectionFacilityLabel) { + if (collectionFacilityLabel != null) { + this.collectionFacilityLabel = collectionFacilityLabel; + } else { + this.collectionFacilityLabel = ""; + } + } + + public void setCollector(String collector) { + if (collector != null) { + this.collector = collector; + } else { + this.collector = ""; + } + } + + public String getInstanceNumber() { + return instanceNumber; + } + + public String getObservationVariableId() { + return observationVariableId; + } + + public String getObservationVariableDbId() { + return observationVariableDbId; + } + + public String getSeason() { + return season; + } + + public String getObservationValue() { + return observationValue; + } + + public String getObservationTimeStamp() { + return observationTimeStamp; + } + + public String getCollectionFacilityLabel() { + return collectionFacilityLabel; + } + + public String getCollector() { + return collector; + } + + + + + +// public void fillDataTable(ResultSet resultat) throws SQLException { +// setInstanceNumber(resultat.getString("date") + ":" + resultat.getString("sensor") + ":" + resultat.getString("codeVariable")); +// setObservationVariableId(resultat.getString("codeVariable")); +// setObservationVariableDbId(resultat.getString("codeVariable")); +// setSeason(resultat.getString("season")); +// setObservationValue(resultat.getString("value")); +// setObservationTimeStamp(resultat.getString("date")); +// setCollectionFacilityLabel(resultat.getString("site")); +// setCollector(resultat.getString("sensor")); +// } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/authentication/Session.java b/phis2-ws/src/main/java/phis2ws/service/authentication/Session.java new file mode 100644 index 000000000..80ae7291a --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/authentication/Session.java @@ -0,0 +1,144 @@ +package phis2ws.service.authentication; + +import phis2ws.service.model.User; + +/** + * Session - Permet de créer des objets correspondant aux différentes sessions + * utilisateur + * + * @version1.0 + * + * @author Samuël Chérimont + * @date 25/11/2015 + * @note Les champs dateStart et dateEnd ne sont pas utilisés pour le moment + * @update Anraud CHARLEROY Définir SQLDBModel et les cahmps uniques dans + * Session + */ +public class Session { + + private String dateStart; + private String dateEnd; + private String id; + private String name; + private User user; + + public Session() { + } + + public Session(String dateStart, String dateEnd, String id, String name) { + this.dateStart = dateStart; + this.dateEnd = dateEnd; + this.id = id; + this.name = name; + this.user = new User(name); + } + + public User getUser() { + return user; + } + + public String getDateStart() { + return dateStart; + } + + public String getDateEnd() { + return dateEnd; + } + + + /** + * Session() - Initialise tous les champs de l'objet Session + * + * @param id Identifiant de session + * @param name Nom de l'utilisateur + * + * @see setDateStart(),setDateEnd(), setFamilyName(), setId() + * @date 25/11/2015 + * @update AC 07/16 Modification utilisation des attributs privés plutôt que + * de passer par des setters publiques + */ + public Session(String id, String name) { + this.dateStart = null; + this.dateEnd = null; + this.name = name; + this.id = id; + this.user = new User(name); + } + + public Session(String id, String name, User u) { + this.dateStart = null; + this.dateEnd = null; + this.name = name; + this.id = id; + this.user = u; + } + + /** + * getId() - Récupère l'identifiant ed la session + * + * @return l'identifiant de session + * @date 25/11/2015 + */ + public String getId() { + return this.id; + } + + /** + * getFamilyName() - Récupère le nom de l'utilisateur correspondant à la session + représentée par l'instanec de Session + * + * @return le nom de l'utilisateur + * @date 25/11/2015 + */ + public String getName() { + return this.name; + } + + /** + * setDateStart() - Initialise le champ dateStart d'une instance de session + * avec la date de connection de l'utilisateur + * + * @param dateStart Date de connection + * + * @date 25/11/2015 + * @note Inutilisée pour le moment + */ + public void setDateStart(String dateStart) { + this.dateStart = dateStart; + } + + /** + * setDateEnd() - Initialise le champ dateEnd d'une instance de Session avec + * la date de fin de connection de l'utilisateur + * + * @param dateEnd date de fin de session + * + * @date 25/11/2015 + * @note Inutilisée pour le moment + */ + public void setDateEnd(String dateEnd) { + this.dateEnd = dateEnd; + } + + /** + * setid() - Initialise le champ id d'une instance de Session avec un + * identifiant de session + * + * @param id identifiant de session + * @date 25/11/2015 + */ + public void setId(String id) { + this.id = id; + } + + /** + * setFamilyName() - Initialise le champ name d'une instance de Session + * + * @param name Nom d'utilisateur + * @date 25/11/2015 + */ + public void setName(String name) { + this.name = name; + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/authentication/SessionThread.java b/phis2-ws/src/main/java/phis2ws/service/authentication/SessionThread.java new file mode 100644 index 000000000..fc7fb357c --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/authentication/SessionThread.java @@ -0,0 +1,97 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.authentication; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; + +/** + * SessionThread - Extension de la classe Thread Prend en charge la gestion du temps + de connection d'une session + * + * @version1.0 + * + * @author Samuël Chérimont + * @see DbConnector + * @date 25/11/2015 + * @update AC 05/2016 Ajout TokenDAO, log et modification des fichiers de propriétés + */ +public class SessionThread extends Thread { + private static final String propsFileName = "service"; + final static Logger logger = LoggerFactory.getLogger(SessionThread.class); + + private final String id, username; + private boolean cmp; + private Integer sessionTime; //sleep en millisecond + + public SessionThread(String id, String username) { + this.id = id; + this.username = username; + cmp = true; + this.setTimeSession(); + } + + /** + * Update 20/06/14 Temps en seconde *1000 + */ + private void setTimeSession(){ + try { + this.sessionTime = Integer.valueOf(PropertiesFileManager.getConfigFileProperty(propsFileName, "sessionTime")) * 1000; + } catch (NumberFormatException e) { + logger.error("Error : No session time defined or file parsing error for "+ propsFileName +" properties file", e); + } + } + + /** + * run() - Execution du thread actuel Le thread est mis en pause un certain + * temps, qui peut etre allongé en fonction du comportement de + * l'utilisateur et + * supprime l'objet Session correspondant de la liste des sessions actives + * + * @see phenomeapi.service.model.brapi.authentication.TokenDaoPhisBraphi,TokenManager.removeSession() + * @date 25/11/2015 + * @update 09/02/2016 AT : ne met plus la bd à jour, déplacé dans le manager + properties du temps de session + * + */ + @Override + public void run() { + try { + while (cmp != false) { + cmp = false; + SessionThread.sleep(this.sessionTime); + } + } catch (InterruptedException ex) { + logger.info("The session was interrupted", ex); + } + TokenManager.Instance().removeSession(this.id); + this.interrupt(); + } + + /** + * addTime - Permet d'augmenter le temps de connection d'un utilisateur + * Replace le compteur utilisé dans run() sur true ce qui met le thread sur + * pause plus longtemps et donc retarde la fin de la session correspondante + * + * @see run() + * @date 25/11/2015 + */ + public void addTime() { + this.cmp = true; + } + + /** + * getSessionId - Récupère l'identifiant de la session gérée par cette + instance de SessionThread + * + * @return l'identifiant de de la session + * + * @date 25/11/2015 + */ + public String getSessionId() { + return this.id; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/authentication/TokenManager.java b/phis2-ws/src/main/java/phis2ws/service/authentication/TokenManager.java new file mode 100644 index 000000000..af35e60e4 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/authentication/TokenManager.java @@ -0,0 +1,295 @@ +package phis2ws.service.authentication; + +import phis2ws.service.dao.phis.UserDaoPhisBrapi; +import java.sql.SQLException; +import java.util.ArrayList; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import org.joda.time.DateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.dao.phis.SessionDaoPhisBrapi; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.ResponseFormGET; + +/** + * Connection Manager - Permet la gestion de toutes les sessions et + * authentification par l'intermédiaire d'une seule instance de cette classe + * + * @version1.0 + * @author Samuël Chérimont + * @date 25/11/2015 + */ +public class TokenManager { + + static final Logger logger = LoggerFactory.getLogger(TokenManager.class); + private ArrayList listSession; + private ArrayList listThread; + private static TokenManager _instance = null; + + /** + * Instance() - Méthode de classe permettant de créer une instance unique de + * TokenManager Cette méthode doit être appelée à la place du constructeur + * de la classe TokenManager + * + * exemple ConnectionManger instance = TokenManager.Instance(); + * + * @return une instance unique de TokenManager + * @date 26/11/2015 + */ + public static TokenManager Instance() { + if (_instance == null) { + _instance = new TokenManager(); + } + return _instance; + } + + /** + * addThread() - Méthode privée appelée dans createToken() Crée un objet + * SessionThread pour chaque nouvel utilisateur qui se connecte, l'ajoute à + * une liste d'où il sera accessible et lance le nouveau thread + * + * @param id Identifiant de session + * @param username Nom de l'utilisateur + * + * @see SessionThreadcreateToken() + * @date 26/11/2015 + */ + private void addThread(String id, String username) { + if (this.listThread == null) { + this.listThread = new ArrayList(); + } + this.removeEmptyThread(); + SessionThread newThread = new SessionThread(id, username); + listThread.add(newThread); + newThread.start(); + } + + + private void removeThread(String id) { + int i = 0; + boolean interrupted = false; + while (i < listThread.size() && !interrupted) { + if (listThread.get(i).getSessionId().equals(id)) { + listThread.get(i).interrupt(); + listThread.remove(listThread.get(i)); + interrupted = true; + } + i++; + } + } + /** + * reloadToken() - Rajoute du temps de connection à un utilisateur identifié + * par son id + * + * @param id Identifiant de connection de l'utilisateur + * + * @see SessionThread + * @date 26/11/2015 + */ + public void reloadToken(String id) { + if (id != null && (listThread != null && !listThread.isEmpty())) { + int i = 0; + while (i < listThread.size()) { + if (this.listThread.get(i).getSessionId().equals(id)) { + this.listThread.get(i).addTime(); + return; + } + i++; + } + } + } + + /** + * removeEmptyThread - Supprime tout objet SessionThread ayant terminé son + * execution de la liste de threads + * + * @date 26/11/2015 + */ + private void removeEmptyThread() { + int i = 0; + while (i < listThread.size()) { + if (!listThread.get(i).isAlive()) { + listThread.remove(listThread.get(i)); + } + i++; + } + } + + /** + * searchSession() - Méthode appelée au moment de l'authentification + * Recherche si l'utilisateur identifié par name est présent dans la liste + * des sessions actives + * + * @param name Nom de l'utilisateur + * @return une String correspondant a l'identifiant de session de + * l'utilisateur ou null si aucune session n'est active pour cet utilisateur + * + * @see Token.getConnection() + * @date 26/11/2015 + */ + public String searchSession(String name) { + if (listSession != null && !listSession.isEmpty()) { + int i = 0; + while (i < listSession.size()) { + if (name.equals(listSession.get(i).getName())) { + return listSession.get(i).getId(); + } + i++; + } + } + return null; + } + + public Session getSession(String id) { + if (listSession != null && !listSession.isEmpty()) { + int i = 0; + while (i < listSession.size()) { + if (id.equals(listSession.get(i).getId())) { + return listSession.get(i); + } + i++; + } + } + return null; + } + + /** + * addSession() - Méthode appelée par createToken() Ajoute un objet Session + * à la liste des sessions actives + * + * @param session Un objet Session représentant une nouvelle session active + * + * @see createSession() + * @date 25/11/2015 + * @note L'accès a cette méthode peut être remplacée par private, dans ce + * cas il faut changer le test unitaire de checkAuthentification() + * + * @update 09/02/2016 + * @info AT, ajout d'une session au manager = ajout dans la bd, voir plus + * tard dao + */ + public void addSession(Session session) { + if (this.listSession == null) { + this.listSession = new ArrayList(); + } + this.listSession.add(session); +// logger.debug(this.listSession.toString()); + //BD + SessionDaoPhisBrapi sessionDao = new SessionDaoPhisBrapi(); + try { + session.setDateStart(new DateTime().toString("yyyy-MM-dd HH:mm:ss")); + sessionDao.insertOrUpdateOrDeleteQueryFromDAO("INSERT INTO session (email, id, date) VALUES ('" + session.getName() + "', '" + session.getId() + "', now())"); + } catch (SQLException ex) { + final Status status = new Status("Can't create session token", StatusCodeMsg.ERR, ex.getMessage()); + throw new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(new ResponseFormGET(status)).build()); + } finally { + } + } + + /** + * removeSession - Méthode appelée à la fin de l'execution d'un + * SessionThread Supprime un objet Session de la liste des sessions actives + * et met a jour la bd en ajoutant la date de fin de validité de la session + * + * @param id Identifiant de session + * + * @see MyThread.run() + * @date 25/11/2015 + * + * @update 09/02/2016 + * @info AT lien bd pour invalider la session, déplacé ici + */ + public void removeSession(String id) { + if (listSession == null || listSession.isEmpty()) { + return; + } + SessionDaoPhisBrapi sessionDao = new SessionDaoPhisBrapi(); + int i = 0; + boolean find = false; + while (i < listSession.size() && !find) { + String sessionid = listSession.get(i).getId(); + if (id.equals(sessionid)) { + sessionDao.endSession(sessionid); + listSession.remove(i); + find = true; + } + i++; + } + this.removeThread(id); + } + + /** + * createToken() - Méthode appelée à chaque nouvelle authentification + * réussie Ajoute une session a la liste des sessions actives et crée un + * nouveau thread qui va gérer cette session + * + * @param session Un objet Session représentant une nouvelle session active + * + * @see addSession(), addThread(), DbConnector.getConnection() + * @date 25/11/2015 + */ + public void createToken(Session session) { + this.addSession(session); + this.addThread(session.getId(), session.getName()); + } + + + /** + * createTokenFromBD() - Méthode appelée à chaque nouvelle authentification + * réussie Ajoute une ancienne session a la liste des sessions actives et crée un + * nouveau thread qui va gérer cette session + * + * @param session Un objet Session représentant une nouvelle session active + * + * @see addSession(), addThread(), DbConnector.getConnection() + * @date 25/11/2015 + */ + public void createTokenFromBD(Session session) { + if (this.listSession == null) { + this.listSession = new ArrayList(); + } + this.listSession.add(session); + this.addThread(session.getId(), session.getName()); +// logger.debug(JsonConverter.ConvertToJson(this.listSession)); + } + + /** + * checkAuthentification() - Vérifie que la session déterminée par son id + * est encore valable et rajoute du temps de connection si la session est + * valide + * + * @param id L'identifiant de la session + * @return true si la session est valide ou false si elle ne l'est pas + * + * @see reloadToken() + * @date 25/11/2015 + */ + public boolean checkAuthentification(String id) { + if (id != null && (this.listSession != null && !this.listSession.isEmpty())) { + int i = 0; + while (i < listSession.size()) { + if (id.equals(listSession.get(i).getId())) { +// this.reloadToken(id); + return true; + } + i++; + } + } + return false; + } + + public void shutdown() { + if (this.listSession != null && !this.listSession.isEmpty() && listThread != null && !listThread.isEmpty()) { + int i = 0; + while (i < listThread.size()) { + this.removeSession(listThread.get(i).getSessionId()); + listThread.get(i).interrupt(); + listThread.remove(listThread.get(i)); + } + listThread = null; + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/authentication/TokenResponseStructure.java b/phis2-ws/src/main/java/phis2ws/service/authentication/TokenResponseStructure.java new file mode 100644 index 000000000..86f8ba102 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/authentication/TokenResponseStructure.java @@ -0,0 +1,52 @@ +package phis2ws.service.authentication; + +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.view.brapi.Metadata; + +/** + * TokenResponseStructure - Prépare le résultat final pour qu'il soit conforme + * au modèle de la Plant breeding API + * + * @version1.0 + * + * @author Samuël Chérimont + * @date 25/11/2015 + * @note session_token n'est pas conforme aux norme de nomination Java + */ +public class TokenResponseStructure { + + private final Metadata metadata; + private final String userDisplayName; + private final String access_token; + private final String expires_in; + + /** + * ResultForm() - Prepare l'objet métadata vide et associe l'identifiant de + * session à la variable session_token + * + * @param id L'identifiant de session + * @param userDisplayName nom et prénome de l'utilisateur + * + * @see result_output.Metadata + * @date 25/11/2015 + * @update 08/2016 + */ + public TokenResponseStructure(String id, String userDisplayName) { + this.metadata = new Metadata(0, 0, 1); + this.userDisplayName = userDisplayName; + this.access_token = id; + this.expires_in = PropertiesFileManager.getConfigFileProperty("service", "sessionTime"); + } + + public TokenResponseStructure(String id, String userDisplayName, String expires_in) { + this.metadata = new Metadata(0, 0, 1); + this.userDisplayName = userDisplayName; + this.access_token = id; + if (expires_in == null) { + this.expires_in = PropertiesFileManager.getConfigFileProperty("service", "sessionTime"); + } else { + this.expires_in = expires_in; + } + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/configuration/DateFormats.java b/phis2-ws/src/main/java/phis2ws/service/configuration/DateFormats.java new file mode 100644 index 000000000..86c290c0a --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/configuration/DateFormats.java @@ -0,0 +1,37 @@ +//********************************************************************************************** +// DateFormats.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Dates formats used in the WS +//*********************************************************************************************** +package phis2ws.service.configuration; + +import java.util.Arrays; +import java.util.List; + +/** + * Regroupe les formats de date utilisées dans tout le webService + * @date 08/16 + * @author A. CHARLEROY + */ +public final class DateFormats { + + + public final static String DATETIME_MONGO_MEASURE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSSZZ"; + public final static String DATETIME_JSON_SERIALISATION_FORMAT = "yyyy-MM-dd HH:mm:ssZZ"; + public final static String DATETIME_METEO_DB_FORMAT = "yyyy-MM-dd HH:mm:ssZZ"; + public final static String DATETIME_SPARQL_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZZ"; + public final static List DATETIME_MONGO_FORMAT = Arrays.asList("yyyy-MM-dd HH:mm:ss.SSSZZ", "yyyy-MM-dd HH:mm:ss.SSSSSS", "yyyy-MM-dd HH:mm:ss.SSSSSSZZ", "yyyy-MM-dd HH:mm:ssZZ"); + + public final static List AUTHORIZED_DATE_FORMATS = Arrays.asList("yyyy-MM-dd HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ssZZ", "yyyy-MM-dd"); + public final static List AUTHORIZED_USER_METEO_DATE_FORMATS = Arrays.asList("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ssZZ"); + public final static List AUTHORIZED_USER_SPARQL_DATE_FORMATS = Arrays.asList("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ssZZ"); + public final static List AUTHORIZED_USER_MONGO_DATE_FORMATS = Arrays.asList("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd HH:mm:ssZZ", "yyyy-MM-dd HH:mm:ss.SSSZZ"); + + public final static String DATETIME_YMD_FORMAT = "yyyy-MM-dd"; +} diff --git a/phis2-ws/src/main/java/phis2ws/service/configuration/DefaultBrapiPaginationValues.java b/phis2-ws/src/main/java/phis2ws/service/configuration/DefaultBrapiPaginationValues.java new file mode 100644 index 000000000..5f0ec2798 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/configuration/DefaultBrapiPaginationValues.java @@ -0,0 +1,24 @@ +//********************************************************************************************** +// DefaultBrapiPaginationValues.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Give BRAPI parameters defaut values +//*********************************************************************************************** +package phis2ws.service.configuration; + +/** + * Défini les valeurs par défaut du WebService selon la Plant Breeding API + * 06/16 + * @author Arnaud CHARLEROY + * @see http://docs.brapi.apiary.io/# + */ +public final class DefaultBrapiPaginationValues { + + public final static String PAGE_SIZE = "20"; + public final static String PAGE = "0"; +} diff --git a/phis2-ws/src/main/java/phis2ws/service/configuration/GlobalWebserviceValues.java b/phis2-ws/src/main/java/phis2ws/service/configuration/GlobalWebserviceValues.java new file mode 100644 index 000000000..f56528cdf --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/configuration/GlobalWebserviceValues.java @@ -0,0 +1,35 @@ +//********************************************************************************************** +// GlobalWebserviceValues.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Recognize sessionInject utilisation in jersey resources +//*********************************************************************************************** +package phis2ws.service.configuration; + +import phis2ws.service.documentation.StatusCodeMsg; +import javax.ws.rs.core.Response; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.ResponseFormGET; + +/** + * Définit des réponse qui seront utilisées par défaut dans le + * WebService + * @date 07/16 + * @author Arnaud CHARLEROY + */ +public final class GlobalWebserviceValues { + +// BRAPI V1 + public static final String AUTHORIZATION_PROPERTY = "Authorization"; + public static final String AUTHENTICATION_SCHEME = "Bearer"; + public static final Response ACCESS_DENIED = Response.status(Response.Status.UNAUTHORIZED) + .entity(new ResponseFormGET(new Status("Access error",StatusCodeMsg.ERR, "You cannot access this resource"))).build(); + public static final Response ACCESS_FORBIDDEN = Response.status(Response.Status.FORBIDDEN) + .entity(new ResponseFormGET(new Status("Access error",StatusCodeMsg.ERR, "Access blocked for all users !!"))).build(); + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/configuration/URINamespaces.java b/phis2-ws/src/main/java/phis2ws/service/configuration/URINamespaces.java new file mode 100644 index 000000000..a721643dd --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/configuration/URINamespaces.java @@ -0,0 +1,186 @@ +//********************************************************************************************** +// URINamespaces.java from uris.php +// +// Author(s): Isabelle NEMBROT, Arnaud CHARLEROY, Morgane Vidal +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:i.nembrot@laposte.net, arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: June, 2017 (adaptation to PHIS field) +// Subject: personal parameters for global usage +// G_URI : +// - M3P URI for further use so that we don"t need to repeat paths in each page +// Usage : +// ~~~~~~ URINamespaces uris = new URINamespaces("m3p"); +// ~~~~~~ String vocabulary = URINamespaces.getContextsProperty("pVocaPlateform") +//*********************************************************************************************** + +package phis2ws.service.configuration; + +import java.util.HashMap; +import java.util.Map; +import phis2ws.service.PropertiesFileManager; + + +/** + * Permet de creer des URI de concepts, objets et relation entre objets et proprietes en fonction pour une platform + * @author A. CHARLEROY + */ +public class URINamespaces { + + private static final Map CONTEXTS = new HashMap<>(); + private static final Map NAMESPACES = new HashMap<>(); + private static final Map OBJECTS = new HashMap<>(); + private static final Map RELATIONS = new HashMap<>(); + private static final Map W3C_NAMESPACES = new HashMap<>(); + private static String platform = null; + + public URINamespaces() { + platform = PropertiesFileManager.getConfigFileProperty("sesame_rdf_config", "platform");; + setContexts(); + setNamespaces(); + setW3CNamespaces(); + setRelations(); + setObjects(); + } + + public Map getContexts() { + return URINamespaces.CONTEXTS; + } + + public String getContextsProperty(String prop) { + String propValue = null; + if (URINamespaces.CONTEXTS.containsKey(prop)) { + propValue = URINamespaces.CONTEXTS.get(prop); + } + return propValue; + } + + public String getObjectsProperty(String prop) { + String propValue = null; + if (URINamespaces.OBJECTS.containsKey(prop)) { + propValue = URINamespaces.OBJECTS.get(prop); + } + return propValue; + } + + public String getNamespaceProperty(String prop) { + if (URINamespaces.NAMESPACES.containsKey(prop)) { + return URINamespaces.NAMESPACES.get(prop); + } else { + return null; + } + } + + public boolean objectsPropertyContainsValue(String value) { + return OBJECTS.containsValue(value); + } + + public String getRelationsProperty(String prop) { + String propValue = null; + if (URINamespaces.RELATIONS.containsKey(prop)) { + propValue = URINamespaces.RELATIONS.get(prop); + } + return propValue; + } + + public String getW3CNamespacesProp(String prop) { + String propValue = null; + if (URINamespaces.W3C_NAMESPACES.containsKey(prop)) { + propValue = URINamespaces.W3C_NAMESPACES.get(prop); + } + return propValue; + } + + private void setContexts() { + if (platform == null) { + platform = ""; + } + //px => prefix + //c => context + //n => namespace + //p => phenome + + + + //Plateforme et préfixes + CONTEXTS.put("pxPhenome", "http://www.phenome-fppn.fr"); + CONTEXTS.put("pxDublinCore", "http://purl.org/dc/terms"); + CONTEXTS.put("pxPlatform", CONTEXTS.get("pxPhenome") + "/" + platform); + CONTEXTS.put("pxGeoSPARQL", "http://www.opengis.net/ont/geosparql#"); + + + //Context(s) + CONTEXTS.put("pVoc2017", CONTEXTS.get("pxPhenome") + "/vocabulary/2017"); + CONTEXTS.put("documents", CONTEXTS.get("pxPlatform") + "/documents"); + CONTEXTS.put("agronomicalObjects", CONTEXTS.get("pxPlatform") + "/agronomicalObjects"); + CONTEXTS.put("variables", CONTEXTS.get("pxPlatform") + "/variables"); + + + + //Contextes de phenomeApi version serre +// CONTEXTS.put("pxPhenome", "http://www.phenome-fppn.fr"); +// CONTEXTS.put("pExperiment", "http://www.phenome-fppn.fr/m3p/experiment"); +// CONTEXTS.put("pVocaPlateform", "http://www.phenome-fppn.fr/vocabulary/2015"); +// CONTEXTS.put("annotation", "http://www.mistea.supagro.inra.fr/ontologies/2014/v1/annotation"); +// CONTEXTS.put("annotSemantic", "http://www.mistea.supagro.inra.fr/ontologies/2014/v1/semanticAnnotation"); +// CONTEXTS.put("pEvent", "http://www.phenome-fppn.fr/vocabulary/"+ platform + "/2015/event"); +// CONTEXTS.put("eventRepo", "http://www.phenome-fppn.fr/" + platform + "/event"); +// CONTEXTS.put("annotationRepo", "http://www.phenome-fppn.fr/" + platform + "/annotation"); +// CONTEXTS.put("documents", "http://www.mistea.supagro.inra.fr/ontologies/2014/v1/documents"); +// CONTEXTS.put("documentsRepo", "http://www.phenome-fppn.fr/" + platform + "/document"); + } + + private void setNamespaces() { + NAMESPACES.put("variables", CONTEXTS.get("pxPlatform") + "/id/variables"); + NAMESPACES.put("traits", CONTEXTS.get("pxPlatform") + "/id/traits"); + NAMESPACES.put("methods", CONTEXTS.get("pxPlatform") + "/id/methods"); + NAMESPACES.put("units", CONTEXTS.get("pxPlatform") + "/id/units"); + } + + private void setW3CNamespaces() { + +// W3C_NAMESPACES.put("time", "http://www.w3.org/2006/time"); + } + + private void setObjects() { + //c , concept + OBJECTS.put("cDocuments", CONTEXTS.get("pVoc2017") + "#Document"); + OBJECTS.put("cExperiment", CONTEXTS.get("pVoc2017") + "#Experiment"); + OBJECTS.put("cPlot", CONTEXTS.get("pVoc2017") + "#Plot"); + OBJECTS.put("cFields", CONTEXTS.get("pVoc2017") + "#Fields"); + OBJECTS.put("cCultivatedLand", CONTEXTS.get("pVoc2017") + "#CultivatedLand"); + OBJECTS.put("cVariety", CONTEXTS.get("pVoc2017") + "#Variety"); + OBJECTS.put("cGenotype", CONTEXTS.get("pVoc2017") + "#Genotype"); + OBJECTS.put("cSpecies", CONTEXTS.get("pVoc2017") + "#Species"); + OBJECTS.put("cVariable", CONTEXTS.get("pVoc2017") + "#Variable"); + OBJECTS.put("cTrait", CONTEXTS.get("pVoc2017") + "#Trait"); + OBJECTS.put("cMethod", CONTEXTS.get("pVoc2017") + "#Method"); + OBJECTS.put("cUnit", CONTEXTS.get("pVoc2017") + "#Unit"); + } + + private void setRelations() { + //r , relation + //d , draft + //v , validated draft + + RELATIONS.put("rHasDocument", CONTEXTS.get("pVoc2017") + "#hasDocument"); + RELATIONS.put("rConcern", CONTEXTS.get("pVoc2017") + "#concern"); + RELATIONS.put("rHasPlot", CONTEXTS.get("pVoc2017") + "#hasPlot"); + RELATIONS.put("rFromGenotype", CONTEXTS.get("pVoc2017") + "#fromGenotype"); + RELATIONS.put("rFromVariety", CONTEXTS.get("pVoc2017") + "#fromVariety"); + RELATIONS.put("rExperimentModalities", CONTEXTS.get("pVoc2017") + "#hasExperimentModalities"); + RELATIONS.put("rHasRepetition", CONTEXTS.get("pVoc2017") + "#hasRepetition"); + RELATIONS.put("rHasAlias", CONTEXTS.get("pVoc2017") + "#hasAlias"); + RELATIONS.put("rStatus", CONTEXTS.get("pVoc2017") + "#status"); + RELATIONS.put("rHasTrait", CONTEXTS.get("pVoc2017") + "#hasTrait"); + RELATIONS.put("rHasMethod", CONTEXTS.get("pVoc2017") + "#hasMethod"); + RELATIONS.put("rHasUnit", CONTEXTS.get("pVoc2017") + "#hasUnit"); + + //Relations skos + RELATIONS.put("rExactMatch", "http://www.w3.org/2008/05/skos#exactMatch"); + RELATIONS.put("rCloseMatch", "http://www.w3.org/2008/05/skos#closeMatch"); + RELATIONS.put("rNarrower", "http://www.w3.org/2008/05/skos#narrower"); + RELATIONS.put("rBroader", "http://www.w3.org/2008/05/skos#broader"); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/datasource/AbstractSQLDataSource.java b/phis2-ws/src/main/java/phis2ws/service/dao/datasource/AbstractSQLDataSource.java new file mode 100644 index 000000000..7cd8d0b4e --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/datasource/AbstractSQLDataSource.java @@ -0,0 +1,39 @@ +//********************************************************************************************** +// AbstractSQLDataSource.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Abstract class for SQL datasources +//*********************************************************************************************** +package phis2ws.service.dao.datasource; + +import org.apache.tomcat.jdbc.pool.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Représente une source de données avec les paramètres communs à toutes les + * sources de données SQL + * + * @author Arnaud CHARLEROY + */ +public abstract class AbstractSQLDataSource extends DataSource { + + // Récupération des logs + final static Logger logger = LoggerFactory.getLogger(AbstractSQLDataSource.class); + // Fichier de configuration + protected static String propertyFileName; + + public static String getPropertyFileName() { + return propertyFileName; + } + + public static void setPropertyFileName(String propertyFileName) { + AbstractSQLDataSource.propertyFileName = propertyFileName; + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/datasource/DataSourceDAOPhisBrapi.java b/phis2-ws/src/main/java/phis2ws/service/dao/datasource/DataSourceDAOPhisBrapi.java new file mode 100644 index 000000000..c011be248 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/datasource/DataSourceDAOPhisBrapi.java @@ -0,0 +1,78 @@ +//********************************************************************************************** +// DataSourceDAOPhisBrapi.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Datasource for Phis database, create a pool of connexion for this database +//*********************************************************************************************** +package phis2ws.service.dao.datasource; + +import java.sql.Connection; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import org.apache.tomcat.jdbc.pool.PoolProperties; +import static phis2ws.service.PropertiesFileManager.getSQLPoolDataSourceProperties; + +/** + * Source de données qui gère un ensemble de connexion pour la base de données + * relationnelle Postgresql Phis pattern SingletonHolder + * + * @date 05/2016 + * @author Arnaud CHARLEROY + */ +public abstract class DataSourceDAOPhisBrapi extends AbstractSQLDataSource { + + private DataSourceDAOPhisBrapi() { + setPropertyFileName("phis_sql_config"); + // récupération des propriétés + final PoolProperties p = getSQLPoolDataSourceProperties(propertyFileName); + + try { + this.setPoolProperties(p); // S'l n'y a aucune connexion le web service propage une exception INTERNAL_SERVER_ERROR + } catch (Exception e) { + logger.error("Can not access to Phis Database.", e); + throw new WebApplicationException( + Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Can not access to Phis Database : " + e.getMessage()).build()); + } + } + + /** + * ThreadSafe + */ + private static class DataSourceDAOPhisBrapiHolder { + + final private static DataSourceDAOPhisBrapi instance = new DataSourceDAOPhisBrapi() { + }; + } + + /** + * Récupère une et unique instance du pool de connexion + * + * @return DataSourceDAOPhisBrapi + */ + public static DataSourceDAOPhisBrapi getInstance() { + return DataSourceDAOPhisBrapiHolder.instance; + } + + /** + * Récupère une connexion du pool de connexion + * + * @return Connection + */ + public static Connection getInstanceConnection() { + try { + return getInstance().getConnection(); + } catch (Exception e) { + logger.error("Can not access to Phis Database.", e); + throw new WebApplicationException( + Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Can not access to Phis Database : " + e.getMessage()) + .build()); + } + + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAO.java b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAO.java new file mode 100644 index 000000000..6052fe11a --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAO.java @@ -0,0 +1,67 @@ +//********************************************************************************************** +// DAO.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A list of reusable functions for all DAO inherit class +//*********************************************************************************************** +package phis2ws.service.dao.manager; + +/** + * Représente les fonctions de bases pour tout DAO + * + * @author Arnaud CHARLEROY + * @param + */ +public abstract class DAO { + + /** + * Méthode de création + * + * @param obj + * @return boolean + * @throws java.lang.Exception + */ + public abstract Boolean create(T obj) throws Exception; + + /** + * Méthode pour effacer + * + * @param obj + * @return boolean + * @throws java.lang.Exception + */ + public abstract boolean delete(T obj) throws Exception; + + /** + * Méthode de mise à jour + * + * @param obj + * @return boolean + * @throws java.lang.Exception + */ + public abstract boolean update(T obj) throws Exception; + + /** + * Méthode de recherche des informations + * + * @param obj + * @return T + * @throws java.lang.Exception + */ + public abstract T find(T obj) throws Exception; + + /** + * Méthode de test d'existence + * + * @param obj + * @return T + * @throws java.lang.Exception + */ + public abstract boolean existInDB(T obj) throws Exception; + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOFactory.java b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOFactory.java new file mode 100644 index 000000000..66b3a4ae4 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOFactory.java @@ -0,0 +1,38 @@ +//********************************************************************************************** +// DAOFactory.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@inra.fr, morgane.vidal@inra.fr anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: MArch, 2017 +// Subject: Class which permit to list availableDAO +//*********************************************************************************************** +package phis2ws.service.dao.manager; + +public class DAOFactory { + protected DAOFactory() { + } + /** + * Existing Dao for MongoDB + * @return MongoDBDAOFactory + */ +// public static MongoDBDAOFactory getMongoDBDAOFactory() { +// return new MongoDBDAOFactory(); +// } + /** + * Existing Dao for SQL Database + * @return SQLDAOFactory + */ +// public static SQLDAOFactory getSQLDAOFactory() { +// return new SQLDAOFactory(); +// } + /** + * Existing Dao for Sesame + * @return SESAMEDAOFactory + */ + public static SESAMEDAOFactory getSESAMEDAOFactory() { + return new SESAMEDAOFactory(); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOMongo.java b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOMongo.java new file mode 100644 index 000000000..1be99453e --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOMongo.java @@ -0,0 +1,166 @@ +//********************************************************************************************** +// DAOMongo.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject:This abstract class is the base of all Dao class for the Mongo DB +//*********************************************************************************************** +package phis2ws.service.dao.manager; + +import com.mongodb.BasicDBObject; +import com.mongodb.MongoClient; +import com.mongodb.MongoClientURI; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.gridfs.GridFS; +import java.util.ArrayList; +import org.bson.Document; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.model.User; + +/** + * Répresente une définition de la classe DAO permettant de se connecter à la + * source de données MongoDB + * + * @author Arnaud CHARLEROY + * @param + */ +public abstract class DAOMongo { + + /** + * @see service.properties file + */ + private final static MongoClient MONGO_CLIENT = new MongoClient( + new MongoClientURI(PropertiesFileManager.getConfigFileProperty("mongodb_nosql_config", "url"))); + protected GridFS gridFS = new GridFS(MONGO_CLIENT.getDB(PropertiesFileManager.getConfigFileProperty("mongodb_nosql_config", "db"))); + protected MongoDatabase database; + protected MongoCollection collection; + + public User user; + protected Integer page; + protected Integer pageSize; + /** + * User ip adress + */ + protected String remoteUserAdress; + + /** + * @see service.properties file + */ + public DAOMongo() { + this.setDatabase(MONGO_CLIENT.getDatabase(PropertiesFileManager.getConfigFileProperty("mongodb_nosql_config", "db"))); + } + + public static MongoClient getMongoClient() { + return MONGO_CLIENT; + } + + public MongoDatabase getDatabase() { + return database; + } + + public void setDatabase(MongoDatabase database) { + this.database = database; + } + + + + public MongoCollection getCollection() { + return collection; + } + + public void setCollection(MongoCollection collection) { + this.collection = collection; + } + + /** + * La page de l'api brapi commence à 0 + * + * @return numéro de la page courante + */ + public Integer getPage() { + if (page == null || pageSize < 0) { + return 0; + } + return page; + } + + /** + * La page de l'api brapi pour pouvoir l'utiliser pour la pagination dans + * une base de données + * + * @return numéro de la page courante + 1 + */ + public Integer getPageForDBQuery() { + if (page == null || pageSize < 0) { + return 1; + } + return page + 1; + } + + /** + * Définit le paramètre page + * + * @param page + */ + public void setPage(Integer page) { + if (page < 0) { + this.page = Integer.valueOf(DefaultBrapiPaginationValues.PAGE); + } + this.page = page; + } + + /** + * Retourne le paramètre taille de la page + */ + public Integer getPageSize() { + if (pageSize == null || pageSize < 0) { + return Integer.valueOf(DefaultBrapiPaginationValues.PAGE_SIZE); + } + return pageSize; + } + + /** + * + * @return Les logs qui seront utilisés pour la traçabilité + */ + protected String getTraceabilityLogs() { + String log = ""; + if (remoteUserAdress != null) { + log += "IP Address " + remoteUserAdress + " - "; + } + if (user != null) { + log += "User : " + user.getEmail() + " - "; + } + + return log; + } + + /** + * Définit le paramètre taille de page + * + * @param pageSize + */ + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + /** + * Fonction qui permet de créer la partie commune d'une requête à la fois + * pour lister les éléments et les récupérés + * + * @return BasicDBObject + */ + abstract protected BasicDBObject prepareSearchQuery(); + + /** + * Retourne les élements retournés par la requête en prenant en compte la pagination de l'utilisateur + * @return + */ + public abstract ArrayList allPaginate(); +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOPhisBrapi.java b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOPhisBrapi.java new file mode 100644 index 000000000..1e7176d68 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOPhisBrapi.java @@ -0,0 +1,71 @@ +//********************************************************************************************** +// DAOPhisBrapi.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: august 2016 +// Contact:arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: January, 2017 +// Subject:This abstract class is the base of all Dao class for the Phis DB +//*********************************************************************************************** +package phis2ws.service.dao.manager; + +import java.util.List; +import phis2ws.service.dao.datasource.DataSourceDAOPhisBrapi; +import phis2ws.service.utils.POSTResultsReturn; + +/** + * Répresente une définition de la classe DAO permettant de se connecter à la + * source de données Phis tout en ayant les méthodes déjà définies + * + * @author Arnaud CHARLEROY + * @date 05/2016 + * @param Classe représentant l'objet + * @param Classe représentant l'objet à enregistrer en BD + */ +public abstract class DAOPhisBrapi extends SQLDAO { + + public DAOPhisBrapi() { + if (dataSource == null) { + dataSource = DataSourceDAOPhisBrapi.getInstance(); + } + } + + /** + * + * @param newObject + * @return + */ + public abstract POSTResultsReturn checkAndInsert(D newObject); + + /** + * Vérifie les données et les enregistre en base de données + * @param newObjects + * @return + */ + public abstract POSTResultsReturn checkAndInsertList(List newObjects); + + /** + * Vérifie les données et fais les modifications en BD + * @param newObjects + * @return + */ + public abstract POSTResultsReturn checkAndUpdateList(List newObjects); + + /** + * + * @return Les logs qui seront utilisés pour la traçabilité + */ + protected String getTraceabilityLogs() { + String log = ""; + if (remoteUserAdress != null) { + log += "IP Address " + remoteUserAdress + " - "; + } + if (user != null) { + log += "User : " + user.getEmail() + " - "; + } + + return log; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOSesame.java b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOSesame.java new file mode 100644 index 000000000..6dbbd3070 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/manager/DAOSesame.java @@ -0,0 +1,278 @@ +//********************************************************************************************** +// DAOSesame.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject:This abstract class is the base of all Dao class for the Sesame TripleStore +//*********************************************************************************************** +package phis2ws.service.dao.manager; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.repository.Repository; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.repository.RepositoryException; +import org.eclipse.rdf4j.repository.http.HTTPRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.authentication.TokenManager; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.model.User; +import phis2ws.service.utils.sparql.SPARQLQueryBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.ResponseFormPOST; + +/** + * Répresente une définition de la classe DAO permettant de se connecter au + * TripleStore Sesame + * + * @author Arnaud CHARLEROY + * @param + */ +public abstract class DAOSesame { + + final static Logger LOGGER = LoggerFactory.getLogger(DAOSesame.class); + protected static final String PROPERTY_FILENAME = "sesame_rdf_config"; + //SILEX:test + // Pour le soucis de pool de connexion plein + protected static final String SESAME_SERVER = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "sesameServer"); + protected static final String REPOSITORY_ID = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "repositoryID"); + //\SILEX:test + + protected static Repository rep; + private RepositoryConnection connection; + + protected static String resourceType; + + public User user; + protected Integer page; + protected Integer pageSize; + /** + * User ip adress + */ + public String remoteUserAdress; + + public DAOSesame() { + try { + String sesameServer = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "sesameServer"); + String repositoryID = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "repositoryID"); + rep = new HTTPRepository(sesameServer, repositoryID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + } catch (Exception e) { + ResponseFormPOST postForm = new ResponseFormPOST(new Status("Can't connect to triplestore", StatusCodeMsg.ERR, e.getMessage())); + throw new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(postForm).build()); + } + } + + public DAOSesame(String repositoryID) { + try { + String sesameServer = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "sesameServer"); + rep = new HTTPRepository(sesameServer, repositoryID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + } catch (Exception e) { + ResponseFormPOST postForm = new ResponseFormPOST(new Status("Can't connect to triplestore", StatusCodeMsg.ERR, e.getMessage())); + throw new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(postForm).build()); + } + } + + public RepositoryConnection getConnection() { + return connection; + } + + public final void setConnection(RepositoryConnection connection) { + this.connection = connection; + } + + public static Repository getRepository() { + return rep; + } + + /** + * La page de l'api brapi commence à 0 + * + * @return numéro de la page courante + */ + public Integer getPage() { + if (page == null || pageSize < 0) { + return 0; + } + return page; + } + + /** + * La page de l'api brapi pour pouvoir l'utiliser pour la pagination dans + * une base de données + * + * @return numéro de la page courante + 1 + */ + public Integer getPageForDBQuery() { + if (page == null || pageSize < 0) { + return 1; + } + return page + 1; + } + + /** + * Définit le paramètre page + * + * @param page + */ + public void setPage(Integer page) { + if (page < 0) { + this.page = Integer.valueOf(DefaultBrapiPaginationValues.PAGE); + } + this.page = page; + } + + /** + * Retourne le paramètre taille de la page + * @return + */ + public Integer getPageSize() { + if (pageSize == null || pageSize < 0) { + return Integer.valueOf(DefaultBrapiPaginationValues.PAGE_SIZE); + } + return pageSize; + } + + /** + * Définit le paramètre taille de page + * + * @param pageSize + */ + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + /** + * Méthode de test d'existence d'un sujet par triplet + * + * @param subject + * @param predicate + * @param object + * @return boolean + */ + public boolean exist(String subject, String predicate, String object) throws RepositoryException, MalformedQueryException, QueryEvaluationException { + boolean exist = false; + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + query.appendSelect(null); + query.appendTriplet(subject, predicate, object, null); + query.appendParameters("LIMIT 1"); + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, query.toString()); + try (TupleQueryResult result = tupleQuery.evaluate()) { + if (result.hasNext()) { + exist = true; + } + } +// LOGGER.trace(query.toString()); + return exist; + } + + /** + * + * @param objectURI l'uri de l'objet recherché + * @return true si l'objet est dans le triplestore, + * false sinon + */ + public boolean existObject(String objectURI) { + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + query.appendSelect("?p"); + query.appendTriplet(objectURI, "?p", "?o", null); + query.appendParameters("LIMIT 1"); + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, query.toString()); + try (TupleQueryResult result = tupleQuery.evaluate()) { + if (result.hasNext()) { + return true; + } + } + + return false; + } + + /** + * Méthode de récupération d'élement d'existence par triplet + * + * @param subject + * @param predicate + * @return + * @throws RepositoryException + * @throws MalformedQueryException + * @throws QueryEvaluationException + */ + public String getValueFromPredicate(String subject, String predicate) throws RepositoryException, MalformedQueryException, QueryEvaluationException { + String value = null; + if (subject != null || predicate != null) { + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + query.appendSelect("?x"); + query.appendTriplet(subject, predicate, "?x", null); + query.appendParameters("LIMIT 1"); + LOGGER.trace(query.toString()); + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, query.toString()); + try (TupleQueryResult result = tupleQuery.evaluate()) { + if (result.hasNext()) { + value = result.next().getBinding("x").getValue().stringValue(); + } + } + LOGGER.trace(value); + } + return value; + } + + /** + * Fonction qui permet de créer la partie commune d'une requête à la fois + * pour lister les éléments et les récupérés + * + * @return SPARQLQueryBuilder + */ + abstract protected SPARQLQueryBuilder prepareSearchQuery(); + + /** + * Compte le nombre d'élement retournés par la requête + * + * @return Integer + */ + public abstract Integer count() throws RepositoryException, MalformedQueryException, QueryEvaluationException; + + /** + * + * @return Les logs qui seront utilisés pour la traçabilité + */ + protected String getTraceabilityLogs() { + String log = ""; + if (remoteUserAdress != null) { + log += "IP Address " + remoteUserAdress + " - "; + } + if (user != null) { + log += "User : " + user.getEmail() + " - "; + } + + return log; + } + + /** + * Définit un objet utilisateur à partir d'un identifiant + * + * @param id identifiant + */ + public void setUser(String id) { +// LOGGER.debug(JsonConverter.ConvertToJson(TokenManager.Instance().getSession(id).getUser())); + if (TokenManager.Instance().getSession(id).getUser() == null) { + throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build()); + } else { + this.user = TokenManager.Instance().getSession(id).getUser(); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/manager/SESAMEDAOFactory.java b/phis2-ws/src/main/java/phis2ws/service/dao/manager/SESAMEDAOFactory.java new file mode 100644 index 000000000..7b3974e47 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/manager/SESAMEDAOFactory.java @@ -0,0 +1,20 @@ +//********************************************************************************************** +// SESAMEDAOFactory.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 2016 +// Subject: List available Dao which are connected to Sesame +//*********************************************************************************************** +package phis2ws.service.dao.manager; + +import phis2ws.service.dao.sesame.DocumentDaoSesame; + +public class SESAMEDAOFactory extends DAOFactory { + public DocumentDaoSesame getDocumentsDaoSesame() { + return new DocumentDaoSesame(); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/manager/SQLDAO.java b/phis2-ws/src/main/java/phis2ws/service/dao/manager/SQLDAO.java new file mode 100644 index 000000000..34443fcd8 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/manager/SQLDAO.java @@ -0,0 +1,648 @@ +//********************************************************************************************** +// SQLDAO.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: List all methods usable for all SQL Database DAO +//*********************************************************************************************** +package phis2ws.service.dao.manager; + +import java.lang.reflect.Field; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.logging.Level; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import org.apache.tomcat.jdbc.pool.DataSource; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.postgis.Geometry; +import org.postgresql.util.PGobject; +import phis2ws.service.authentication.TokenManager; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.model.User; +import phis2ws.service.utils.JsonConverter; +import phis2ws.service.utils.sql.SQLQueryBuilder; + +/** + * Répresente les attributs et les methode d'un DAO se connectant à une base de + * données relationnelle + * + * @author Arnaud CHARLEROY + * @param + * @date 05/2016 + */ +public abstract class SQLDAO extends DAO { + + private final static Logger logger = LoggerFactory.getLogger(SQLDAO.class); + protected final static String DUPLICATE_KEY_ERROR_POSTGRE = "23505"; + + /** + * user c'est l'objet qui représente l'utilisateur + */ + public User user; + protected Integer page; + protected Integer pageSize; + + public String remoteUserAdress; + /** + * Nom de la table du dao (table principale) + */ + protected String table; + protected String tableAlias; + + /** + * Connexion du DAO pool de con ;) + */ + protected DataSource dataSource; + + /** + * pour le batch + */ + protected final int batchSize = 1000; + +// protected static Connection connectCommitFalse = null; + public DataSource getDataSource() { + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + public String getTableAlias() { + return tableAlias; + } + + public void setTableAlias(String alias) { + this.tableAlias = alias; + } + + /** + * La page de l'api brapi commence à 0 + * + * @return numéro de la page courante + */ + public Integer getPage() { + if (page == null || pageSize < 0) { + return 0; + } + return page; + } + + /** + * La page de l'api brapi pour pouvoir l'utiliser pour la pagination dans + * une base de données + * + * @return numéro de la page courante + 1 + */ + public Integer getPageForDBQuery() { + if (page == null || pageSize < 0) { + return 1; + } + return page + 1; + } + + /** + * Définit le paramètre page + * + * @param page + */ + public void setPage(Integer page) { + if (page < 0) { + this.page = Integer.valueOf(DefaultBrapiPaginationValues.PAGE); + } + this.page = page; + } + + /** + * Retourne le paramètre taille de la page + * + * @return Integer taille de la page + */ + public Integer getPageSize() { + if (pageSize == null || pageSize < 0) { + return Integer.valueOf(DefaultBrapiPaginationValues.PAGE_SIZE); + } + return pageSize; + } + + /** + * Définit le paramètre taille de page + * + * @param pageSize + */ + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + /** + * Définit un objet utilisateur à partir d'un identifiant + * + * @param id identifiant + */ + public void setUser(String id) { +// logger.debug(JsonConverter.ConvertToJson(TokenManager.Instance().getSession(id).getUser())); + if (TokenManager.Instance().getSession(id).getUser() == null) { + throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build()); + } else { + this.user = TokenManager.Instance().getSession(id).getUser(); + } + } + + public void setTable(String table) { + this.table = table; + } + + /** + * Pas pratique les ressources (statement et resulset) restent ouvertes + * Exécute une requête de recherche à partir d'un DAO qui est en lien vers + * une BD relationnelle. + * + * @author Arnaud CHARLEROY + * @param query + * @return + * @deprecated + * @throws SQLException + */ + public ResultSet selectQueryFromDAO(String query) throws SQLException { + Connection con = null; + Statement stat = null; + con = dataSource.getConnection(); + stat = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); +// System.err.println(query); + return stat.executeQuery(query); + } + + /** + * + * Exécute une requête d'ajout de suppresion ou de mise à jour à partir d'un + * DAO qui est en lien vers une BD relationnelle. + * + * @author Arnaud CHARLEROY + * @param query + * @return + * @throws SQLException + */ + public Integer insertOrUpdateOrDeleteQueryFromDAO(String query) throws SQLException { + Connection con = dataSource.getConnection(); + Statement stat = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + Integer executeUpdate = stat.executeUpdate(query); + stat.close(); + con.close(); + return executeUpdate; + } + + /** + * Prepare une requete sql en utilisant une chaine de variables sous forme + * variable1,variable2 + * + * @author Samuel Cherimon + * @param variables + * @param request + * @update AC 05/16 Rendre la méthode générique + * @param column column pour laquelle les variables seront ajoutées + * @return La chaine correspondant a la requete complète + * + */ + public static String formatMultipleValueQuery(String variables, String column, String request) { + StringTokenizer st = new StringTokenizer(variables, ","); + String result = "("; + result = result + column + " = '" + st.nextToken() + "'"; +// result = result + "mm.\"codeVariable\" = '" + st.nextToken() + "'"; + while (st.hasMoreTokens()) { + result = result + " OR " + column + " = '" + st.nextToken() + "'"; +// result = result + " OR mm.\"codeVariable\" = '" + st.nextToken() + "'"; + } + result = result + ")"; + return request + " AND " + result; + } + + /** + * Construit et exécute la requête qui permet de savoir si un objet de type + * T est présent dans la base dans une base de données relationnelle à + * partir des informations d'un objet T + * + * @param obj l'objet à chercher + * @return + * @throws Exception + */ + @Override + public boolean existInDB(T obj) throws Exception { + String query = new StringBuilder("SELECT * ") + .append("FROM ") + .append("\"").append(table).append("\"") + .append(" WHERE ") + .append(makeFindSQLConditionQuery(obj, false)).toString(); + ResultSet rs = null; + PreparedStatement statement = null; + Connection con = null; + + logger.debug(query); + try { + con = dataSource.getConnection(); + statement = con.prepareStatement(query); + rs = statement.executeQuery(); + if (rs != null) { + return rs.next(); + } + return false; + } catch (SQLException e) { + logger.error("SQL error Exist Request Method ", e); + logger.error(query); + return false; + } finally { + try { + + if (rs != null) { + rs.close(); + } + if (statement != null) { + statement.close(); + } + if (con != null) { + con.close(); + } + } catch (SQLException ex) { + logger.error(ex.getMessage()); + } + } + } + + /** + * Construit et exécute la requête qui permet de trouver un objet de type T + * dans une base de données relationnelle à partir des informations d'un + * objet T, Cette fonction retourne un objet de type T + * + * @param obj l'objet à chercher + * @return + * @throws Exception + */ + @Override + public T find(T obj) throws Exception { + StringBuilder strSQLBuilder = new StringBuilder(); + // Requete SELECT préparée + strSQLBuilder.append("SELECT * ") + .append("FROM ") + .append("\"").append(table).append("\"") + .append(" WHERE ") + .append(makeFindSQLConditionQuery(obj, false)); + logger.debug(strSQLBuilder.toString()); + Statement Statement = null; + ResultSet rs = null; + Connection con = null; + Map objectFields = relationFieldsJavaSQLObject(); + final Field[] attributes = obj.getClass().getDeclaredFields(); + logger.debug(JsonConverter.ConvertToJson(obj)); + try { + con = dataSource.getConnection(); + Statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + rs = Statement.executeQuery(strSQLBuilder.toString()); + if (rs != null && rs.first()) { + for (Field field : attributes) { + field.setAccessible(true); + if (objectFields.containsKey(field.getName()) + && rs.getObject(objectFields.get(field.getName())) != null ) { + + if (rs.getObject(objectFields.get(field.getName())) instanceof Date) { + if (field.getType() == String.class) { + LocalDate fromDateFields = LocalDate.fromDateFields((Date) rs.getObject(objectFields.get(field.getName()))); + field.set(obj, fromDateFields.toString("yyyy-MM-dd")); + } else if (field.getType() == DateTime.class) { + Timestamp ts = new Timestamp(((Date) rs.getObject(objectFields.get(field.getName()))).getTime()); + field.set(obj, new DateTime(ts)); + } + + } else if (rs.getObject(objectFields.get(field.getName())) instanceof Geometry) { + if (field.getType() == String.class) { + field.set(obj, field.toString()); + } + } else { + field.set(obj, rs.getObject(objectFields.get(field.getName()).replaceAll("\"", "")).toString()); + } + } + } + } + return obj; + } catch (SQLException e) { + logger.error("SQL error Exist Request ", e); + logger.error(strSQLBuilder.toString()); +// e.printStackTrace(); + return null; + } finally { +// logger.debug(strSQLBuilder.toString()); + if (Statement != null) { + try { + Statement.close(); + } catch (SQLException ex) { + logger.error(ex.getMessage()); + } + } + if (rs != null) { + rs.close(); + } + if (con != null) { + con.close(); + } + } + } + + /** + * Construit et exécute la requête qui permet d'inserér un objet de type T + * dans une base de données relationnelle à partir des informations d'un + * objet T + * + * @param obj l'objet à inserér + * @return + * @throws Exception + */ + @Override + public Boolean create(T obj) throws Exception { + String query = new StringBuilder("INSERT INTO ") + .append("\"").append(table).append("\" ").toString(); + Connection con = null; + PreparedStatement preparedStatement = null; + try { + con = dataSource.getConnection(); + preparedStatement = makeCreatePreparedSQLConditionQuery(con, query, obj); + preparedStatement.executeUpdate(); + String log = ""; + if (remoteUserAdress != null) { + log += " IP Adress : " + remoteUserAdress + " - "; + } + if (user != null) { + log += "User : " + user.getEmail() + "-"; + } + logger.trace(log + " query : " + preparedStatement.toString()); +// logger.trace(preparedStatement.toString()); +// logger.debug(preparedStatement.toString()); + return true; + } catch (SQLException e) { +// System.err.println(e.getMessage()); + if (e.getSQLState().contains(DUPLICATE_KEY_ERROR_POSTGRE)) { + return null; + } else { + logger.error("SQL error Create Request " + e.getErrorCode() + e.getSQLState(), e); + return false; + } + } finally { + if (preparedStatement != null) { + preparedStatement.close(); + } + if (con != null) { + con.close(); + } + + } + + } + + /** + * HashMap qui décrit les attributs qui correspondent à la clé primaire de + * l'objet et leur label dans la BD + * + * @return Map + */ + public abstract Map pkeySQLFieldLink(); + + /** + * HashMap qui décrit les attributs qui correspondent à l'objet et leur + * label dans la BD + * + * @return Map + */ + public abstract Map relationFieldsJavaSQLObject(); + + @Override + public boolean delete(T obj) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean update(T obj) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * Crée automatiquement une requête pour récupérer un élément dans une base + * de données à partir de sa clé primaire + * + * @param obj + * @param like + * @return String requête SQL + */ + public String makeFindSQLConditionQuery(T obj, boolean like) { + final Map pkeyLink = this.pkeySQLFieldLink(); + StringBuilder strBuilder = new StringBuilder(); + List attributes = new ArrayList<>(); + for (Class c = obj.getClass(); c != null; c = c.getSuperclass()) { + attributes.addAll(Arrays.asList(c.getDeclaredFields())); + } + try { + for (Field field : attributes) { + field.setAccessible(true); + Object fieldObject = field.get(obj); + if (pkeyLink.containsKey(field.getName()) && fieldObject != null) { + if (strBuilder.length() > 0) { + strBuilder.append(" AND "); + } + String sqlField = "\"" + pkeyLink.get(field.getName()) + "\""; + if (fieldObject instanceof DateTime) { + if (like) { + final DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); + final String finalDate = fmt.print((DateTime) fieldObject); + strBuilder.append(sqlField).append("::text LIKE '").append(finalDate).append("%'"); + } else { + final DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ssZ"); + final String finalDate = fmt.print((DateTime) fieldObject).substring(0, 22); + strBuilder.append(sqlField).append("='").append(finalDate).append("'"); + } + } else if (like) { + strBuilder.append(sqlField).append(" LIKE '").append(fieldObject).append("%'"); + } else { + strBuilder.append(sqlField).append("='").append(fieldObject).append("'"); + } + } + } + + } catch (SecurityException | IllegalArgumentException | IllegalAccessException ex) { + logger.error(ex.getMessage(), ex); + return null; + } + return strBuilder.toString(); + } + + /** + * Crée automatiquement une requête pour insérer un élément dans une base de + * données à partir de sa clé primaire + * + * @param obj l'objet à inserér + * @return + */ + public String makeCreateSQLConditionQuery(T obj) { + final Map createSQLField = this.relationFieldsJavaSQLObject(); + StringBuilder attributesBuilder = new StringBuilder(); + StringBuilder valuesBuilder = new StringBuilder(); + final Field[] attributes = obj.getClass().getDeclaredFields(); + try { + for (Field field : attributes) { + field.setAccessible(true); + Object fieldObject = field.get(obj); + if (createSQLField.containsKey(field.getName())) { + if (attributesBuilder.length() > 0) { + attributesBuilder.append(", "); + } + String sqlField = createSQLField.get(field.getName()); + attributesBuilder.append(sqlField); + + if (valuesBuilder.length() > 0) { + valuesBuilder.append(", "); + } + if (fieldObject == null) { + valuesBuilder.append("NULL"); + } else if (fieldObject instanceof DateTime) { + final DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ssZ"); + final String finalDate = fmt.print((DateTime) fieldObject).substring(0, 22); + valuesBuilder.append("'").append(finalDate).append("'"); + } else { + valuesBuilder.append("'").append(fieldObject).append("'"); + } + } + } + } catch (SecurityException | IllegalArgumentException | IllegalAccessException ex) { + logger.error(ex.getMessage(), ex); + return null; + } + final String createSQLValuesQuery = "(" + attributesBuilder.toString() + ")" + + " VALUES (" + valuesBuilder.toString() + ")"; + return createSQLValuesQuery; + } + + /** + * Crée automatiquement une requête préparée pour insérer un élément dans + * une base de données à partir de sa clé primaire + * + * @param con + * @param query L'endroit de l'insertion + * @param obj l'objet à inserér + * @return + */ + public PreparedStatement makeCreatePreparedSQLConditionQuery(Connection con, String query, T obj) { + PreparedStatement preparedStatement = null; + final Map createSQLField = this.relationFieldsJavaSQLObject(); + StringBuilder attributesBuilder = new StringBuilder(); + StringBuilder valuesBuilder = new StringBuilder(); + final Field[] attributes = obj.getClass().getDeclaredFields(); + + try { + for (Field field : attributes) { + field.setAccessible(true); + if (createSQLField.containsKey(field.getName())) { + if (attributesBuilder.length() > 0) { + attributesBuilder.append(", "); + } + String sqlField = createSQLField.get(field.getName()); + attributesBuilder.append(sqlField); + + if (valuesBuilder.length() > 0) { + valuesBuilder.append(", "); + } + valuesBuilder.append("?"); + } + } + final String createSQLValuesQuery = query + "(" + attributesBuilder.toString() + ")" + + " VALUES (" + valuesBuilder.toString() + ")"; +// System.err.println(createSQLValuesQuery); + + con = dataSource.getConnection(); + preparedStatement = con.prepareStatement(createSQLValuesQuery); + int fieldCount = 1; + for (Field field : attributes) { + field.setAccessible(true); + Object fieldObject = field.get(obj); + if (createSQLField.containsKey(field.getName())) { + if (fieldObject == null) { + preparedStatement.setObject(fieldCount, null); + } else if (fieldObject instanceof DateTime) { + preparedStatement.setTimestamp(fieldCount, new Timestamp(((DateTime) fieldObject).getMillis())); + } else { + preparedStatement.setObject(fieldCount, fieldObject); + } + fieldCount++; + } + + } + } catch (SecurityException | IllegalArgumentException | IllegalAccessException ex) { + logger.error(ex.getMessage(), ex); + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(SQLDAO.class.getName()).log(Level.SEVERE, null, ex); + } +// logger.debug(preparedStatement.toString()); + return preparedStatement; + } + + public abstract T findByFields(Map Attr, String table); + + public abstract T single(int id); + + public abstract ArrayList all(); + + /** + * Transforme un objet ResulSet en objet défini T + * + * @param result Données retournées par la BD + * @return un objet de type défini T + * @throws SQLException + */ + public abstract T get(ResultSet result) throws SQLException; + + /** + * Retourne les élements retournés par la requête en prenant en compte la + * pagination de l'utilisateur + * + * @return + */ + public abstract ArrayList allPaginate(); + + /** + * Compte le nombre d'élement retournés par la requête + * + * @return Integer + */ + public abstract Integer count(); + + /** + * Compare deux objet du type T + * + * @param fromDB Premier objet à comparer + * @param object Deuxième objet à comparer + * @return un objet avec les informations des deux objets + */ + protected abstract T compareAndMergeObjects(T fromDB, T object); + + /** + * Fonction qui permet de créer la partie commune d'une requête à la fois + * pour lister les éléments et les récupérés + * + * @return SQLQueryBuilder + */ + protected abstract SQLQueryBuilder prepareSearchQuery(); +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/mongo/DocumentDaoMongo.java b/phis2-ws/src/main/java/phis2ws/service/dao/mongo/DocumentDaoMongo.java new file mode 100644 index 000000000..fc6b85de6 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/mongo/DocumentDaoMongo.java @@ -0,0 +1,100 @@ +//********************************************************************************************** +// DocumentDaoMongo.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: June 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: June, 2017 +// Subject: A Dao specific to documents insert into mongodb +//*********************************************************************************************** +package phis2ws.service.dao.mongo; + +import com.mongodb.BasicDBObject; +import com.mongodb.gridfs.GridFSDBFile; +import com.mongodb.gridfs.GridFSInputFile; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.commons.io.IOUtils; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.dao.manager.DAOMongo; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.Document; + +public class DocumentDaoMongo extends DAOMongo { + + @Override + protected BasicDBObject prepareSearchQuery() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList allPaginate() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * + * @param filePath le chemin du document à enregistrer en mongo + * @param fileUri l'uri du fichier, à mettre dans les métadonnées du document dans mongo + * @return POSTResultsReturn + */ + public POSTResultsReturn insertFile(String filePath, String fileUri) { + POSTResultsReturn result = null; + List insertStatusList = new ArrayList<>(); + try { + File file = new File(filePath); + + GridFSInputFile in = gridFS.createFile(file); + in.put("uri", fileUri); + in.save(); + result = new POSTResultsReturn(true, true, true); + insertStatusList.add(new Status("File saved", StatusCodeMsg.INFO, "File saved in mongodb")); + result.statusList = insertStatusList; + file.delete(); + } catch (IOException ex) { + result = new POSTResultsReturn(false, false, false); + insertStatusList.add(new Status("File exception", StatusCodeMsg.ERR, "Error while loading the file")); + result.statusList = insertStatusList; + Logger.getLogger(DocumentDaoMongo.class.getName()).log(Level.SEVERE, null, ex); + } + return result; + } + + /** + * + * @param documentURI l'URI du document à récupérer dans mongo + * @return null si le document n'est pas dans la base, + * le document sinon + */ + public File getDocument(String documentURI) { + GridFSDBFile out = (GridFSDBFile) gridFS.findOne(new BasicDBObject("uri", documentURI)); + InputStream is = out.getInputStream(); + + File file = new File(PropertiesFileManager.getConfigFileProperty("service", "uploadFileServerDirectory") + out.get("filename")); + + try { + OutputStream outputStream = new FileOutputStream(file); + IOUtils.copy(is, outputStream); + } catch (FileNotFoundException ex) { + Logger.getLogger(DocumentDaoMongo.class.getName()).log(Level.SEVERE, null, ex); + return null; + } catch (IOException ex) { + Logger.getLogger(DocumentDaoMongo.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + + return file; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/mongo/PhenotypeDaoMongo.java b/phis2-ws/src/main/java/phis2ws/service/dao/mongo/PhenotypeDaoMongo.java new file mode 100644 index 000000000..8dbab2f9e --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/mongo/PhenotypeDaoMongo.java @@ -0,0 +1,277 @@ +//********************************************************************************************** +// PhenotypeDaoMongo.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: September 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: September, 14 2017 +// Subject: A specific Dao to retrieve data on phenotypes. +//*********************************************************************************************** +package phis2ws.service.dao.mongo; + +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.BasicDBObjectBuilder; +import com.mongodb.client.FindIterable; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoCursor; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.regex.Pattern; +import javax.ws.rs.core.Response; +import org.bson.Document; +import org.eclipse.rdf4j.repository.sparql.query.SPARQLBooleanQuery; +import org.joda.time.DateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.dao.manager.DAOMongo; +import phis2ws.service.dao.phis.AgronomicalObjectDao; +import phis2ws.service.dao.sesame.AgronomicalObjectDaoSesame; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.PhenotypeDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.ResourcesUtils; +import phis2ws.service.utils.sparql.SPARQLQueryBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.AgronomicalObject; +import phis2ws.service.view.model.phis.Data; +import phis2ws.service.view.model.phis.Phenotype; + +public class PhenotypeDaoMongo extends DAOMongo { + + final static Logger LOGGER = LoggerFactory.getLogger(PhenotypeDaoMongo.class); + + private final MongoCollection provenanceCollection = database.getCollection(PropertiesFileManager.getConfigFileProperty("mongodb_nosql_config", "provenance")); + private final MongoCollection dataCollection = database.getCollection(PropertiesFileManager.getConfigFileProperty("mongodb_nosql_config", "data")); + + public String experiment; + public String variable; + public ArrayList agronomicalObjects = new ArrayList<>(); + public String startDate; + public String endDate; + + public PhenotypeDaoMongo() { + super(); + } + + @Override + protected BasicDBObject prepareSearchQuery() { + BasicDBObject query = new BasicDBObject(); + + if (variable != null) { + query.append("variable", variable); + } + + if (startDate != null && endDate != null) { + try { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + Date start = df.parse(startDate); + Date end = df.parse(endDate); + + query.append("date", BasicDBObjectBuilder.start("$gte", start).add("$lte", end).get()); + + } catch (ParseException ex) { + java.util.logging.Logger.getLogger(PhenotypeDaoMongo.class.getName()).log(Level.SEVERE, null, ex); + } + } + + if (agronomicalObjects != null && !agronomicalObjects.isEmpty()) { + if (agronomicalObjects.size() > 1) { + BasicDBList or = new BasicDBList(); + for (String agronomicalObject : agronomicalObjects) { + BasicDBObject clause = new BasicDBObject("agronomicalObject", agronomicalObject); + or.add(clause); + } + query.append("$or", or); + } else { + query.append("agronomicalObject", agronomicalObjects.get(0)); + } + } + + return query; + } + + /** + * @action récupère la liste des objets agronomiques de l'expérimentation pour + * les ajouter à la liste des objets agronomiques recherchés. + * @param experiment + */ + private void updateAgronomicalObjectsWithExperimentsAgronomicalObjects() { + AgronomicalObjectDaoSesame agronomicalObjectDaoSesame = new AgronomicalObjectDaoSesame(); + agronomicalObjectDaoSesame.experiment = experiment; + + ArrayList agronomicalObjects = agronomicalObjectDaoSesame.allPaginate(); + + for (AgronomicalObject agronomicalObject : agronomicalObjects) { + this.agronomicalObjects.add(agronomicalObject.getUri()); + } + } + + /** + * + * @return liste de phénotypes, résultats de la recherche, vide si pas de résultats + */ + @Override + public ArrayList allPaginate() { + //Si on a une expérimentation, il faut récupérer la liste des objets agronomiques + //pour la requête mongo + if (experiment != null) { + updateAgronomicalObjectsWithExperimentsAgronomicalObjects(); + } + + BasicDBObject query = prepareSearchQuery(); + + LOGGER.trace(getTraceabilityLogs() + " query : " + query.toString()); + FindIterable phenotypesMongo = dataCollection.find(query); + + ArrayList phenotypes = new ArrayList<>(); + Phenotype phenotype = new Phenotype(); + phenotype.setExperiment(experiment); + phenotype.setVariableURI(variable); + + //SILEX:todo + //récupérer les valeurs en fonction de la pagination... + //il faut faire cette pagination sur les objets agronomiques.. + //en discuter avec Anne ? + //pour l'instant, j'ai enlevé la pagination + //\SILEX:todo + + try (MongoCursor phenotypesCursor = phenotypesMongo.iterator()) { + while (phenotypesCursor.hasNext()) { + Document phenotypeDocument = phenotypesCursor.next(); + + Data data = new Data(); + data.setAgronomicalObject(phenotypeDocument.getString("agronomicalObject")); + data.setDate(new SimpleDateFormat("yyyy-MM-dd").format(phenotypeDocument.getDate("date"))); // TODO: changer le format de la date pour le retour + data.setValue(phenotypeDocument.getString("value")); + data.setVariable(phenotypeDocument.getString("variable")); + + phenotype.addData(data); + } + } + phenotypes.add(phenotype); + + return phenotypes; + } + + private boolean isElementValid(PhenotypeDTO phenotypeDTO) { + Map phenotypeOk = phenotypeDTO.isOk(); + return (boolean) phenotypeOk.get("state"); + } + + //SILEX:todo + //Faire une fonction "check" commune entre l'insert et le update + //\SILEX:todo + private POSTResultsReturn checkAndInsertPhenotypesList(ArrayList phenotypesDTO) throws Exception { + List insertStatusList = new ArrayList<>(); + POSTResultsReturn result = null; + + ArrayList phenotypes = new ArrayList<>(); + boolean dataState = true; + + + for (PhenotypeDTO phenotypeDTO : phenotypesDTO) { + if (isElementValid(phenotypeDTO)) { + for (Data data : phenotypeDTO.getData()) { + AgronomicalObjectDao agronomicalObjectDao = new AgronomicalObjectDao(); + if (!agronomicalObjectDao.existInDB(new AgronomicalObject(data.getAgronomicalObject()))) { + dataState = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Unknown Agronomical Object URI : " + data.getAgronomicalObject())); + } + } + + Phenotype phenotype = phenotypeDTO.createObjectFromDTO(); + phenotypes.add(phenotype); + + } else { + dataState = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Fields are missing in JSON Data")); + } + } + + if (dataState) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + //SILEX:todo + //FAIRE UN TRUC EQUIVALENT AUX TRANSACTIONS + //On doit faire l'insertion dans mongoDb + for (Phenotype phenotype : phenotypes) { + //1. Insertion de la provenance + Document provenance = new Document(); + Date creationDate = df.parse(phenotype.getProvenance().getCreationDate()); + + provenance.append("creationDate", creationDate); + provenance.append("wasGeneratedBy", phenotype.getProvenance().getWasGeneratedBy().getWasGeneratedBy()); + provenance.append("wasGeneratedByDescription", phenotype.getProvenance().getWasGeneratedBy().getWasGeneratedByDescription()); + provenance.append("documents", phenotype.getProvenance().getDocumentsUris()); + provenance.append("uri", phenotype.getProvenance().getUri()); + + LOGGER.trace("MongoDB insert : " + provenance.toJson()); + + provenanceCollection.insertOne(provenance); + + ArrayList dataToInsert = new ArrayList<>(); + //2. Insertion des data + for (Data data : phenotype.getData()) { + Document d = new Document(); + Date date = df.parse(data.getDate()); + + d.append("date", date); + d.append("variable", phenotype.getVariableURI()); + d.append("value", data.getValue()); + d.append("agronomicalObject", data.getAgronomicalObject()); + //SILEX:todo + //Regarder DBRef (https://docs.mongodb.com/manual/reference/database-references/#dbref-explanation) + d.append("provenance", provenance.get("_id")); + //\SILEX:todo + + LOGGER.trace("MongoDB insert : " + d.toJson()); + + dataToInsert.add(d); + } + + dataCollection.insertMany(dataToInsert); + } + + insertStatusList.add(new Status("Resource created", StatusCodeMsg.INFO, "phenotypes inserted"));; + result = new POSTResultsReturn(dataState); + result.setHttpStatus(Response.Status.CREATED); + result.statusList = insertStatusList; + //\SILEX:todo + } else { + result = new POSTResultsReturn(dataState); + result.setHttpStatus(Response.Status.BAD_REQUEST); + result.statusList = insertStatusList; + } + + return result; + } + + /** + * @action enregistre les données dans MongoDB + * @param phenotypesDTO liste de phénotypes à enregistrer en BD + * @return le résultat de l'insertion + */ + public POSTResultsReturn checkAndInsert(ArrayList phenotypesDTO) { + POSTResultsReturn postResult; + + try { + postResult = this.checkAndInsertPhenotypesList(phenotypesDTO); + } catch (Exception ex) { + LOGGER.error(ex.getMessage(), ex); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, ex.toString()); + } + + return postResult; + } + +//TODO + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/phis/AgronomicalObjectDao.java b/phis2-ws/src/main/java/phis2ws/service/dao/phis/AgronomicalObjectDao.java new file mode 100644 index 000000000..b1ac0907f --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/phis/AgronomicalObjectDao.java @@ -0,0 +1,329 @@ +//********************************************************************************************** +// AgronomicalObjectDao.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: July 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: August 28, 2017 +// Subject: A DAO specific to retrieve agronomical object data +//*********************************************************************************************** +package phis2ws.service.dao.phis; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.dao.manager.DAOPhisBrapi; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.AgronomicalObjectDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sql.SQLQueryBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.AgronomicalObject; + +public class AgronomicalObjectDao extends DAOPhisBrapi { + + final static Logger LOGGER = LoggerFactory.getLogger(AgronomicalObjectDao.class); + + public String uri; + public String typeAgronomicalObject; + public String geometry; + public String namedGraph; + + public AgronomicalObjectDao() { + super(); + setTable("agronomical_object"); + setTableAlias("ao"); + } + + @Override + public POSTResultsReturn checkAndInsert(AgronomicalObjectDTO newObject) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private boolean isElementValid(AgronomicalObjectDTO agronomicalObjectDTO) { + Map agronomicalObjectOk = agronomicalObjectDTO.isOk(); + return (boolean) agronomicalObjectOk.get("state"); + } + + private POSTResultsReturn checkAndInsertAgronomicalObjectsList(List newAgronomicalObjectsDTO, List newAgronomicalObjects) throws Exception { + //init result returned maps + List insertStatusList = new ArrayList<>(); + boolean dataState = true; + boolean resultState = true; + boolean insertionState = true; + POSTResultsReturn results = null; + + for (AgronomicalObjectDTO agronomicalObjectDTO : newAgronomicalObjectsDTO) { + if (!isElementValid(agronomicalObjectDTO)) { + dataState = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Fields are missing in JSON Data")); + } + } + + if (dataState) { + PreparedStatement insertPreparedStatement = null; + + final String insertGab = "INSERT INTO \"agronomical_object\" (\"uri\", \"type\", \"geometry\", \"named_graph\") " + + "VALUES (?, ?, ST_GeomFromText(?, 4326), ?)"; + Connection connection = null; + int inserted = 0; + int exists = 0; + + try { + //batch + boolean insertionLeft = true; + int count = 0; + + //connexion + préparation de la transaction + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + insertPreparedStatement = connection.prepareStatement(insertGab); + + for (AgronomicalObject agronomicalObject : newAgronomicalObjects) { + if (!existInDB(agronomicalObject)) { + insertionLeft = true; + insertPreparedStatement.setString(1, agronomicalObject.getUri()); + insertPreparedStatement.setString(2, agronomicalObject.getTypeAgronomicalObject()); + insertPreparedStatement.setString(3, agronomicalObject.getGeometry()); + insertPreparedStatement.setString(4, agronomicalObject.getUriExperiment()); + + LOGGER.trace(getTraceabilityLogs() + " quert : " + insertPreparedStatement.toString()); + + insertPreparedStatement.execute(); + + inserted++; + } else { + exists++; + } + + //Insertion par batch + if (++count % batchSize == 0) { + insertPreparedStatement.executeBatch(); + insertionLeft = false; + } + } + + if (insertionLeft) { + insertPreparedStatement.executeBatch(); + } + + connection.commit(); +//////////////////// +//ATTENTION, vérifications à re regarder et re vérifier +////////////////// + //Si data insérées et existantes + if (exists > 0 && inserted > 0) { + results = new POSTResultsReturn(resultState, insertionState, dataState); + insertStatusList.add(new Status("Already existing data", StatusCodeMsg.INFO, "All agronomical objects already exist")); + results.setHttpStatus(Response.Status.OK); + results.statusList = insertStatusList; + } else { + if (exists > 0) { //Si données existantes et aucunes insérées + insertStatusList.add(new Status ("Already existing data", StatusCodeMsg.INFO, String.valueOf(exists) + " agronomical objects already exists")); + } else { //Si données qui n'existent pas et donc sont insérées + insertStatusList.add(new Status("Data inserted", StatusCodeMsg.INFO, String.valueOf(inserted) + " agronomical objects inserted")); + } + } + results = new POSTResultsReturn(resultState, insertionState, dataState); + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + + //Rollback + if (connection != null) { + connection.rollback(); + } + + results = new POSTResultsReturn(false, insertionState, dataState); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getMessage())); + if (e.getNextException() != null) { + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getNextException().getMessage())); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERR, "Duplicated project in json or in database")); + } + results.statusList = insertStatusList; + } finally { + if (insertPreparedStatement != null) { + insertPreparedStatement.close(); + } + if (connection != null) { + connection.close(); + } + } + } else { + results = new POSTResultsReturn(resultState, insertionState, dataState); + results.statusList = insertStatusList; + } + + return results; + } + + public POSTResultsReturn checkAndInsertListAO(List newObjects, List newObjectsDTO) { + POSTResultsReturn postResult; + try { + postResult = this.checkAndInsertAgronomicalObjectsList(newObjectsDTO, newObjects); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, e.toString()); + } + + return postResult; + } + + @Override + public POSTResultsReturn checkAndUpdateList(List newObjects) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Map pkeySQLFieldLink() { + Map pkeySQLFieldLink = new HashMap<>(); + pkeySQLFieldLink.put("uri", "uri"); + return pkeySQLFieldLink; + } + + @Override + public Map relationFieldsJavaSQLObject() { + Map createSQLFields = new HashMap<>(); + createSQLFields.put("uri", "uri"); + createSQLFields.put("typeAgronomicalObject", "type"); + createSQLFields.put("geometry", "geometry"); + createSQLFields.put("namedGraph", "named_graph"); + + return createSQLFields; + } + + @Override + public AgronomicalObject findByFields(Map Attr, String table) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public AgronomicalObject single(int id) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList all() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public AgronomicalObject get(ResultSet result) throws SQLException { + AgronomicalObject agronomicalObject = new AgronomicalObject(); + agronomicalObject.setUri(result.getString("uri")); + agronomicalObject.setGeometry(result.getString("geometry")); + agronomicalObject.setTypeAgronomicalObject(result.getString("type")); + agronomicalObject.setUriExperiment(result.getString("named_graph")); + + return agronomicalObject; + } + + @Override + public ArrayList allPaginate() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Integer count() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected AgronomicalObject compareAndMergeObjects(AgronomicalObject fromDB, AgronomicalObject object) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected SQLQueryBuilder prepareSearchQuery() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * + * @param agronomicalObjectsURIs la liste des uris pour lesquelles on veut la géométrie + * @return la géométrie associée à chaque uri, dans la BD, en geojson + * ex : {"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]} + */ + public HashMap getGeometries(ArrayList agronomicalObjectsURIs) throws SQLException { + Connection connection = null; + Statement statement = null; + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendSelect("ST_AsGeoJSON(ST_Transform(geometry, 4326)), ao.uri"); + query.appendFrom(table, tableAlias); + + for (String agronomicalObjectURI : agronomicalObjectsURIs) { + query.appendORWhereConditionIfNeeded("uri", agronomicalObjectURI, "=", null, tableAlias); + } + + LOGGER.trace(getTraceabilityLogs() + " quert : " + query.toString()); + + ResultSet queryResult = statement.executeQuery(query.toString()); + HashMap geometries = new HashMap<>(); + + while (queryResult.next()) { + geometries.put(queryResult.getString("uri"), queryResult.getString("st_asgeojson")); + } + + return geometries; + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(AgronomicalObjectDao.class.getName()).log(Level.SEVERE, null, ex); + return null; + } finally { + if (statement != null) { + statement.close(); + } + if (connection != null) { + connection.close(); + } + } + } + + /** + * + * @param year + * @return String correspondant au nombre d'agronomical objects actuellement + * enregistrés sur l'année year + */ + public String getNumberOfAgronomicalObjectForYear(String year) { + try { + String toReturn; + try (Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT)) { + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendSelect("count(*)"); + query.appendFrom(table, tableAlias); + query.appendANDWhereConditionIfNeeded("uri", "/" + year + "/", "~*", null, tableAlias); + LOGGER.trace(getTraceabilityLogs() + " quert : " + query.toString()); + ResultSet queryResult = statement.executeQuery(query.toString()); + queryResult.next(); + toReturn = queryResult.getString("count"); + } + + return toReturn; + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(AgronomicalObjectDao.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + } + + @Override + public POSTResultsReturn checkAndInsertList(List newObjects) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/phis/DocumentDao.java b/phis2-ws/src/main/java/phis2ws/service/dao/phis/DocumentDao.java new file mode 100644 index 000000000..d0ba2c042 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/phis/DocumentDao.java @@ -0,0 +1,113 @@ +//********************************************************************************************** +// DocumentDao.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@inra.fr, morgane.vidal@inra.fr anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: A Dao specific to document insertion into PHIS DB +//*********************************************************************************************** +package phis2ws.service.dao.phis; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.dao.manager.DAOPhisBrapi; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sql.SQLQueryBuilder; +import phis2ws.service.view.model.phis.Document; + +public class DocumentDao extends DAOPhisBrapi { + final static Logger logger = LoggerFactory.getLogger(DocumentDao.class); + + public DocumentDao() { + super(); + setTable("document"); + } + + @Override + public Integer count() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Map pkeySQLFieldLink() { + Map pkeySQLFieldLink = new HashMap<>(); + pkeySQLFieldLink.put("id", "id"); + pkeySQLFieldLink.put("name", "name"); + pkeySQLFieldLink.put("uri", "uri"); + return pkeySQLFieldLink; + } + + @Override + public Map relationFieldsJavaSQLObject() { + Map createSQLFields = new HashMap<>(); + createSQLFields.put("name", "name"); + createSQLFields.put("type", "type"); + createSQLFields.put("date", "date"); + createSQLFields.put("path", "path"); + createSQLFields.put("targetTable", "target_table"); + createSQLFields.put("targetPk", "target_pk"); + createSQLFields.put("version", "version"); + createSQLFields.put("uri", "uri"); + + return createSQLFields; + } + + @Override + public Document findByFields(Map Attr, String table) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Document single(int id) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList all() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Document get(ResultSet result) throws SQLException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList allPaginate() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected SQLQueryBuilder prepareSearchQuery() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public POSTResultsReturn checkAndInsert(Object newObject) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public POSTResultsReturn checkAndInsertList(List newObjects) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected Document compareAndMergeObjects(Document fromDB, Document object) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public POSTResultsReturn checkAndUpdateList(List newObjects) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/phis/ExperimentDao.java b/phis2-ws/src/main/java/phis2ws/service/dao/phis/ExperimentDao.java new file mode 100644 index 000000000..20fc8387e --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/phis/ExperimentDao.java @@ -0,0 +1,859 @@ +//********************************************************************************************** +// ExperimentDao.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: January 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 31 2017 : Passage de trial à experiment +// Subject: A DAO specific to retrieve experiment data +//*********************************************************************************************** + +package phis2ws.service.dao.phis; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.dao.manager.DAOPhisBrapi; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.model.User; +import phis2ws.service.resources.dto.ExperimentDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sql.JoinAttributes; +import phis2ws.service.utils.sql.SQLQueryBuilder; +import phis2ws.service.view.model.phis.Contact; +import phis2ws.service.view.model.phis.Group; +import phis2ws.service.view.model.phis.Project; +import phis2ws.service.view.model.phis.Experiment; + + +public class ExperimentDao extends DAOPhisBrapi { + + final static Logger LOGGER = LoggerFactory.getLogger(ExperimentDao.class); + + public String uri; + public String startDate; + public String endDate; + public String field; + public String campaign; + public String place; + public String alias; + public String keyword; + public String groups; + public String cropSpecies; + + public ExperimentDao() { + super(); + setTable("trial"); + setTableAlias("t"); + } + + public ExperimentDao(String uri) { + super(); + this.uri = uri; + setTable("trial"); + setTableAlias("t"); + } + + @Override + public Map pkeySQLFieldLink() { + Map pkeySQLFieldLink = new HashMap<>(); + pkeySQLFieldLink.put("uri", "uri"); + return pkeySQLFieldLink; + } + + @Override + public Map relationFieldsJavaSQLObject() { + Map createSQLFields = new HashMap<>(); + createSQLFields.put("uri","uri"); + createSQLFields.put("startDate", "start_date"); + createSQLFields.put("endDate", "end_date"); + createSQLFields.put("field", "field"); + createSQLFields.put("campaign", "campaign"); + createSQLFields.put("place", "place"); + createSQLFields.put("alias", "alias"); + createSQLFields.put("comment", "comment"); + createSQLFields.put("keywords", "keywords"); + createSQLFields.put("objective", "objective"); + createSQLFields.put("groups", "groups"); + createSQLFields.put("cropSpecies", "crop_species"); + + return createSQLFields; + } + + @Override + public Experiment findByFields(Map Attr, String table) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Experiment single(int id) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList all() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Experiment get(ResultSet result) throws SQLException { + Experiment experiment = new Experiment(); + experiment.setUri(result.getString("uri")); + experiment.setStartDate(result.getString("start_date")); + experiment.setEndDate(result.getString("end_date")); + experiment.setField(result.getString("field")); + experiment.setCampaign(result.getString("campaign")); + experiment.setPlace(result.getString("place")); + experiment.setAlias(result.getString("alias")); + experiment.setComment(result.getString("comment")); + experiment.setKeywords(result.getString("keywords")); + experiment.setObjective(result.getString("objective")); + experiment.setCropSpecies(result.getString("crop_species")); + + return experiment; + } + + @Override + public ArrayList allPaginate() { + ResultSet queryResult = null; + Connection connection = null; + Statement statement = null; + ArrayList experiments = new ArrayList(); + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + SQLQueryBuilder query = new SQLQueryBuilder(); + + Map sqlFields = relationFieldsJavaSQLObject(); + + //Ajout des conditions dans la requête + query.appendFrom("trial", tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("uri"), uri, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("startDate"), startDate, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("endDate"), endDate, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("field"), field, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("campaign"), campaign, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("place"), place, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("alias"), alias, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("keyword"), keyword, "ILIKE", null, tableAlias); + query.appendLimit(String.valueOf(pageSize)); + + queryResult = statement.executeQuery(query.toString()); + + UserDaoPhisBrapi userDao = new UserDaoPhisBrapi(); + userDao.isAdmin(user); + boolean isAdmin = (user.getAdmin().equals("t") || user.getAdmin().equals("true")); + + while (queryResult.next()) { + //SILEX:access + Experiment experiment = get(queryResult); + if (isAdmin || canUserSeeExperiment(user, experiment)) { + experiments.add(get(queryResult)); + } + //\SILEX:access + } + + //SILEX:dbjoin + experiments = getExperimentsContacts(experiments, statement); + //\SILEX:dbjoin + experiments = getExperimentsProjects(experiments, statement); + for (Experiment experiment : experiments) { + experiment.setGroupList(this.getExperimentGroups(experiment)); + } + + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(ExperimentDao.class.getName()).log(Level.SEVERE, null, ex); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + } + if (statement != null) { + statement.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(ExperimentDao.class.getName()).log(Level.SEVERE, null, ex); + } + } + return experiments; + } + + /** + * + * @param experiments ArrayList liste des expérimentations pour lesquels on veut aussi avoir la liste des projets + * @param statement Statement + * @return la liste envoyée en paramètre contenant en plus pour chaque expérimentation la liste de ses projets + * @throws SQLException + */ + private ArrayList getExperimentsProjects(ArrayList experiments, Statement statement) throws SQLException { + for (Experiment experiment : experiments) { + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendSelect("p.acronyme, p.uri"); + query.appendFrom("at_trial_project", "tp"); + query.appendANDWhereConditionIfNeeded("trial_uri", experiment.getUri(), "=", null, "tp"); + query.appendJoin(JoinAttributes.INNERJOIN, "project", "p", "p.uri = tp.project_uri"); + + ResultSet queryResult = statement.executeQuery(query.toString()); + while (queryResult.next()) { + Project project = new Project(queryResult.getString("uri")); + project.setAcronyme(queryResult.getString("acronyme")); + experiment.addProject(project); + } + } + return experiments; + } + + /** + * @param statement Statement + * @param experiments ArrayList liste des expérimentations pour lesquels on veut aussi avoir la liste des contacts + * @return la liste envoyée en paramètre contenant en plus pour chaque expérimentation la liste de ses contacts + */ + private ArrayList getExperimentsContacts(ArrayList experiments, Statement statement) throws SQLException { + for (Experiment experiment : experiments) { + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendSelect("u.email, u.first_name, u.family_name, tu.type"); + query.appendFrom("at_trial_users", "tu"); + query.appendANDWhereConditionIfNeeded("trial_uri", experiment.getUri(), "=", null, "tu"); + query.appendJoin(JoinAttributes.INNERJOIN, "users", "u", "u.email = tu.users_email"); + + ResultSet queryResult = statement.executeQuery(query.toString()); + while (queryResult.next()) { + Contact contact = new Contact(); + contact.setEmail(queryResult.getString("email")); + contact.setFirstName(queryResult.getString("first_name")); + contact.setFamilyName(queryResult.getString("family_name")); + contact.setType(queryResult.getString("type")); + experiment.addContact(contact); + } + } + + return experiments; + } + + @Override + public Integer count() { + + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendCount(); + query.appendDistinct(); + query.appendSelect(tableAlias + ".uri"); + query.appendFrom(table, tableAlias); + + if (uri != null) { + query.appendWhereConditions("uri", uri, "=", null, tableAlias); + } + + Connection connection = null; + ResultSet resultSet = null; + Statement statement = null; + + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(); + resultSet = statement.executeQuery(query.toString()); + + if(resultSet.next()) { + return resultSet.getInt(1); + }else{ + return 0; + } + + + } catch(SQLException e) { + LOGGER.error(e.getMessage(), e); + return null; + } finally { + try { + if (statement != null) { + statement.close(); + } + if (resultSet != null) { + resultSet.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + } + + @Override + protected Experiment compareAndMergeObjects(Experiment fromDB, Experiment object) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected SQLQueryBuilder prepareSearchQuery() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private boolean isElementValid(ExperimentDTO experimentDTO) { + Map experimentOk = experimentDTO.isOk(); + return (boolean) experimentOk.get("state"); + } + + private POSTResultsReturn checkAndInsertExperimentList(List newExperiments) throws SQLException, Exception { + //init results returned maps + List insertStatusList = new ArrayList<>(); + boolean dataState = true; + boolean resultState = true; + boolean insertionState = true; + POSTResultsReturn results = null; + ArrayList experiments = new ArrayList<>(); + + for (ExperimentDTO experimentDTO : newExperiments) { + if (isElementValid(experimentDTO)) { + Experiment experiment = experimentDTO.createObjectFromDTO(); + experiments.add(experiment); + } else { + dataState = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Fields are missing in JSON Data")); + } + } + + if (dataState) { + PreparedStatement insertPreparedStatementExperiments = null; + PreparedStatement insertPreparedStatementAtExperimentProject = null; + PreparedStatement insertPreparedStatementAtGroupExperiment = null; + PreparedStatement insertPreparedStatementAtExperimentUsers = null; + + final String insertGabExperiment = "INSERT INTO \"trial\"" + + "(\"uri\", \"start_date\", \"end_date\", \"field\"," + + "\"campaign\", \"place\", \"alias\", \"comment\", \"keywords\"," + + " \"objective\", \"crop_species\")" + + " VALUES (?, to_date(?, 'YYYY-MM-DD'), to_date(?, 'YYYY-MM-DD'), ?, ?, ?, ?, ?, ?, ?, ?)"; + + final String insertGabAtExperimentProject = "INSERT INTO \"at_trial_project\"" + + "(\"project_uri\", \"trial_uri\")" + + " VALUES (?, ?)"; + + final String insertGabAtExperimentGroup = "INSERT INTO \"at_group_trial\"" + + "(\"group_uri\", \"trial_uri\")" + + " VALUES (?, ?)"; + final String insertGabAtExperimentUsers = "INSERT INTO \"at_trial_users\"" + + "(\"trial_uri\", \"users_email\", \"type\")" + + " VALUES (?, ?, ?)"; + + Connection con = null; + int inserted = 0; + int exists = 0; + + try { + //batch + boolean insertionLeft = true; + int count = 0; + + //connexion + préparation de la transaction + con = dataSource.getConnection(); + con.setAutoCommit(false); + + insertPreparedStatementExperiments = con.prepareStatement(insertGabExperiment); + insertPreparedStatementAtExperimentProject = con.prepareStatement(insertGabAtExperimentProject); + insertPreparedStatementAtGroupExperiment = con.prepareStatement(insertGabAtExperimentGroup); + insertPreparedStatementAtExperimentUsers = con.prepareStatement(insertGabAtExperimentUsers); + + for (Experiment experiment : experiments) { + if (!existInDB(experiment)) { + insertionLeft = true; + insertPreparedStatementExperiments.setString(1, experiment.getUri()); + insertPreparedStatementExperiments.setString(2, experiment.getStartDate()); + insertPreparedStatementExperiments.setString(3, experiment.getEndDate()); + insertPreparedStatementExperiments.setString(4, experiment.getField()); + insertPreparedStatementExperiments.setString(5, experiment.getCampaign()); + insertPreparedStatementExperiments.setString(6, experiment.getPlace()); + insertPreparedStatementExperiments.setString(7, experiment.getAlias()); + insertPreparedStatementExperiments.setString(8, experiment.getComment()); + insertPreparedStatementExperiments.setString(9, experiment.getKeywords()); + insertPreparedStatementExperiments.setString(10, experiment.getObjective()); + insertPreparedStatementExperiments.setString(11, experiment.getCropSpecies()); + + //ajout dans les logs de qui a fait quoi (traçabilité) + String log = ""; + if (remoteUserAdress != null) { + log += "IP Address " + remoteUserAdress + " - "; + } + if (user != null) { + log += "User : " + user.getEmail() + " - "; + } + + insertPreparedStatementExperiments.execute(); + + //Ajout dans at_trial_project des projets auxquels l'essai appartient + for (Project project : experiment.getProjects()) { + insertPreparedStatementAtExperimentProject.setString(1, project.getUri()); + insertPreparedStatementAtExperimentProject.setString(2, experiment.getUri()); + LOGGER.trace(log + " quert : " + insertPreparedStatementAtExperimentProject.toString()); + insertPreparedStatementAtExperimentProject.execute(); + } + + //Ajout dans at_group_trial des groupes auxquels l'essai appartient + for (Group group : experiment.getGroups()) { + insertPreparedStatementAtGroupExperiment.setString(1, group.getUri()); + insertPreparedStatementAtGroupExperiment.setString(2, experiment.getUri()); + LOGGER.trace(log + " quert : " + insertPreparedStatementAtExperimentProject.toString()); + insertPreparedStatementAtGroupExperiment.execute(); + } + + //Ajout dans at_trial_users des contacts + for (Contact contact : experiment.getContacts()) { + insertPreparedStatementAtExperimentUsers.setString(1, experiment.getUri()); + insertPreparedStatementAtExperimentUsers.setString(2, contact.getEmail()); + insertPreparedStatementAtExperimentUsers.setString(3, contact.getType()); + insertPreparedStatementAtExperimentUsers.execute(); + LOGGER.trace(log + " quert : " + insertPreparedStatementAtExperimentUsers.toString()); + } + + inserted++; + } else { + exists++; + } + + //Insertion par batch + if (++count % batchSize == 0) { + insertPreparedStatementExperiments.executeBatch(); + insertPreparedStatementAtExperimentProject.executeBatch(); + insertPreparedStatementAtGroupExperiment.executeBatch(); + insertPreparedStatementAtExperimentUsers.executeBatch(); + insertionLeft = false; + } + } + + if (insertionLeft) { + insertPreparedStatementExperiments.executeBatch(); //checkAndInsert remaining records + insertPreparedStatementAtExperimentProject.executeBatch(); + insertPreparedStatementAtGroupExperiment.executeBatch(); + insertPreparedStatementAtExperimentUsers.executeBatch(); + } + + con.commit(); //Envoi des données ds bd + +//////////////////// + //ATTENTION, vérifications à re regarder et re vérifier +////////////////// + //Si data insérées et existantes + if (exists > 0 && inserted > 0) { + results = new POSTResultsReturn(resultState, insertionState, dataState); + insertStatusList.add(new Status("Already existing data", StatusCodeMsg.INFO, "All experiments already exist")); + results.setHttpStatus(Response.Status.OK); + results.statusList = insertStatusList; + } else { + if (exists > 0) { //Si données existantes et aucunes insérées + insertStatusList.add(new Status ("Already existing data", StatusCodeMsg.INFO, String.valueOf(exists) + " experiment already exists")); + } else { //Si données qui n'existent pas et donc sont insérées + insertStatusList.add(new Status("Data inserted", StatusCodeMsg.INFO, String.valueOf(inserted) + " experiments inserted")); + } + } + results = new POSTResultsReturn(resultState, insertionState, dataState); + + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + + //Rollback + if (con != null) { + con.rollback(); + } + + results = new POSTResultsReturn(false, insertionState, dataState); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getMessage())); + if (e.getNextException() != null) { + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getNextException().getMessage())); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERR, "Duplicated experiment in json or in database")); + } + results.statusList = insertStatusList; + } finally { + if (insertPreparedStatementExperiments != null) { + insertPreparedStatementExperiments.close(); + } + if (insertPreparedStatementAtGroupExperiment != null) { + insertPreparedStatementAtGroupExperiment.close(); + } + if (insertPreparedStatementAtExperimentProject != null) { + insertPreparedStatementAtExperimentProject.close(); + } + if (insertPreparedStatementAtExperimentUsers != null) { + insertPreparedStatementAtExperimentUsers.close(); + } + if (con != null) { + con.close(); + } + } + } else { + results = new POSTResultsReturn(resultState, insertionState, dataState); + results.statusList = insertStatusList; + } + + return results; + } + + /** + * Vérifie tous les experimentations et les insère dans la BD Postgres + * @param newObjects liste des expérimentations + * @return + */ + @Override + public POSTResultsReturn checkAndInsertList(List newObjects) { + + POSTResultsReturn postResult; + try { + postResult = this.checkAndInsertExperimentList(newObjects); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, e.toString()); + } + + return postResult; + } + + + @Override + public POSTResultsReturn checkAndInsert(ExperimentDTO newObject) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * + * @param experiment Experiment + * @return la liste des groupes ayant accès à l'expérimentation (lite des noms de groupes) + * @throws java.sql.SQLException + */ + public ArrayList getExperimentGroups(Experiment experiment) throws SQLException { + ResultSet result = null; + Connection connection = null; + Statement statement = null; + ArrayList experimentGroups = new ArrayList<>(); + + try { + if (this.existInDB(experiment)) { + //Récupération des groupes dans les tables at_group_trial et group + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendSelect("gp.uri, gp.level, gp.name"); + query.appendFrom("at_group_trial", "gt"); + query.appendANDWhereConditionIfNeeded("trial_uri", experiment.getUri(), "=", null, "gt"); + query.appendJoin(JoinAttributes.INNERJOIN, "group", "gp", "gt.group_uri = gp.uri"); + + connection = dataSource.getConnection(); + statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY,ResultSet.HOLD_CURSORS_OVER_COMMIT); + + result = statement.executeQuery(query.toString()); + while (result.next()) { + Group group = new Group(result.getString("uri")); + group.setLevel(result.getString("level")); + group.setName(result.getString("name")); + experimentGroups.add(group); + } + } + } catch (Exception ex) { + java.util.logging.Logger.getLogger(ExperimentDao.class.getName()).log(Level.SEVERE, null, ex); + } finally { + if (connection != null) { + connection.close(); + } + if (statement != null) { + statement.close(); + } + if (result != null) { + result.close(); + } + } + + return experimentGroups; + } + + /** + * + * @param u User + * @param experiment Experiment + * @return true si l'utilisateur fait partie d'un des groupes ayant accès à l'experimentation, false sinon + */ + public boolean canUserSeeExperiment(User u, Experiment experiment) { + try { + UserDaoPhisBrapi userDao = new UserDaoPhisBrapi(); + + ArrayList experimentGroups = this.getExperimentGroups(experiment); + //Quand l'essai n'est dans aucun groupe, il est public donc l'utilisateur peut le consulter + if (experimentGroups.isEmpty()) { + return true; + } else { + ArrayList userGroups = userDao.getUserGroups(u); + for (Group userGroup : userGroups) { + for (Group experimentGroup : experimentGroups) { + if (userGroup.getUri().equals(experimentGroup.getUri())) { + return true; + } + } + } + } + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(ExperimentDao.class.getName()).log(Level.SEVERE, null, ex); + } + + return false; + } + + /** + * + * @param experiment + * @return boolean true si l'utilisateur peut modifier l'essai + * (administrateur ou membre d'un groupe propriétaire de la donnée) + * false sinon + * @throws SQLException + */ + private boolean canUserUpdateExperiment(Experiment experiment) throws SQLException { + UserDaoPhisBrapi userDao = new UserDaoPhisBrapi(); + + if (userDao.isAdmin(user)) { + return true; + } + + ArrayList userGroups = userDao.getUserGroups(user); + ArrayList experimentGroups = this.getExperimentGroups(experiment); + + if (!experimentGroups.isEmpty() || !userGroups.isEmpty()) { + for (Group userGroup : userGroups) { + for (Group experimentGroup : experimentGroups) { + if ( userGroup.getLevel().equals("Owner") + && userGroup.getUri().equals(experimentGroup.getUri())) { + + return true; + } + } + } + } + return false; + } + + /** + * + * @param updateExperiments List liste des expérimentations à modifier + * @return POSTResultReturn, le resultat des updates + */ + private POSTResultsReturn checkAndUpdateExperimentList(List updateExperiments) throws SQLException, Exception { + //init result returned maps + List insertStatusList = new ArrayList<>(); + boolean allExperimentsAlreadyInDB = true; + POSTResultsReturn results = null; + + ArrayList experiments = new ArrayList<>(); + + String log = getTraceabilityLogs(); + + //1. On récupère la liste des expérimentations et on vérifie qu'ils existent bien en BD + // et que l'utilisateur a bien le droit de les modifier + if (updateExperiments != null && !updateExperiments.isEmpty()) { + for (ExperimentDTO experimentDTO : updateExperiments) { + Experiment experiment = experimentDTO.createObjectFromDTO(); + if (existInDB(experiment) && canUserUpdateExperiment(experiment)) { + experiments.add(experiment); + } else { + allExperimentsAlreadyInDB = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Unknown experiment")); + } + } + } + + //2. Modification de la BD en fonction des données envoyées + if (allExperimentsAlreadyInDB) { + PreparedStatement updatePreparedStatementExperiment = null; + PreparedStatement deletePreparedStatementProject = null; + PreparedStatement insertPreparedStatementProject = null; + PreparedStatement deletePreparedStatementGroup = null; + PreparedStatement insertPreparedStatementGroup = null; + PreparedStatement deletePreparedStatementContact = null; + PreparedStatement insertPreparedStatementContact = null; + Connection connection = null; + + final String updateExperiment = "UPDATE \"trial\" SET \"start_date\" = to_date(?, 'YYYY:MM:DD'), \"end_date\" = to_date(?, 'YYYY:MM:DD')," + + " \"field\" = ?," + + "\"place\" = ?, \"alias\" = ?, \"comment\" = ?, \"keywords\" = ?, \"objective\" = ?," + + "\"crop_species\" = ? " + + "WHERE \"uri\" = ?"; + final String deleteProject = "DELETE FROM \"at_trial_project\" WHERE \"trial_uri\" = ?"; + final String insertProject = "INSERT INTO \"at_trial_project\" (\"trial_uri\", \"project_uri\") VALUES (?, ?)"; + final String deleteGroup = "DELETE FROM \"at_group_trial\" WHERE \"trial_uri\" = ?"; + final String insertGroup = "INSERT INTO \"at_group_trial\" (\"trial_uri\", \"group_uri\") VALUES (?, ?)"; + final String deleteContact = "DELETE FROM \"at_trial_users\" WHERE \"trial_uri\" = ?"; + final String insertContact = "INSERT INTO \"at_trial_users\" (\"trial_uri\", \"users_email\", \"type\") VALUES (?, ?, ?)"; + + try { + //Batch + int count = 0; + boolean insertionLeft = true; + + //Connection + préparation de la transaction + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + updatePreparedStatementExperiment = connection.prepareStatement(updateExperiment); + deletePreparedStatementProject = connection.prepareStatement(deleteProject); + insertPreparedStatementProject = connection.prepareStatement(insertProject); + deletePreparedStatementGroup = connection.prepareStatement(deleteGroup); + insertPreparedStatementGroup = connection.prepareStatement(insertGroup); + deletePreparedStatementContact = connection.prepareStatement(deleteContact); + insertPreparedStatementContact = connection.prepareStatement(insertContact); + + for (Experiment experiment : experiments) { + //Update des données de la table trial + updatePreparedStatementExperiment.setString(1, experiment.getStartDate()); + updatePreparedStatementExperiment.setString(2, experiment.getEndDate()); + updatePreparedStatementExperiment.setString(3, experiment.getField()); + updatePreparedStatementExperiment.setString(4, experiment.getPlace()); + updatePreparedStatementExperiment.setString(5, experiment.getAlias()); + updatePreparedStatementExperiment.setString(6, experiment.getComment()); + updatePreparedStatementExperiment.setString(7, experiment.getKeywords()); + updatePreparedStatementExperiment.setString(8, experiment.getObjective()); + updatePreparedStatementExperiment.setString(9, experiment.getCropSpecies()); + updatePreparedStatementExperiment.setString(10, experiment.getUri()); + LOGGER.trace(log + " quert : " + updatePreparedStatementExperiment.toString()); + updatePreparedStatementExperiment.execute(); + + + //Delete des Projets + deletePreparedStatementProject.setString(1, experiment.getUri()); + deletePreparedStatementProject.execute(); + LOGGER.trace(log + " quert : " + deletePreparedStatementProject.toString()); + + //Insert des projets + if (experiment.getProjects() != null && !experiment.getProjects().isEmpty()) { + for (Project project : experiment.getProjects()) { + insertPreparedStatementProject.setString(1, experiment.getUri()); + insertPreparedStatementProject.setString(2, project.getUri()); + insertPreparedStatementProject.execute(); + LOGGER.trace(log + " quert : " + insertPreparedStatementProject); + } + } + + //Delete des groups + deletePreparedStatementGroup.setString(1, experiment.getUri()); + deletePreparedStatementGroup.execute(); + LOGGER.trace(log + " quert : " + deletePreparedStatementGroup.toString()); + + //Insert des groupes + if (experiment.getGroups() != null && !experiment.getGroups().isEmpty()) { + for (Group group : experiment.getGroups()) { + insertPreparedStatementGroup.setString(1, experiment.getUri()); + insertPreparedStatementGroup.setString(2, group.getUri()); + insertPreparedStatementGroup.execute(); + LOGGER.trace(log + " quert : " + insertPreparedStatementGroup.toString()); + } + } + + //Delete des contacts + deletePreparedStatementContact.setString(1, experiment.getUri()); + deletePreparedStatementContact.execute(); + LOGGER.trace(log + " quert : " + deletePreparedStatementContact.toString()); + + //Insert des contacts + if (experiment.getContacts() != null && !experiment.getContacts().isEmpty()) { + for (Contact contact : experiment.getContacts()) { + insertPreparedStatementContact.setString(1, experiment.getUri()); + insertPreparedStatementContact.setString(2, contact.getEmail()); + insertPreparedStatementContact.setString(3, contact.getType()); + insertPreparedStatementContact.execute(); + LOGGER.trace(log + " quert : " + insertPreparedStatementContact.toString()); + } + } + + //Insertion par batch + if (++count % batchSize == 0) { + updatePreparedStatementExperiment.executeBatch(); + deletePreparedStatementProject.executeBatch(); + insertPreparedStatementProject.executeBatch(); + deletePreparedStatementGroup.executeBatch(); + insertPreparedStatementGroup.executeBatch(); + deletePreparedStatementContact.executeBatch(); + insertPreparedStatementContact.executeBatch(); + insertionLeft = false; + } + } + if (insertionLeft) { + updatePreparedStatementExperiment.executeBatch(); + deletePreparedStatementProject.executeBatch(); + insertPreparedStatementProject.executeBatch(); + deletePreparedStatementGroup.executeBatch(); + insertPreparedStatementGroup.executeBatch(); + deletePreparedStatementContact.executeBatch(); + insertPreparedStatementContact.executeBatch(); + } + + connection.commit(); //Envoi des données à la BD + + insertStatusList.add(new Status("Data updated", StatusCodeMsg.INFO, "experiments updated")); + results = new POSTResultsReturn(true, true, allExperimentsAlreadyInDB); + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + + //Rollback + if (connection != null) { + connection.rollback(); + } + + results = new POSTResultsReturn(false, true, allExperimentsAlreadyInDB); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getMessage())); + if (e.getNextException() != null) { + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getNextException().getMessage())); + } + results.statusList = insertStatusList; + } finally { + if (updatePreparedStatementExperiment != null) { + updatePreparedStatementExperiment.close(); + } + if (deletePreparedStatementProject != null) { + deletePreparedStatementProject.close(); + } + if (insertPreparedStatementProject != null) { + insertPreparedStatementProject.close(); + } + if (deletePreparedStatementGroup != null) { + deletePreparedStatementGroup.close(); + } + if (insertPreparedStatementGroup != null) { + insertPreparedStatementGroup.close(); + } + if (deletePreparedStatementContact != null) { + deletePreparedStatementContact.close(); + } + if (insertPreparedStatementContact != null) { + insertPreparedStatementContact.close(); + } + if (connection != null) { + connection.close(); + } + } + } else { + results = new POSTResultsReturn(true, true, allExperimentsAlreadyInDB); + results.statusList = insertStatusList; + } + return results; + } + + @Override + public POSTResultsReturn checkAndUpdateList(List newObjects) { + POSTResultsReturn postResult; + try { + postResult = this.checkAndUpdateExperimentList(newObjects); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, e.toString()); + } + return postResult; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/phis/GroupDao.java b/phis2-ws/src/main/java/phis2ws/service/dao/phis/GroupDao.java new file mode 100644 index 000000000..862dfcd47 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/phis/GroupDao.java @@ -0,0 +1,564 @@ +//********************************************************************************************** +// GroupDao.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: April, 2017 +// Subject: A Dao specific to retrieve group data +//*********************************************************************************************** +package phis2ws.service.dao.phis; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.dao.manager.DAOPhisBrapi; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.model.User; +import phis2ws.service.resources.dto.GroupDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sql.JoinAttributes; +import phis2ws.service.utils.sql.SQLQueryBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.Group; + +public class GroupDao extends DAOPhisBrapi { + + final static Logger LOGGER = LoggerFactory.getLogger(GroupDao.class); + + public String uri; + public String name; + public String level; + public String description; + + public GroupDao() { + super(); + setTable("group"); + setTableAlias("g"); + } + + public GroupDao(String uri) { + super(); + this.uri = uri; + setTable("group"); + setTableAlias("g"); + } + + @Override + public POSTResultsReturn checkAndInsert(GroupDTO newObject) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private boolean isElementValid(GroupDTO groupDTO) { + Map groupOk = groupDTO.isOk(); + return (boolean) groupOk.get("state"); + } + + private POSTResultsReturn checkAndInsertGroupList(List newGroups) throws SQLException, Exception { + //init result returned maps + List insertStatusList = new ArrayList<>(); + boolean dataState = true; + boolean resultState = true; + boolean insertionState = true; + POSTResultsReturn results = null; + ArrayList groups = new ArrayList<>(); + + for (GroupDTO groupDTO : newGroups) { + if (isElementValid(groupDTO)) { + Group group = groupDTO.createObjectFromDTO(); + groups.add(group); + } else { + dataState = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Fields are missing in JSON data")); + } + } + if (dataState) { + PreparedStatement insertPreparedStatement = null; + PreparedStatement insertPreparedStatementGroupUser = null; + + final String insertGab = "INSERT INTO \"group\"" + + "(\"uri\", \"name\", " + + "\"level\", \"description\")" + + "VALUES (?, ?, ?, ?)"; + final String insertGabGroupUsers = "INSERT INTO \"at_group_users\"" + + "(\"group_uri\", \"users_email\")" + + " VALUES (?, ?)"; + Connection connection = null; + int inserted = 0; + int exists = 0; + + try { + //batch + boolean insertionLeft = true; + int count = 0; + + //connexion + préparation de la transaction + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + insertPreparedStatement = connection.prepareStatement(insertGab); + insertPreparedStatementGroupUser = connection.prepareStatement(insertGabGroupUsers); + + for (Group group : groups) { + if (!existInDB(group)) { + insertionLeft = true; + insertPreparedStatement.setString(1, group.getUri()); + insertPreparedStatement.setString(2, group.getName()); + insertPreparedStatement.setString(3, group.getLevel()); + insertPreparedStatement.setString(4, group.getDescription()); + + //Ajout dans les logs de qui a fait quoi (traçabilité) + String log = ""; + if (remoteUserAdress != null) { + log += "IP Addess " + remoteUserAdress + " - "; + } + if (user != null) { + log += "User : " + user.getEmail() + " - "; + } + + LOGGER.trace(log + " quert : " + insertPreparedStatement.toString()); + insertPreparedStatement.execute(); + + for (User u : group.getUsers()) { + insertPreparedStatementGroupUser.setString(1, group.getUri()); + insertPreparedStatementGroupUser.setString(2, u.getEmail()); + LOGGER.trace(log + " quert : " + insertPreparedStatementGroupUser.toString()); + insertPreparedStatementGroupUser.execute(); + } + inserted++; + } else { + exists++; + } + + //Insertion par batch + if (++count % batchSize == 0) { + insertPreparedStatement.executeBatch(); + insertPreparedStatementGroupUser.execute(); + insertionLeft = false; + } + } + + if (insertionLeft) { + insertPreparedStatement.executeBatch(); // checkAndInsert remaining records + insertPreparedStatementGroupUser.executeBatch(); + } + connection.commit(); //Envoi des données dans la BD +//////////////////// +//ATTENTION, vérifications à re regarder et re vérifier +////////////////// + //Si data insérées et existantes + if (exists > 0 && inserted > 0) { + results = new POSTResultsReturn(resultState, insertionState, dataState); + insertStatusList.add(new Status("Already existing data", StatusCodeMsg.INFO, "All groups already exist")); + results.setHttpStatus(Response.Status.OK); + results.statusList = insertStatusList; + } else { + if (exists > 0) { //Si données existantes et aucunes insérées + insertStatusList.add(new Status ("Already existing data", StatusCodeMsg.INFO, String.valueOf(exists) + " group already exists")); + } else { //Si données qui n'existent pas et donc sont insérées + insertStatusList.add(new Status("Data inserted", StatusCodeMsg.INFO, String.valueOf(inserted) + " groups inserted")); + } + } + results = new POSTResultsReturn(resultState, insertionState, dataState); + + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + + //Rollback + if (connection != null) { + connection.rollback(); + } + + results = new POSTResultsReturn(false, insertionState, dataState); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getMessage())); + if (e.getNextException() != null) { + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getNextException().getMessage())); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERR, "Duplicated group in json or in database")); + } + results.statusList = insertStatusList; + } finally { + if (insertPreparedStatement != null) { + insertPreparedStatement.close(); + } + if (insertPreparedStatementGroupUser != null) { + insertPreparedStatementGroupUser.close(); + } + if (connection != null) { + connection.close(); + } + } + } else { + results = new POSTResultsReturn(resultState, insertionState, dataState); + results.statusList = insertStatusList; + } + + return results; + } + + @Override + public POSTResultsReturn checkAndInsertList(List newObjects) { + POSTResultsReturn postResult; + try { + postResult = this.checkAndInsertGroupList(newObjects); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, e.toString()); + } + return postResult; + } + + @Override + public Map pkeySQLFieldLink() { + Map pkeySQLFieldLink = new HashMap<>(); + pkeySQLFieldLink.put("uri", "uri"); + return pkeySQLFieldLink; + } + + @Override + public Map relationFieldsJavaSQLObject() { + Map createSQLFields = new HashMap<>(); + createSQLFields.put("uri", "uri"); + createSQLFields.put("name", "name"); + createSQLFields.put("level", "level"); + createSQLFields.put("description", "description"); + + return createSQLFields; + } + + @Override + public Group findByFields(Map Attr, String table) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Group single(int id) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList all() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Group get(ResultSet result) throws SQLException { + Group group = new Group(); + group.setUri(result.getString("uri")); + group.setName(result.getString("name")); + group.setLevel(result.getString("level")); + group.setDescription(result.getString("description")); + + return group; + } + + /** + * + * @param group Group + * @return la liste des users faisant partie du groupe + * @throws SQLException + */ + public ArrayList getGroupUsers(Group group) throws SQLException { + ResultSet result = null; + Connection connection = null; + Statement statement = null; + ArrayList groupUsers = new ArrayList<>(); + + try { + if (this.existInDB(group)) { + //Récupération des users dans les tables at_group_users et users + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendSelect("u.email, u.first_name, u.family_name"); + query.appendFrom("at_group_users", "gu"); + query.appendANDWhereConditionIfNeeded("group_uri", group.getUri(), "=", null, "gu"); + query.appendJoin(JoinAttributes.INNERJOIN, "users", "u", "gu.users_email = u.email"); + + connection = dataSource.getConnection(); + statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY,ResultSet.HOLD_CURSORS_OVER_COMMIT); + + result = statement.executeQuery(query.toString()); + while(result.next()) { + User u = new User(result.getString("email")); + u.setFirstName(result.getString("first_name")); + u.setFamilyName(result.getString("family_name")); + groupUsers.add(u); + } + } + } catch (Exception ex) { + java.util.logging.Logger.getLogger(GroupDao.class.getName()).log(Level.SEVERE, null, ex); + } finally { + if (connection != null) { + connection.close(); + } + if (statement != null) { + statement.close(); + } + if (result != null) { + result.close(); + } + } + return groupUsers; + } + + @Override + public ArrayList allPaginate() { + ResultSet queryResult = null; + Connection connection = null; + Statement statement = null; + + ArrayList groups = new ArrayList<>(); + + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + SQLQueryBuilder query = new SQLQueryBuilder(); + + Map sqlFields = relationFieldsJavaSQLObject(); + + //Ajout des conditions à la requête + query.appendFrom(table, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("uri"), uri, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("name"), name, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("level"), level, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("description"), description, "ILIKE", null, tableAlias); + query.appendLimit(String.valueOf(pageSize)); + + queryResult = statement.executeQuery(query.toString()); + + while(queryResult.next()) { + groups.add(get(queryResult)); + } + + for (Group group : groups) { + group.setUserList(this.getGroupUsers(group)); + } + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(GroupDao.class.getName()).log(Level.SEVERE, null, ex); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + } + if (statement != null) { + statement.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(GroupDao.class.getName()).log(Level.SEVERE, null, ex); + } + } + return groups; + } + + @Override + public Integer count() { + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendCount(); + query.appendDistinct(); + query.appendSelect(tableAlias + ".uri"); + query.appendFrom(table, tableAlias); + + if (uri != null) { + query.appendANDWhereConditionIfNeeded("uri", uri, "=", null, tableAlias); + } + + Connection connection = null; + ResultSet resultSet = null; + Statement statement = null; + + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(); + resultSet = statement.executeQuery(query.toString()); + + if (resultSet.next()) { + return resultSet.getInt(1); + } else { + return 0; + } + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + return null; + } finally { + try { + if (statement != null) { + statement.close(); + } + if (resultSet != null) { + resultSet.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + } + + @Override + protected Group compareAndMergeObjects(Group fromDB, Group object) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected SQLQueryBuilder prepareSearchQuery() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * @action fais les modifications des groupes en base de données. Même organisation de code que pour les post + * @param updatedGroups + * @return + */ + private POSTResultsReturn checkAndUpdateGroupList(List updatedGroups) throws SQLException, Exception { + //init result returned maps + List insertStatusList = new ArrayList<>(); + boolean allGroupsAlreadyInDB = true; + POSTResultsReturn results = null; + + ArrayList groups = new ArrayList<>(); + + //Logs pour la traçabilité + String log = getTraceabilityLogs(); + + //1. on récupère la liste des utilisateurs et on vérifie s'ils existent bien en BD + for (GroupDTO groupDTO : updatedGroups) { + Group group = groupDTO.createObjectFromDTO(); + if (existInDB(group)) { + groups.add(group); + } else { + allGroupsAlreadyInDB = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Unknown group(s)")); + } + } + + //2. Modifications de la BD en fonction des données envoyées + if (allGroupsAlreadyInDB) { //Si tous les users sont présents en BD, on peut continuer + PreparedStatement updatePreparedStatementGroup = null; + PreparedStatement deletePreparedStatementUserGroup = null; + PreparedStatement insertPreparedStatementUserGroup = null; + Connection connection = null; + + final String updateGabGroup = "UPDATE \"group\" SET \"level\" = ?, \"description\" = ? " + + "WHERE \"uri\" = ?"; + final String deleteGabUserGroup = "DELETE FROM \"at_group_users\" WHERE \"group_uri\" = ?"; + final String insertGabUserGroup = "INSERT INTO \"at_group_users\" (\"group_uri\", \"users_email\")" + + " VALUES(?, ?)"; + try { + //Batch + int count = 0; + boolean insertionLeft = true; + + //Connection + préparation de la transaction + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + updatePreparedStatementGroup = connection.prepareStatement(updateGabGroup); + deletePreparedStatementUserGroup = connection.prepareStatement(deleteGabUserGroup); + insertPreparedStatementUserGroup = connection.prepareStatement(insertGabUserGroup); + + for (Group group : groups) { + //Update des données de la table group + updatePreparedStatementGroup.setString(1, group.getLevel()); + updatePreparedStatementGroup.setString(2, group.getDescription()); + updatePreparedStatementGroup.setString(3, group.getUri()); + updatePreparedStatementGroup.execute(); + LOGGER.trace(log + " quert : " + updatePreparedStatementGroup.toString()); + + //Delete des liens group / user + deletePreparedStatementUserGroup.setString(1, group.getUri()); + deletePreparedStatementUserGroup.execute(); + LOGGER.trace(log + " quert : " + deletePreparedStatementUserGroup.toString()); + + //Insert des nouveaux liens users / email + if (group.getUsers() != null && !group.getUsers().isEmpty()) { + for (User u : group.getUsers()) { + insertPreparedStatementUserGroup.setString(1, group.getUri()); + insertPreparedStatementUserGroup.setString(2, u.getEmail()); + insertPreparedStatementUserGroup.execute(); + LOGGER.trace(log + " quert : " + insertPreparedStatementUserGroup); + } + } + + //Insertion par batch + if (++count % batchSize == 0) { + updatePreparedStatementGroup.executeBatch(); + deletePreparedStatementUserGroup.executeBatch(); + insertPreparedStatementUserGroup.executeBatch(); + insertionLeft = false; + } + } + + if (insertionLeft) { + updatePreparedStatementGroup.executeBatch(); + deletePreparedStatementUserGroup.executeBatch(); + insertPreparedStatementUserGroup.executeBatch(); + } + + connection.commit(); //Envoi des données à la BD + + insertStatusList.add(new Status("Data inserted", StatusCodeMsg.INFO, "groups updated")); + results = new POSTResultsReturn(true, true, allGroupsAlreadyInDB); + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + + //Rollback + if (connection != null) { + connection.rollback(); + } + + results = new POSTResultsReturn(false, true, allGroupsAlreadyInDB); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getMessage())); + if (e.getNextException() != null) { + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getNextException().getMessage())); + } + results.statusList = insertStatusList; + } finally { + if (updatePreparedStatementGroup != null) { + updatePreparedStatementGroup.close(); + } + if (deletePreparedStatementUserGroup != null) { + deletePreparedStatementUserGroup.close(); + } + if (insertPreparedStatementUserGroup != null) { + insertPreparedStatementUserGroup.close(); + } + if (connection != null) { + connection.close(); + } + } + + } else { //Certains users ne sont pas déjà présents en BD. + results = new POSTResultsReturn(true, true, allGroupsAlreadyInDB); + results.statusList = insertStatusList; + } + + return results; + } + + @Override + public POSTResultsReturn checkAndUpdateList(List newObjects) { + POSTResultsReturn postResult; + try { + postResult = this.checkAndUpdateGroupList(newObjects); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, e.toString()); + } + return postResult; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/phis/LayerDao.java b/phis2-ws/src/main/java/phis2ws/service/dao/phis/LayerDao.java new file mode 100644 index 000000000..557483db8 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/phis/LayerDao.java @@ -0,0 +1,227 @@ +//********************************************************************************************** +// LayerDao.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: August 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: September, 6 2017 +// Subject: A DAO specific to product layers files +//*********************************************************************************************** +package phis2ws.service.dao.phis; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.nio.file.attribute.UserPrincipal; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.logging.Level; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.configuration.URINamespaces; +import phis2ws.service.dao.sesame.AgronomicalObjectDaoSesame; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.LayerDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.AgronomicalObject; +import phis2ws.service.view.model.phis.Property; + + +public class LayerDao { + + /** + * @param objectURI URI de l'objet auquel la couche correspond + * @param objectType type de l'objet (ex : http://www.phenome-fppn.fr/Vocabulary/2017/Experiment) + * @param depth true si la couche a tous les descendants de objectURI + * false si on elle n'a que les enfants directs + * @param filePath le chemin du fichier geoJSON correspondant à la couche + * @param children la liste des descendants de objectURI. Clé : URI, + * valeur : HashMap avec (clé : le type de l'attribut ex. type, valeur : sa valeur ex. http://...../Experiment) + */ + + final static Logger LOGGER = LoggerFactory.getLogger(LayerDao.class); + + public String objectURI; + public String objectType; + public String depth; + public String filePath; + public String fileWebPath; + public HashMap children = new HashMap<>(); + + private static final String LAYER_FILE_SERVER_DIRECTORY = PropertiesFileManager.getConfigFileProperty("service", "layerFileServerDirectory"); + private static final String LAYER_FILE_SERVER_ADDRESS = PropertiesFileManager.getConfigFileProperty("service", "layerFileServerAddress"); + + + + /** + * @throws java.sql.SQLException + * @action récupère la liste des enfants de objectURI et met à jour + * l'attribut children en conséquence + * @param layerDTO + */ + public void searchAndUpdateChildren(LayerDTO layerDTO) throws SQLException { + AgronomicalObjectDaoSesame agronomicalObjectDaoSesame = new AgronomicalObjectDaoSesame(); + AgronomicalObjectDao agronomicalObject = new AgronomicalObjectDao(); + + HashMap childrendAgronomicalObjectDaoSesame = agronomicalObjectDaoSesame.searchChildren(layerDTO); + + ArrayList childrenURIs = new ArrayList<>(childrendAgronomicalObjectDaoSesame.keySet()); + HashMap childrenAgronomicalObjectsGeometries = agronomicalObject.getGeometries(childrenURIs); + + for (Entry child : childrendAgronomicalObjectDaoSesame.entrySet()) { + AgronomicalObject ao = child.getValue(); + ao.setGeometry(childrenAgronomicalObjectsGeometries.get(child.getKey())); + children.put(child.getKey(), ao); + } + } + + /** + * + * @param objectURI + * @return le lien vers le fichier geojson correspondant à la couche + */ + public String getObjectURILayerFilePath(String objectURI) { + String[] splitUri = objectURI.split("/"); + String layerName = splitUri[splitUri.length-1]; + String filename = layerName + ".geojson"; + return LAYER_FILE_SERVER_DIRECTORY + "/" + filename; + } + + public String getObjectURILayerFileWebPath(String objectURI) { + String[] splitUri = objectURI.split("/"); + String layerName = splitUri[splitUri.length-1]; + String filename = layerName + ".geojson"; + return LAYER_FILE_SERVER_ADDRESS + "/" + filename; + } + + /** + * @action uilisé pour la façon de nommer les propriétés dans le geojson correspondant à la couche + * @return une hashmap avec les noms des propriétés en fonction de l'uri de la relation ou du concept + */ + private HashMap getTypesByURIRelationOrConcept() { + HashMap typesByRelationOrConcept = new HashMap<>(); + URINamespaces uriNamespaces = new URINamespaces(); + typesByRelationOrConcept.put(uriNamespaces.getObjectsProperty("cVariety"), "variety"); + typesByRelationOrConcept.put(uriNamespaces.getObjectsProperty("cGenotype"), "genotype"); + typesByRelationOrConcept.put(uriNamespaces.getObjectsProperty("cSpecies"), "species"); + typesByRelationOrConcept.put(uriNamespaces.getRelationsProperty("rFromGenotype"), "genotype"); + typesByRelationOrConcept.put(uriNamespaces.getRelationsProperty("rFromVariety"), "variety"); + typesByRelationOrConcept.put(uriNamespaces.getRelationsProperty("rFromSpecies"), "species"); + typesByRelationOrConcept.put(uriNamespaces.getRelationsProperty("rExperimentModalities"), "experimentModalities"); + typesByRelationOrConcept.put(uriNamespaces.getRelationsProperty("rHasRepetition"), "repetition"); + typesByRelationOrConcept.put(uriNamespaces.getRelationsProperty("rHasAlias"), "alias"); + typesByRelationOrConcept.put("http://www.w3.org/1999/02/22-rdf-syntax-ns#type", "typeElement"); + + return typesByRelationOrConcept; + } + /** + * @action génère le fichier geojson correspondant à la couche + * @param layerDTO + * @return + */ + public POSTResultsReturn createLayerFile(LayerDTO layerDTO) throws IOException { + POSTResultsReturn createLayerFile; + List createStatusList = new ArrayList<>(); + List createdResourcesFilesPaths = new ArrayList<>(); + boolean createLayerFileOk = true; + try { + //1. on récupère tous les descendants à mettre dans la couche + searchAndUpdateChildren(layerDTO); + + //2. on crée le fichier + String[] splitUri = layerDTO.getObjectUri().split("/"); + String layerName = splitUri[splitUri.length-1]; + String filename = layerName + ".geojson"; + filePath = LAYER_FILE_SERVER_DIRECTORY + "/" + filename; + + try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filePath))) { + //1. On écrit le parent et le début du fichier + //SILEX:test + //code à revoir et à mieux écrire plus tard... + writer.write("{"); + writer.write("\"type\" : \"FeatureCollection\","); + writer.write("\"features\" : ["); + int nbChild = 0; + for (Entry child : children.entrySet()) { + //On écrit chaque enfant + writer.write("{\"type\" : \"Feature\","); + + writer.write("\"geometry\" : " + child.getValue().getGeometry() + ","); + writer.write("\"properties\" : {"); + writer.write("\"uri\" : \"" + child.getValue().getUri()+ "\","); + + //SILEX:conception + //Il faudrait trouver une façon plus générique de le faire + // (dans URINamespaces, une HashMap avec les correspondances uri type/relation --> type de la prop ?) + //Ajout des propriétés associées à l'AO (vartiety, repetition, ...) + HashMap typesByRelationOrConcept = getTypesByURIRelationOrConcept(); + int nbProperties = 0; + for (Property property : child.getValue().getProperties()) { + //On déduit le nom de la propriété du type de relation ou de concept + if (property.getTypeProperty() != null) { + writer.write("\"" + typesByRelationOrConcept.get(property.getTypeProperty()) + "\" : \"" + property.getValue() + "\""); + } else { + writer.write("\"" + typesByRelationOrConcept.get(property.getRelation()) + "\" : \"" + property.getValue() + "\""); + } + nbProperties ++; + if (nbProperties < child.getValue().getProperties().size()) { + writer.write(","); + } + } + //\SILEX:conception + + writer.write("}}"); + nbChild++; + if (nbChild < children.size()) { + writer.write(","); + } + } + + writer.write("]}"); + + java.nio.file.Path path = Paths.get(filePath); + UserPrincipalLookupService lookupService = FileSystems.getDefault().getUserPrincipalLookupService(); + UserPrincipal up = lookupService.lookupPrincipalByName("www-data"); +// Files.setOwner(path, up); ///!\ À décommenter en prod + + File f = new File(filePath); + final Set perms = PosixFilePermissions.fromString("rw-rw-r--"); + Files.setPosixFilePermissions(f.toPath(), perms); + + fileWebPath = LAYER_FILE_SERVER_ADDRESS + "/" + filename; + + createdResourcesFilesPaths.add(filePath); + createStatusList.add(new Status("Resources created", StatusCodeMsg.INFO, createdResourcesFilesPaths.size() + " new resources created")); + //\SILEX:test + //Le fichier est normalement automatiquement fermé + } catch (IOException ex) { + java.util.logging.Logger.getLogger(LayerDao.class.getName()).log(Level.SEVERE, null, ex); + createLayerFileOk = false; + createStatusList.add(new Status("Error while create layer file", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.ERR).toString())); + } + + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(LayerDao.class.getName()).log(Level.SEVERE, null, ex); + createLayerFileOk = false; + createStatusList.add(new Status("Error while create layer file", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.ERR).toString())); + } + + createLayerFile = new POSTResultsReturn(createLayerFileOk, createLayerFileOk, createLayerFileOk); + createLayerFile.statusList = createStatusList; + return createLayerFile; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/phis/ProjectDao.java b/phis2-ws/src/main/java/phis2ws/service/dao/phis/ProjectDao.java new file mode 100644 index 000000000..28f2977af --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/phis/ProjectDao.java @@ -0,0 +1,609 @@ +//********************************************************************************************** +// ProjectDao.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: A Dao specific to retrieve project data +//*********************************************************************************************** +package phis2ws.service.dao.phis; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.dao.manager.DAOPhisBrapi; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.ProjectDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sql.JoinAttributes; +import phis2ws.service.utils.sql.SQLQueryBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.Contact; +import phis2ws.service.view.model.phis.Project; + +public class ProjectDao extends DAOPhisBrapi { + + final static Logger LOGGER = LoggerFactory.getLogger(ProjectDao.class); + + public String uri; + public String name; + public String acronyme; + public String subprojectType; + public String financialSupport; + public String financialName; + public String dateStart; + public String dateEnd; + public String keywords; + public String description; + public String objective; + public String parentProject; + public String website; + + public ProjectDao() { + super(); + setTable("project"); + setTableAlias("p"); + } + + public ProjectDao(String projectURI) { + super(); + this.uri = projectURI; + setTable("project"); + setTableAlias("p"); + } + + @Override + public POSTResultsReturn checkAndInsert(ProjectDTO newObject) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private boolean isElementValid(ProjectDTO projectDTO) { + Map projectOk = projectDTO.isOk(); + return (boolean) projectOk.get("state"); + } + + private POSTResultsReturn checkAndInsertProjectList(List newProjects) throws SQLException, Exception { + //init resuts returned maps + List insertStatusList = new ArrayList<>(); + boolean dataState = true; + boolean resultState = true; + boolean insertionState = true; + POSTResultsReturn results = null; + ArrayList projects = new ArrayList<>(); + + for (ProjectDTO projectDTO : newProjects) { + if (isElementValid(projectDTO)) { + Project project = projectDTO.createObjectFromDTO(); + projects.add(project); + } else { + dataState = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Fields are missing in JSON Data")); + } + } + if (dataState) { + PreparedStatement insertPreparedStatementProject = null; + PreparedStatement insertPreparedStatementProjectContact = null; + + final String insertGabProject = "INSERT INTO \"project\"" + + "(\"uri\", \"name\", \"acronyme\", \"subproject_type\", \"financial_support\"," + + "\"financial_name\", \"date_start\", \"date_end\", \"keywords\", \"description\"," + + "\"objective\", \"parent_project\"," + + " \"website\")" + + " VALUES (?, ?, ?, ?, ?, ?, to_date(?, 'YYYY:MM:DD'), to_date(?, 'YYYY:MM:DD'), ?, ?, ?, ?, ?)"; + final String insertGabProjectContact = "INSERT INTO \"at_project_users\"" + + "(\"project_uri\", \"users_email\", \"type\")" + + " VALUES (?, ?, ?)"; + + Connection connection = null; + int inserted = 0; + int exists = 0; + + try { + //batch + boolean insertionLeft = true; + int count = 0; + + //connexion + préparation de la transaction + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + insertPreparedStatementProject = connection.prepareStatement(insertGabProject); + insertPreparedStatementProjectContact = connection.prepareStatement(insertGabProjectContact); + + for (Project project : projects) { + if (!existInDB(project)) { + insertionLeft = true; + insertPreparedStatementProject.setString(1, project.getUri()); + insertPreparedStatementProject.setString(2, project.getName()); + insertPreparedStatementProject.setString(3, project.getAcronyme()); + insertPreparedStatementProject.setString(4, project.getSubprojectType()); + insertPreparedStatementProject.setString(5, project.getFinancialSupport()); + insertPreparedStatementProject.setString(6, project.getFinancialName()); + insertPreparedStatementProject.setString(7, project.getDateStart()); + insertPreparedStatementProject.setString(8, project.getDateEnd()); + insertPreparedStatementProject.setString(9, project.getKeywords()); + insertPreparedStatementProject.setString(10, project.getDescription()); + insertPreparedStatementProject.setString(11, project.getObjective()); + insertPreparedStatementProject.setString(12, project.getParentProject()); + insertPreparedStatementProject.setString(13, project.getWebsite()); + + //Ajout dans les logs de qui a fait quoi (traçabilité) + String log = ""; + if (remoteUserAdress != null) { + log += "IP Addess " + remoteUserAdress + " - "; + } + if (user != null) { + log += "User : " + user.getEmail() + " - "; + } + + insertPreparedStatementProject.execute(); + LOGGER.trace(log + " quert : " + insertPreparedStatementProject.toString()); + + for (Contact contact : project.getContacts()) { + insertPreparedStatementProjectContact.setString(1, project.getUri()); + insertPreparedStatementProjectContact.setString(2, contact.getEmail()); + insertPreparedStatementProjectContact.setString(3, contact.getType()); + insertPreparedStatementProjectContact.execute(); + LOGGER.trace(log + " quert : " + insertPreparedStatementProjectContact.toString()); + } + inserted++; + } else { + exists++; + } + + //Insertion par batch + if (++count % batchSize == 0) { + insertPreparedStatementProject.executeBatch(); + insertPreparedStatementProjectContact.executeBatch(); + insertionLeft = false; + } + } + + if (insertionLeft) { + insertPreparedStatementProject.executeBatch(); // checkAndInsert remaining records + insertPreparedStatementProjectContact.executeBatch(); + } + + connection.commit(); //Envoi des données dans la BD + +//////////////////// + //ATTENTION, vérifications à re regarder et re vérifier +////////////////// + //Si data insérées et existantes + if (exists > 0 && inserted > 0) { + results = new POSTResultsReturn(resultState, insertionState, dataState); + insertStatusList.add(new Status("Already existing data", StatusCodeMsg.INFO, "All projects already exist")); + results.setHttpStatus(Response.Status.OK); + results.statusList = insertStatusList; + } else { + if (exists > 0) { //Si données existantes et aucunes insérées + insertStatusList.add(new Status ("Already existing data", StatusCodeMsg.INFO, String.valueOf(exists) + " project already exists")); + } else { //Si données qui n'existent pas et donc sont insérées + insertStatusList.add(new Status("Data inserted", StatusCodeMsg.INFO, String.valueOf(inserted) + " projects inserted")); + } + } + results = new POSTResultsReturn(resultState, insertionState, dataState); + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + + //Rollback + if (connection != null) { + connection.rollback(); + } + + results = new POSTResultsReturn(false, insertionState, dataState); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getMessage())); + if (e.getNextException() != null) { + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getNextException().getMessage())); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERR, "Duplicated project in json or in database")); + } + results.statusList = insertStatusList; + } finally { + if (insertPreparedStatementProject != null) { + insertPreparedStatementProject.close(); + } + if (insertPreparedStatementProjectContact != null) { + insertPreparedStatementProjectContact.close(); + } + if (connection != null) { + connection.close(); + } + } + } else { + results = new POSTResultsReturn(resultState, insertionState, dataState); + results.statusList = insertStatusList; + } + + return results; + } + + + /** + * Vérifie tous les projets et les insère dans la BD Postgres + * @param newObjects list des projets + * @return + */ + @Override + public POSTResultsReturn checkAndInsertList(List newObjects) { + POSTResultsReturn postResult; + try { + postResult = this.checkAndInsertProjectList(newObjects); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, e.toString()); + } + + return postResult; + } + + @Override + public Map pkeySQLFieldLink() { + Map pkeySQLFieldLink = new HashMap<>(); + pkeySQLFieldLink.put("uri", "uri"); + return pkeySQLFieldLink; + } + + @Override + public Map relationFieldsJavaSQLObject() { + Map createSQLFields = new HashMap<>(); + createSQLFields.put("uri", "uri"); + createSQLFields.put("name", "name"); + createSQLFields.put("acronyme", "acronyme"); + createSQLFields.put("subprojectType", "subproject_type"); + createSQLFields.put("financialSupport", "financial_support"); + createSQLFields.put("financialName", "financial_name"); + createSQLFields.put("dateStart", "date_start"); + createSQLFields.put("dateEnd", "date_end"); + createSQLFields.put("keywords", "keywords"); + createSQLFields.put("description", "description"); + createSQLFields.put("objective", "objective"); + createSQLFields.put("parentProject", "parent_project"); + createSQLFields.put("website", "website"); + createSQLFields.put("type", "type"); + + return createSQLFields; + } + + @Override + public Project findByFields(Map Attr, String table) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Project single(int id) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList all() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Project get(ResultSet result) throws SQLException { + Project project = new Project(); + project.setUri(result.getString("uri")); + project.setName(result.getString("name")); + project.setAcronyme(result.getString("acronyme")); + project.setSubprojectType(result.getString("subproject_type")); + project.setFinancialSupport(result.getString("financial_support")); + project.setFinancialName(result.getString("financial_name")); + project.setDateStart(result.getString("date_start")); + project.setDateEnd(result.getString("date_end")); + project.setKeywords(result.getString("keywords")); + project.setDescription(result.getString("description")); + project.setObjective(result.getString("objective")); + project.setParentProject(result.getString("parent_project")); + project.setWebsite(result.getString("website")); + + return project; + } + + /** + * + * @param projects ArrayList liste des projets pour lesquels on veut les contacts + * @param statement Statement + * @return la liste envoyée en paramètre contenant en plus pour chaque projet la liste de ses contacts + * @throws SQLException + */ + private ArrayList getProjectsContacts(ArrayList projects, Statement statement) throws SQLException{ + for (Project project : projects) { + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendSelect("u.email, u.first_name, u.family_name, pu.type"); + query.appendFrom("at_project_users", "pu"); + query.appendANDWhereConditionIfNeeded("project_uri", project.getUri(), "=", null, "pu"); + query.appendJoin(JoinAttributes.INNERJOIN, "users", "u", "u.email = pu.users_email"); + + ResultSet queryResult = statement.executeQuery(query.toString()); + while (queryResult.next()) { + Contact contact = new Contact(); + contact.setEmail(queryResult.getString("email")); + contact.setFirstName(queryResult.getString("first_name")); + contact.setFamilyName(queryResult.getString("family_name")); + contact.setType(queryResult.getString("type")); + project.addContact(contact); + } + } + + return projects; + } + + @Override + public ArrayList allPaginate() { + ResultSet queryResult = null; + Connection connection = null; + Statement statement = null; + ArrayList projects = new ArrayList<>(); + + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + SQLQueryBuilder query = new SQLQueryBuilder(); + + Map sqlFields = relationFieldsJavaSQLObject(); + + //Ajout des conditions à la requête + query.appendFrom(table, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("uri"), uri, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("name"), name, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("acronyme"), acronyme, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("subprojectType"), subprojectType, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("financialSupport"), financialSupport, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("financialName"), financialName, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("dateStart"), dateStart, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("dateEnd"), dateEnd, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("keywords"), keywords, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("description"), description, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("objective"), objective, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("parentProject"), parentProject, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("website"), website, "ILIKE", null, tableAlias); + query.appendLimit(String.valueOf(pageSize)); + + queryResult = statement.executeQuery(query.toString()); + + while (queryResult.next()) { + projects.add(get(queryResult)); + } + + projects = getProjectsContacts(projects, statement); + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(ProjectDao.class.getName()).log(Level.SEVERE, null, ex); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + } + if (statement != null) { + statement.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(ProjectDao.class.getName()).log(Level.SEVERE, null, ex); + } + } + return projects; + } + + @Override + public Integer count() { + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendCount(); + query.appendDistinct(); + query.appendSelect(tableAlias + ".uri"); + query.appendFrom(table, tableAlias); + + if (uri != null) { + query.appendWhereConditions("uri", uri, "=", null, tableAlias); + } + + Connection connection = null; + ResultSet resultSet = null; + Statement statement = null; + + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(); + resultSet = statement.executeQuery(query.toString()); + + if (resultSet.next()) { + return resultSet.getInt(1); + } else { + return 0; + } + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + return null; + } finally { + try { + if (statement != null) { + statement.close(); + } + if (resultSet != null) { + resultSet.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + } + + @Override + protected Project compareAndMergeObjects(Project fromDB, Project object) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected SQLQueryBuilder prepareSearchQuery() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private POSTResultsReturn checkAndUpdateProjectList(List updateProjects) throws SQLException, Exception { + //init result returned maps + List insertStatusList = new ArrayList<>(); + boolean allProjectsAlreadyInDB = true; + POSTResultsReturn results = null; + + ArrayList projects = new ArrayList<>(); + + String log = getTraceabilityLogs(); + + //1. On récupère la liste des projets et on vérifie qu'ils existent bien en BD + if (updateProjects != null && !updateProjects.isEmpty()) { + for (ProjectDTO projectDTO : updateProjects) { + Project project = projectDTO.createObjectFromDTO(); + if (existInDB(project)) { + projects.add(project); + } else { + allProjectsAlreadyInDB = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Unknown project")); + } + } + } + + //2. Modifications de la BD en fonction des données envoyées + if (allProjectsAlreadyInDB) { //Si tous les projets sont présents en BD on peut continuer + PreparedStatement updatePreparedStatementProject = null; + PreparedStatement deletePreparedStatementContacts = null; + PreparedStatement insertPreparedStatementContacts = null; + Connection connection = null; + + final String updateProject = "UPDATE \"project\" SET \"name\" = ?, \"subproject_type\" = ?, " + + "\"financial_support\" = ?, \"financial_name\" = ?, \"date_start\" = to_date(?, 'YYYY:MM:DD'), \"date_end\" = to_date(?, 'YYYY:MM:DD')," + + "\"keywords\" = ?, \"description\" = ?, \"objective\" = ?, \"parent_project\" = ?," + + "\"website\" = ? " + + "WHERE \"uri\" = ?"; + final String deleteContacts = "DELETE FROM \"at_project_users\" WHERE \"project_uri\" = ?"; + final String insertContact = "INSERT INTO \"at_project_users\" (\"project_uri\", \"users_email\", \"type\") " + + "VALUES (?, ?, ?)"; + try { + //Batch + int count = 0; + boolean insertionLeft = true; + + //Connection + préparation de la transaction + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + updatePreparedStatementProject = connection.prepareStatement(updateProject); + deletePreparedStatementContacts = connection.prepareStatement(deleteContacts); + insertPreparedStatementContacts = connection.prepareStatement(insertContact); + + for (Project project : projects) { + //Update des données de la table project + updatePreparedStatementProject.setString(1, project.getName()); + updatePreparedStatementProject.setString(2, project.getSubprojectType()); + updatePreparedStatementProject.setString(3, project.getFinancialSupport()); + updatePreparedStatementProject.setString(4, project.getFinancialName()); + updatePreparedStatementProject.setString(5, project.getDateStart()); + updatePreparedStatementProject.setString(6, project.getDateEnd()); + updatePreparedStatementProject.setString(7, project.getKeywords()); + updatePreparedStatementProject.setString(8, project.getDescription()); + updatePreparedStatementProject.setString(9, project.getObjective()); + updatePreparedStatementProject.setString(10, project.getParentProject()); + updatePreparedStatementProject.setString(11, project.getWebsite()); + updatePreparedStatementProject.setString(12, project.getUri()); + LOGGER.trace(log + " quert : " + updatePreparedStatementProject.toString()); + updatePreparedStatementProject.execute(); + + //Delete des contacts + deletePreparedStatementContacts.setString(1, project.getUri()); + deletePreparedStatementContacts.execute(); + LOGGER.trace(log + " quert : " + deletePreparedStatementContacts.toString()); + + //Insertion des contacts + if (project.getContacts() != null && !project.getContacts().isEmpty()) { + for (Contact contact : project.getContacts()) { + insertPreparedStatementContacts.setString(1, project.getUri()); + insertPreparedStatementContacts.setString(2, contact.getEmail()); + insertPreparedStatementContacts.setString(3, contact.getType()); + insertPreparedStatementContacts.execute(); + LOGGER.trace(log + " quert : " + insertPreparedStatementContacts.toString()); + } + } + + //Insertion par batch + if (++count % batchSize == 0) { + updatePreparedStatementProject.executeBatch(); + deletePreparedStatementContacts.executeBatch(); + insertPreparedStatementContacts.executeBatch(); + insertionLeft = false; + } + } + + if (insertionLeft) { + updatePreparedStatementProject.executeBatch(); + deletePreparedStatementContacts.executeBatch(); + insertPreparedStatementContacts.executeBatch(); + } + + connection.commit(); //Envoi des données à la BD + + insertStatusList.add(new Status("Data updated", StatusCodeMsg.INFO, "projects updated")); + results = new POSTResultsReturn(true, true, allProjectsAlreadyInDB); + + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + + //Rollback + if (connection != null) { + connection.rollback(); + } + + results = new POSTResultsReturn(false, true, allProjectsAlreadyInDB); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getMessage())); + if (e.getNextException() != null) { + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getNextException().getMessage())); + } + results.statusList = insertStatusList; + } finally { + if (updatePreparedStatementProject != null) { + updatePreparedStatementProject.close(); + } + if (deletePreparedStatementContacts != null) { + deletePreparedStatementContacts.close(); + } + if (insertPreparedStatementContacts != null) { + insertPreparedStatementContacts.close(); + } + if (connection != null) { + connection.close(); + } + } + } else { //Certains projets ne sont pas déjà présents en BD. + results = new POSTResultsReturn(true, true, allProjectsAlreadyInDB); + results.statusList = insertStatusList; + } + + return results; + } + + @Override + public POSTResultsReturn checkAndUpdateList(List newObjects) { + POSTResultsReturn postResult; + try { + postResult = this.checkAndUpdateProjectList(newObjects); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, e.toString()); + } + return postResult; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/phis/SessionDaoPhisBrapi.java b/phis2-ws/src/main/java/phis2ws/service/dao/phis/SessionDaoPhisBrapi.java new file mode 100644 index 000000000..22294a21e --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/phis/SessionDaoPhisBrapi.java @@ -0,0 +1,222 @@ +package phis2ws.service.dao.phis; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.dao.manager.DAOPhisBrapi; +import phis2ws.service.authentication.Session; +import phis2ws.service.authentication.TokenManager; +import phis2ws.service.model.User; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sql.SQLQueryBuilder; + +/** + * Manipule les Sessions et leur modifications à partir de la base de données + * + * @date 05/2016 + * @author Arnaud CHARLEROY + */ +public class SessionDaoPhisBrapi extends DAOPhisBrapi { + + final static Logger logger = LoggerFactory.getLogger(SessionDaoPhisBrapi.class); + + public SessionDaoPhisBrapi() { + super(); + setTable("session"); + } + + @Override + public Integer count() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Map pkeySQLFieldLink() { + Map pkeySQLFieldLink = new HashMap<>(); + pkeySQLFieldLink.put("id", "id"); + return pkeySQLFieldLink; + } + + @Override + public Map relationFieldsJavaSQLObject() { + Map createSQLFields = new HashMap<>(); + createSQLFields.put("id", "id"); + createSQLFields.put("email", "email"); + createSQLFields.put("date", "date"); + createSQLFields.put("date_end", "date_end"); + return createSQLFields; + } + + @Override + public Session findByFields(Map Attr, String table) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Session single(int id) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList all() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Session get(ResultSet result) throws SQLException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList allPaginate() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected Session compareAndMergeObjects(Session fromDB, Session object) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected SQLQueryBuilder prepareSearchQuery() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + public void endSession(String sessionId) { + Statement statement = null; + Connection con = null; + ResultSet rs = null; + try { + con = dataSource.getConnection(); + statement = con.createStatement(); + String query = "UPDATE " + table + " SET date_end = now() WHERE id = '" + sessionId + "'"; +// logger.debug(query); + statement.executeUpdate(query); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } finally { + + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + logger.error(e.getMessage(), e); + } + + } + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + logger.error(e.getMessage(), e); + } + } + + if (con != null) { + try { + con.close(); + } catch (SQLException e) { + logger.error(e.getMessage(), e); + } + } + } + } + + public void reloadActiveSession() { + Statement statement = null; + Connection con = null; + ResultSet rs = null; + try { + con = dataSource.getConnection(); + statement = con.createStatement(); + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendFrom(table, null); + query.addISNULL("date_end"); +// logger.debug(query.toString()); + rs = statement.executeQuery(query.toString()); + if (rs != null) { + while (rs.next()) { + if (rs.getString("email") != null) { + String email = rs.getString("email"); + User u = new User(email); + u = loadFromDB(u); + Session s = new Session(rs.getString("id"), email, u); + if (u != null ) { + TokenManager.Instance().createTokenFromBD(s); + } + } + } + } +// TokenManager.Instance().searchSession("5e47fd3d1639c95957f1e9099ddadd84"); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } finally { + + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + logger.error(e.getMessage(), e); + } + + } + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + logger.error(e.getMessage(), e); + } + } + + if (con != null) { + try { + con.close(); + } catch (SQLException e) { + logger.error(e.getMessage(), e); + } + } + + } + } + + /** + * loadFromDB Charge attributs de l'utilisateur depuis la BD + * + * @param user utilisateur avec email et mot de passe + * @return utilisateur avec les champ définis dans UserDaoPhisBrapi dans + relationFieldsJavaSQLObject + */ + private User loadFromDB(User user) { + UserDaoPhisBrapi userDao = new UserDaoPhisBrapi(); + try { + user = userDao.find(user); + } catch (Exception ex) { + logger.error(ex.getMessage(), ex); + } + //user.setIsAdmin(userDao.isAdmin(user)); // Admin ou non + return user; + } + + @Override + public POSTResultsReturn checkAndInsertList(List newObjects) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public POSTResultsReturn checkAndInsert(Object newObject) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public POSTResultsReturn checkAndUpdateList(List newObjects) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } +} + diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/phis/UserDaoPhisBrapi.java b/phis2-ws/src/main/java/phis2ws/service/dao/phis/UserDaoPhisBrapi.java new file mode 100644 index 000000000..b8235d2ff --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/phis/UserDaoPhisBrapi.java @@ -0,0 +1,757 @@ +//********************************************************************************************** +// UserDaoPhisBrapi.java +// +// Author(s): Anne TIREAU, Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: may 2016 +// Contact:arnaud.charleroy@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr, +// morgane.vidal@inra.fr +// Last modification date: May, 2017 +// Subject: Manipule les Sessions et leur modifications à partir de la base de données +//*********************************************************************************************** +package phis2ws.service.dao.phis; + +import phis2ws.service.model.User; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.dao.manager.DAOPhisBrapi; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.UserDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.ResourcesUtils; +import phis2ws.service.utils.sql.JoinAttributes; +import phis2ws.service.utils.sql.SQLQueryBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.Group; + +/** + * Manipule les Sessions et leur modifications à partir de la base de données + * + * @date 05/2016 + * @author Arnaud CHARLEROY + * @update [Morgane Vidal] 04/17 suppression des attributs isAdmin, role, type + * dans la table User ce qui a impliqué la suppression des méthodes isAdmin, + * getProjectUserType, getUserGroup, getUserRole, getUserExperiment + * + */ +public class UserDaoPhisBrapi extends DAOPhisBrapi { + + final static Logger LOGGER = LoggerFactory.getLogger(UserDaoPhisBrapi.class); + + public String email; + public String password; + public String firstName; + public String familyName; + public String address; + public String phone; + public String affiliation; + public String orcid; + public Boolean admin; + public String available; + + public UserDaoPhisBrapi() { + super(); + setTable("users"); + setTableAlias("u"); + } + + public UserDaoPhisBrapi(String email) { + super(); + this.email = email; + setTable("users"); + setTableAlias("u"); + } + + /** + * + * @param email l'email pour lequel on souhaite récupérer le mot de passe + * @return le mot de passe correspondant à l'email envoyé en paramètre + */ + public String getPasswordFromDb(String email) { + ResultSet result = null; + Connection con = null; + Statement stat = null; + try { + con = dataSource.getConnection(); + String query = "SELECT password FROM users WHERE email = '" + email + "'"; + stat = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + result = stat.executeQuery(query); + + while (result.next()) { + return result.getString("password"); + } + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(UserDaoPhisBrapi.class.getName()).log(Level.SEVERE, null, ex); + } finally { + if (result != null) { + try { + result.close(); + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + if (stat != null) { + try { + stat.close(); + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + if (con != null) { + try { + con.close(); + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + } + + return ""; + } + + /** + * Verifie si un utilisateur est Admin + * + * @param u + * @return + */ + public Boolean isAdmin(User u) { + if (u.getAdmin() == null) { + ResultSet result = null; + Connection con = null; + Statement stat = null; + try { + con = dataSource.getConnection(); + String query = "SELECT isadmin FROM users WHERE email='" + u.getEmail() + "'"; + stat = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + result = stat.executeQuery(query); + + while (result.next()) { + u.setAdmin(result.getString("isadmin")); + } + + return ResourcesUtils.getStringBooleanValue(u.getAdmin()); + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } finally { + if (result != null) { + try { + result.close(); + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + if (stat != null) { + try { + stat.close(); + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + if (con != null) { + try { + con.close(); + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + } + } else { + return ResourcesUtils.getStringBooleanValue(u.getAdmin()); + } + return false; + } + + @Override + public Integer count() { + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendCount(); + query.appendDistinct(); + query.appendSelect(tableAlias + ".email"); + query.appendFrom(table, tableAlias); + + if (email != null) { + query.appendANDWhereConditionIfNeeded("email", email, "=", null, tableAlias); + } + + Connection connection = null; + ResultSet resultSet = null; + Statement statement = null; + + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(); + resultSet = statement.executeQuery(query.toString()); + + if (resultSet.next()) { + return resultSet.getInt(1); + } else { + return 0; + } + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + return null; + } finally { + try { + if (statement != null) { + statement.close(); + } + if (resultSet != null) { + resultSet.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + } + + @Override + public Map pkeySQLFieldLink() { + Map pkeySQLFieldLink = new HashMap<>(); + pkeySQLFieldLink.put("email", "email"); + return pkeySQLFieldLink; + } + + @Override + public Map relationFieldsJavaSQLObject() { + Map createSQLFields = new HashMap<>(); + createSQLFields.put("email", "email"); + createSQLFields.put("password", "password"); + createSQLFields.put("firstName", "first_name"); + createSQLFields.put("familyName", "family_name"); + createSQLFields.put("address", "address"); + createSQLFields.put("phone", "phone"); + createSQLFields.put("affiliation", "affiliation"); + createSQLFields.put("orcid", "orcid"); + createSQLFields.put("admin", "isadmin"); + createSQLFields.put("available", "available"); + + return createSQLFields; + } + + @Override + public User findByFields(Map Attr, String table) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public User single(int id) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ArrayList all() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public User get(ResultSet result) throws SQLException { + User userToReturn = new User(result.getString("email")); + userToReturn.setPassword(result.getString("password")); + userToReturn.setFirstName(result.getString("first_name")); + userToReturn.setFamilyName(result.getString("family_name")); + userToReturn.setAddress(result.getString("address")); + userToReturn.setPhone(result.getString("phone")); + userToReturn.setAffiliation(result.getString("affiliation")); + userToReturn.setOrcid(result.getString("orcid")); + userToReturn.setAdmin(result.getString("isadmin")); + userToReturn.setAvailable(result.getString("available")); + + return userToReturn; + } + + @Override + public ArrayList allPaginate() { + ResultSet queryResult = null; + Connection connection = null; + Statement statement = null; + + ArrayList users = new ArrayList<>(); + + try { + connection = dataSource.getConnection(); + statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + SQLQueryBuilder query = new SQLQueryBuilder(); + + Map sqlFields = relationFieldsJavaSQLObject(); + + //Ajout des conditions à la requête + query.appendFrom(table, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("email"), email, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("firstName"), firstName, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("familyName"), familyName, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("address"), address, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("phone"), phone, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("affiliation"), affiliation, "ILIKE", null, tableAlias); + query.appendANDWhereConditionIfNeeded(sqlFields.get("orcid"), orcid, "ILIKE", null, tableAlias); + if (admin != null) { + query.appendANDWhereConditionIfNeeded(sqlFields.get("admin"), String.valueOf(admin), "=", null, tableAlias); + } + if (available != null) { + query.appendANDWhereConditionIfNeeded(sqlFields.get("available"), String.valueOf(available), "=", null, tableAlias); + } + query.appendLimit(String.valueOf(pageSize)); + + queryResult = statement.executeQuery(query.toString()); + + while (queryResult.next()) { + users.add(get(queryResult)); + } + + for (User u : users) { + u.setGroupList(this.getUserGroups(u)); + } + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(UserDaoPhisBrapi.class.getName()).log(Level.SEVERE, null, ex); + } finally { + try { + if (queryResult != null) { + queryResult.close(); + } + if (statement != null) { + statement.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(UserDaoPhisBrapi.class.getName()).log(Level.SEVERE, null, ex); + } + } + return users; + } + + @Override + protected User compareAndMergeObjects(User fromDB, User object) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * + * @param u User + * @return la liste des groupes auxquels appartient l'utilisateur. (liste + * des noms de gp et lvls) + * @throws SQLException + */ + public ArrayList getUserGroups(User u) throws SQLException { + ResultSet result = null; + ResultSet resultDefaultGroup = null; + Connection connection = null; + Statement statement = null; + Statement statementDefaultGroup = null; + ArrayList userGroups = new ArrayList<>(); + + try { + if (this.existInDB(u)) { + //Récupération des groupes dans la table at_group_users + SQLQueryBuilder query = new SQLQueryBuilder(); + query.appendSelect("gp.uri, gp.level, gp.name"); + query.appendFrom("at_group_users", "gu"); + query.appendANDWhereConditions("users_email", u.getEmail(), "=", null, null); + query.appendJoin(JoinAttributes.INNERJOIN, "group", "gp", "gu.group_uri = gp.uri"); + + connection = dataSource.getConnection(); + statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); + + result = statement.executeQuery(query.toString()); + while (result.next()) { + Group gp = new Group(); + gp.setUri(result.getString("uri")); + gp.setLevel(result.getString("level")); + gp.setName(result.getString("name")); + userGroups.add(gp); + } + } + } catch (Exception ex) { + java.util.logging.Logger.getLogger(UserDaoPhisBrapi.class.getName()).log(Level.SEVERE, null, ex); + } finally { + if (connection != null) { + connection.close(); + } + if (statement != null) { + statement.close(); + } + if (result != null) { + result.close(); + } + if (resultDefaultGroup != null) { + resultDefaultGroup.close(); + } + if (statementDefaultGroup != null) { + statementDefaultGroup.close(); + } + } + + return userGroups; + } + + @Override + protected SQLQueryBuilder prepareSearchQuery() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private boolean isElementValid(UserDTO userDTO) { + Map userOk = userDTO.isOk(); + return (boolean) userOk.get("state"); + } + + private POSTResultsReturn checkAndInsertUserList(List newUsers) throws SQLException, Exception { + //init result returned maps + List insertStatusList = new ArrayList<>(); + boolean dataState = true; + boolean resultState = true; + boolean insertionState = true; + POSTResultsReturn results = null; + ArrayList users = new ArrayList<>(); + + for (UserDTO userDTO : newUsers) { + if (isElementValid(userDTO)) { + User u = userDTO.createObjectFromDTO(); + users.add(u); + } else { + dataState = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Fields are missing in JSON data")); + } + } + + if (dataState) { + PreparedStatement insertPreparedStatementUser = null; + PreparedStatement insertPreparedStatementAtGroupUsers = null; + + final String insertGab = "INSERT INTO \"users\"" + + "(\"email\", \"password\", \"first_name\", \"family_name\", \"address\"," + + "\"phone\", \"affiliation\", \"orcid\", \"isadmin\")" + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, cast(? as boolean))"; + + final String insertGabAtUserGroup = "INSERT INTO \"at_group_users\" " + + "(\"users_email\", \"group_uri\")" + + " VALUES (?, ?)"; + Connection connection = null; + int inserted = 0; + int exists = 0; + + try { + //batch + boolean insertionLeft = true; + int count = 0; + + //connexion + préparation de la transaction + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + insertPreparedStatementUser = connection.prepareStatement(insertGab); + insertPreparedStatementAtGroupUsers = connection.prepareStatement(insertGabAtUserGroup); + + for (User u : users) { + if (!existInDB(u)) { + insertionLeft = true; + insertPreparedStatementUser.setString(1, u.getEmail()); + insertPreparedStatementUser.setString(2, u.getPassword()); + insertPreparedStatementUser.setString(3, u.getFirstName()); + insertPreparedStatementUser.setString(4, u.getFamilyName()); + insertPreparedStatementUser.setString(5, u.getAddress()); + insertPreparedStatementUser.setString(6, u.getPhone()); + insertPreparedStatementUser.setString(7, u.getAffiliation()); + insertPreparedStatementUser.setString(8, u.getOrcid()); + if (u.getAdmin() != null) { + insertPreparedStatementUser.setString(9, u.getAdmin()); + } else { + insertPreparedStatementUser.setString(9, "f"); + } + + //Ajout dans les logs de qui a fait quoi (traçabilité) + String log = ""; + if (remoteUserAdress != null) { + log += "IP Addess " + remoteUserAdress + " - "; + } + if (user != null) { + log += "User : " + user.getEmail() + " - "; + } + + insertPreparedStatementUser.execute(); + + //Ajout dans at_group_users des groupes auxquels l'utilisateur appartient. + for (Group group : u.getGroups()) { + insertPreparedStatementAtGroupUsers.setString(1, u.getEmail()); + insertPreparedStatementAtGroupUsers.setString(2, group.getUri()); + LOGGER.trace(log + " quert : " + insertPreparedStatementAtGroupUsers.toString()); + insertPreparedStatementAtGroupUsers.execute(); + } + + inserted++; + } else { + exists++; + } + + //Insertion par batch + if (++count % batchSize == 0) { + insertPreparedStatementUser.executeBatch(); + insertPreparedStatementAtGroupUsers.executeBatch(); + insertionLeft = false; + } + } + + if (insertionLeft) { + insertPreparedStatementUser.executeBatch(); // checkAndInsert remaining records + insertPreparedStatementAtGroupUsers.executeBatch(); + } + connection.commit(); //Envoi des données dans la BD +//////////////////// +//ATTENTION, vérifications à re regarder et re vérifier +////////////////// + //Si data insérées et existantes + if (exists > 0 && inserted > 0) { + results = new POSTResultsReturn(resultState, insertionState, dataState); + insertStatusList.add(new Status("Already existing data", StatusCodeMsg.INFO, "All users already exist")); + results.setHttpStatus(Response.Status.OK); + results.statusList = insertStatusList; + } else { + if (exists > 0) { //Si données existantes et aucunes insérées + insertStatusList.add(new Status("Already existing data", StatusCodeMsg.INFO, String.valueOf(exists) + " user already exists")); + } else { //Si données qui n'existent pas et donc sont insérées + insertStatusList.add(new Status("Data inserted", StatusCodeMsg.INFO, String.valueOf(inserted) + " users inserted")); + } + } + results = new POSTResultsReturn(resultState, insertionState, dataState); + + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + + //Rollback + if (connection != null) { + connection.rollback(); + } + + results = new POSTResultsReturn(false, insertionState, dataState); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getMessage())); + if (e.getNextException() != null) { + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getNextException().getMessage())); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERR, "Duplicated user in json or in database")); + } + results.statusList = insertStatusList; + } finally { + if (insertPreparedStatementUser != null) { + insertPreparedStatementUser.close(); + } + if (insertPreparedStatementAtGroupUsers != null) { + insertPreparedStatementAtGroupUsers.close(); + } + if (connection != null) { + connection.close(); + } + } + } else { + results = new POSTResultsReturn(resultState, insertionState, dataState); + results.statusList = insertStatusList; + } + + return results; + } + + @Override + public POSTResultsReturn checkAndInsertList(List newObjects) { + POSTResultsReturn postResult; + try { + postResult = this.checkAndInsertUserList(newObjects); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, e.toString()); + } + return postResult; + } + + @Override + public POSTResultsReturn checkAndInsert(UserDTO newObject) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * @action fais les modifications des utilisateurs en base de données. Même organisation de code que pour les post + * @param updatedUsers + * @return + */ + private POSTResultsReturn checkAndUpdateUserList(List updatedUsers) throws SQLException, Exception { + //init result returned maps + List insertStatusList = new ArrayList<>(); + boolean allUsersAlreadyInDB = true; + POSTResultsReturn results = null; + + ArrayList users = new ArrayList<>(); + + //Logs pour la traçabilité + String log = getTraceabilityLogs(); + + //1. on récupère la liste des utilisateurs et on vérifie s'ils existent bien en BD + if (updatedUsers != null && !updatedUsers.isEmpty()) { + for (UserDTO userDTO : updatedUsers) { + User u = userDTO.createObjectFromDTO(); + if (existInDB(u)) { + users.add(u); + } else { + allUsersAlreadyInDB = false; + insertStatusList.add(new Status("Data error", StatusCodeMsg.ERR, "Unknown user")); + } + } + } + + //2. Modifications de la BD en fonction des données envoyées + if (allUsersAlreadyInDB) { //Si tous les users sont présents en BD, on peut continuer + PreparedStatement updatePreparedStatementUser = null; + PreparedStatement deletePreparedStatementUserGroup = null; + PreparedStatement insertPreparedStatementUserGroup = null; + PreparedStatement updatePreparedStatementUserPassword = null; + Connection connection = null; + + final String updateGabUser = "UPDATE \"users\" SET \"first_name\" = ?, \"family_name\" = ?, \"address\" = ?," + + " \"available\" = cast(? as boolean), \"phone\" = ?, " + + "\"affiliation\" = ?, \"isadmin\" = cast(? as boolean)" + + " WHERE \"email\" = ?"; + final String deleteGabUserGroup = "DELETE FROM \"at_group_users\" WHERE \"users_email\" = ?"; + final String insertGabUserGroup = "INSERT INTO \"at_group_users\" (\"group_uri\", \"users_email\")" + + " VALUES(?, ?)"; + + final String updateGabUSerPassword = "UPDATE \"users\" SET \"password\"= ? WHERE \"email\" = ?"; + try { + //Batch + int count = 0; + boolean insertionLeft = true; + + //Connection + préparation de la transaction + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + updatePreparedStatementUser = connection.prepareStatement(updateGabUser); + deletePreparedStatementUserGroup = connection.prepareStatement(deleteGabUserGroup); + insertPreparedStatementUserGroup = connection.prepareStatement(insertGabUserGroup); + updatePreparedStatementUserPassword = connection.prepareStatement(updateGabUSerPassword); + + for (User u : users) { + //Update des données de la table user + updatePreparedStatementUser.setString(1, u.getFirstName()); + updatePreparedStatementUser.setString(2, u.getFamilyName()); + updatePreparedStatementUser.setString(3, u.getAddress()); + if (u.getAvailable() != null) { + updatePreparedStatementUser.setString(4, u.getAvailable()); + } else { + updatePreparedStatementUser.setString(4, "f"); + } + updatePreparedStatementUser.setString(5, u.getPhone()); + updatePreparedStatementUser.setString(6, u.getAffiliation()); + if (u.getAdmin() != null) { + updatePreparedStatementUser.setString(7, u.getAdmin()); + } else { + updatePreparedStatementUser.setString(7, "f"); + } + updatePreparedStatementUser.setString(8, u.getEmail()); + updatePreparedStatementUser.execute(); + LOGGER.trace(log + " quert : " + updatePreparedStatementUser.toString()); + //Delete des liens user / group + deletePreparedStatementUserGroup.setString(1, u.getEmail()); + deletePreparedStatementUserGroup.execute(); + LOGGER.trace(log + " quert : " + deletePreparedStatementUserGroup.toString()); + + if (u.getPassword() != null && !u.getPassword().equals("")) { + updatePreparedStatementUserPassword.setString(1, u.getPassword()); + updatePreparedStatementUserPassword.setString(2, u.getEmail()); + updatePreparedStatementUserPassword.execute(); + LOGGER.trace(log + " quert : " + updatePreparedStatementUserPassword.toString()); + } + + //Insert des nouveaux liens users / email + if (u.getGroups() != null && !u.getGroups().isEmpty()) { + for (Group group : u.getGroups()) { + insertPreparedStatementUserGroup.setString(1, group.getUri()); + insertPreparedStatementUserGroup.setString(2, u.getEmail()); + insertPreparedStatementUserGroup.execute(); + LOGGER.trace(log + " quert : " + insertPreparedStatementUserGroup.toString()); + } + } + + //Insertion par batch + if (++count % batchSize == 0) { + updatePreparedStatementUser.executeBatch(); + deletePreparedStatementUserGroup.executeBatch(); + insertPreparedStatementUserGroup.executeBatch(); + updatePreparedStatementUserPassword.executeBatch(); + insertionLeft = false; + } + } + + if (insertionLeft) { + updatePreparedStatementUser.executeBatch(); + deletePreparedStatementUserGroup.executeBatch(); + insertPreparedStatementUserGroup.executeBatch(); + updatePreparedStatementUserPassword.executeBatch(); + } + + connection.commit(); //Envoi des données à la BD + + insertStatusList.add(new Status("Data updated", StatusCodeMsg.INFO, "users updated")); + results = new POSTResultsReturn(true, true, allUsersAlreadyInDB); + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + + //Rollback + if (connection != null) { + connection.rollback(); + } + + results = new POSTResultsReturn(false, true, allUsersAlreadyInDB); + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getMessage())); + if (e.getNextException() != null) { + insertStatusList.add(new Status("Error", StatusCodeMsg.ERRPG, e.getNextException().getMessage())); + } + results.statusList = insertStatusList; + } finally { + if (updatePreparedStatementUser != null) { + updatePreparedStatementUser.close(); + } + if (deletePreparedStatementUserGroup != null) { + deletePreparedStatementUserGroup.close(); + } + if (insertPreparedStatementUserGroup != null) { + insertPreparedStatementUserGroup.close(); + } + if (updatePreparedStatementUserPassword != null) { + updatePreparedStatementUserPassword.close(); + } + if (connection != null) { + connection.close(); + } + } + + } else { //Certains users ne sont pas déjà présents en BD. + results = new POSTResultsReturn(true, true, allUsersAlreadyInDB); + results.statusList = insertStatusList; + } + + return results; + } + + @Override + public POSTResultsReturn checkAndUpdateList(List newObjects) { + POSTResultsReturn postResult; + try { + postResult = this.checkAndUpdateUserList(newObjects); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + postResult = new POSTResultsReturn(false, Response.Status.INTERNAL_SERVER_ERROR, e.toString()); + } + return postResult; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/sesame/AgronomicalObjectDaoSesame.java b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/AgronomicalObjectDaoSesame.java new file mode 100644 index 000000000..928f9d4c2 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/AgronomicalObjectDaoSesame.java @@ -0,0 +1,529 @@ +//********************************************************************************************** +// AgronomicalObjectDaoSesame.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: august 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: September, 6 2017 +// Subject: A specific DAO to retreive data on agronomical objects +//*********************************************************************************************** + +package phis2ws.service.dao.sesame; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.query.Update; +import org.eclipse.rdf4j.repository.RepositoryException; +import org.eclipse.rdf4j.repository.http.HTTPRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.configuration.URINamespaces; +import phis2ws.service.dao.manager.DAOSesame; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.AgronomicalObjectDTO; +import phis2ws.service.resources.dto.LayerDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.ResourcesUtils; +import phis2ws.service.utils.sparql.SPARQLQueryBuilder; +import phis2ws.service.utils.sparql.SPARQLUpdateBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.AgronomicalObject; +import phis2ws.service.view.model.phis.Property; + + +public class AgronomicalObjectDaoSesame extends DAOSesame{ + + final static Logger LOGGER = LoggerFactory.getLogger(AgronomicalObjectDaoSesame.class); + + public String uri; + public String typeAgronomicalObject; + public String experiment; + public String alias; + + public AgronomicalObjectDaoSesame() { + super(); + } + + /** + * Vérifie si les agronomical objects sont corrects + * @param agronomicalObjectsDTO + * @return + * @throws RepositoryException + */ + public POSTResultsReturn check(List agronomicalObjectsDTO) throws RepositoryException { + //Résultats attendus + POSTResultsReturn agronomicalObjectsCheck = null; + //Liste des status retournés + List checkStatusList = new ArrayList<>(); + + boolean dataOk = true; + for (AgronomicalObjectDTO agronomicalObject : agronomicalObjectsDTO) { + //Vérification des agronomical objects + if ((boolean) agronomicalObject.isOk().get("state")) { //Données attendues reçues + //On vérifie que les types soient effectivement présents dans l'ontologie + URINamespaces uriNamespaces = new URINamespaces(); + if (!uriNamespaces.objectsPropertyContainsValue(agronomicalObject.getTypeAgronomicalObject())) { + dataOk = false; + checkStatusList.add(new Status("Wrong value", StatusCodeMsg.ERR, "Wrong agronomical object type value. See ontology")); + } + //SILEX:TODO + //Il faudra aussi faire une vérification sur les properties de l'ao : est-ce que les types sont + //bien présents dans l'ontologie ? Idem pour les relations + //\SILEX:TODO + } else { + // Format des données non attendu par rapport au schéma demandé + dataOk = false; + agronomicalObject.isOk().remove("state"); + checkStatusList.add(new Status("Bad data format", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.MISSINGFIELDS).append(agronomicalObject.isOk()).toString())); + } + } + agronomicalObjectsCheck = new POSTResultsReturn(dataOk, null, dataOk); + agronomicalObjectsCheck.statusList = checkStatusList; + return agronomicalObjectsCheck; + } + + /** + * insère les données dans le triplestore. + * On suppose que la vérification de leur intégrité a été fait auparavent via l'appel à la méthode check() + * @param agronomicalObjects + * @return + */ + public POSTResultsReturn insert(List agronomicalObjects) { + List insertStatusList = new ArrayList<>(); + List createdResourcesURIList = new ArrayList<>(); + + POSTResultsReturn results; + + boolean resultState = false; //Pour savoir si les données sont bonnes et ont bien été insérées + boolean annotationInsert = true; // Si l'insertion a bien été effectuée + + final Iterator iteratorAgronomicalObjects = agronomicalObjects.iterator(); + + while (iteratorAgronomicalObjects.hasNext() && annotationInsert) { + AgronomicalObject agronomicalObject = iteratorAgronomicalObjects.next(); + + //Enregistrement triplestore + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLUpdateBuilder spqlInsert = new SPARQLUpdateBuilder(); + + String graphURI = agronomicalObject.getUriExperiment() != null ? agronomicalObject.getUriExperiment() + : uriNamespaces.getContextsProperty("agronomicalObjects"); + spqlInsert.appendGraphURI(graphURI); + spqlInsert.appendTriplet(agronomicalObject.getUri(), "rdf:type", agronomicalObject.getTypeAgronomicalObject(), null); + + //Propriétés associées à l'AO + for (Property property : agronomicalObject.getProperties()) { + if (property.getTypeProperty() != null && !property.getTypeProperty().equals("")) {//Propriété typée + if (property.getTypeProperty().equals(uriNamespaces.getObjectsProperty("cVariety"))) { + //On génère l'uri de la variété + String propertyURI = uriNamespaces.getContextsProperty("pxPlatform") + "/v/" + property.getValue().toLowerCase(); + spqlInsert.appendTriplet(propertyURI, "rdf:type", property.getTypeProperty(), null); + spqlInsert.appendTriplet(agronomicalObject.getUri(), property.getRelation(), propertyURI, null); + } else { + spqlInsert.appendTriplet(property.getValue(), "rdf:type", property.getTypeProperty(), null); + spqlInsert.appendTriplet(agronomicalObject.getUri(), property.getRelation(), property.getValue(), null); + } + } else { + spqlInsert.appendTriplet(agronomicalObject.getUri(), property.getRelation(), "\"" + property.getValue() + "\"", null); + } + } + + if (agronomicalObject.getUriExperiment() != null) { + spqlInsert.appendTriplet(agronomicalObject.getUriExperiment(),uriNamespaces.getRelationsProperty("rHasPlot"), agronomicalObject.getUri(), null); + } + try { + //SILEX:test + //Toute la notion de connexion au triplestore sera à revoir. + //C'est un hot fix qui n'est pas propre + String sesameServer = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "sesameServer"); + String repositoryID = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "repositoryID"); + rep = new HTTPRepository(sesameServer, repositoryID); //Stockage triplestore Sesame + rep.initialize(); + this.setConnection(rep.getConnection()); + this.getConnection().begin(); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, spqlInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareUpdate.execute(); + //\SILEX:test + createdResourcesURIList.add(agronomicalObject.getUri()); + + + if (annotationInsert) { + resultState = true; + + this.getConnection().commit(); + } else { + // retour en arrière sur la transaction + this.getConnection().rollback(); + } + this.getConnection().close(); + } catch (RepositoryException ex) { + LOGGER.error("Error during commit or rolleback Triplestore statements: ", ex); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationInsert = false; + insertStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed insertion query: " + e.getMessage())); + } +// finally { +// if (this.getConnection() != null) { +// this.getConnection().close(); +// } +// } + } + + results = new POSTResultsReturn(resultState, annotationInsert, true); + results.statusList = insertStatusList; + if (resultState && !createdResourcesURIList.isEmpty()) { + results.createdResources = createdResourcesURIList; + results.statusList.add(new Status("Resources created", StatusCodeMsg.INFO, createdResourcesURIList.size() + " new resource(s) created.")); + } + + return results; + } + + /** + * vérifie les données et les insère dans le triplestore + * @param agronomicalObjects + * @param agronomicalObjectsDTO + * @return + */ + public POSTResultsReturn checkAndInsert(List agronomicalObjects, List agronomicalObjectsDTO) { + POSTResultsReturn checkResult = check(agronomicalObjectsDTO); + if (checkResult.statusList == null) { //Les données ne sont pas bonnes + return checkResult; + } else { //Si les données sont bonnes + return insert(agronomicalObjects); + } + } + + /** + * + * @param experimentURI + * @return la requête permettant de récupérer la liste des plots de l'experimentation ainsi que leurs propriétés + */ + private SPARQLQueryBuilder prepareSearchExperimentPlots(String experimentURI) { + URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder sparqlQuery = new SPARQLQueryBuilder(); + sparqlQuery.appendDistinct(true); + sparqlQuery.appendGraph(experimentURI); + sparqlQuery.appendSelect("?child ?type ?property ?propertyRelation ?propertyType"); + + sparqlQuery.appendTriplet(experimentURI, uriNamespaces.getRelationsProperty("rHasPlot"), "?child", null); + sparqlQuery.appendTriplet("?child", "rdf:type", "?type", null); + sparqlQuery.appendTriplet("?child", "?propertyRelation", "?property", null); + + sparqlQuery.beginBodyOptional(); + sparqlQuery.appendToBody("?property rdf:type ?propertyType"); + sparqlQuery.endBodyOptional(); + + LOGGER.trace("sparql select query : " + sparqlQuery.toString()); + + return sparqlQuery; + } + + /** + * + * @param objectURI + * @return la requête permettant d'avoir tous les éléments contenus (geo:contains) dans objectURI. + * la requête permet d'avoir la liste de tous les descendants + */ + private SPARQLQueryBuilder prepareSearchChildrenWithContains(String objectURI, String objectType) { + URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder sparqlQuery = new SPARQLQueryBuilder(); + sparqlQuery.appendDistinct(true); + sparqlQuery.appendPrefix("geo", uriNamespaces.getContextsProperty("pxGeoSPARQL")); + if (objectType.equals(uriNamespaces.getObjectsProperty("cExperiment"))) { + sparqlQuery.appendGraph(objectURI); + } + sparqlQuery.appendSelect("?child ?type"); + sparqlQuery.appendTriplet(objectURI, "geo:contains*", "?child", null); + sparqlQuery.appendTriplet("?child", "rdf:type", "?type", null); + + LOGGER.debug("sparql select query : " + sparqlQuery.toString()); + + return sparqlQuery; + } + + /** + * + * @param objectURI + * @return La liste des premiers enfants de l'entités (relation geo:contains) + */ + private SPARQLQueryBuilder prepareSearchFirstChildrenWithContains(String objectURI) { + URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder sparqlQuery = new SPARQLQueryBuilder(); + sparqlQuery.appendDistinct(true); + sparqlQuery.appendPrefix("geo", uriNamespaces.getContextsProperty("pxGeoSPARQL")); + sparqlQuery.appendSelect("?child ?type"); + sparqlQuery.appendTriplet(objectURI, "geo:contains", "?child", null); + sparqlQuery.appendTriplet("?child", "rdf:type", "?type", null); + + + LOGGER.trace("sparql select query : " + sparqlQuery.toString()); + + return sparqlQuery; + } + + /** + * + * @param layerDTO + * @return la liste des enfants de layerDTO.getObjectURI. Retourne tous les + * descendants si layerDTO.getDepth == true. (clé : uri, valeur : type) + */ + public HashMap searchChildren(LayerDTO layerDTO) { + HashMap children = new HashMap<>(); // uri (clé), type (valeur) + URINamespaces uriNamespaces = new URINamespaces(); + + //Si c'est une expérimentation, le nom du lien n'est pas le même donc, + //on commence par récupérer la liste des enfants directs + if (layerDTO.getObjectType().equals(uriNamespaces.getObjectsProperty("cExperiment"))) { + //SILEX:test + //Pour les soucis de pool de connexion + rep = new HTTPRepository(SESAME_SERVER, REPOSITORY_ID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + //\SILEX:test + + SPARQLQueryBuilder sparqlQuery = prepareSearchExperimentPlots(layerDTO.getObjectUri()); + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, sparqlQuery.toString()); + + TupleQueryResult result = tupleQuery.evaluate(); + + //SILEX:test + //Pour les soucis de pool de connexion + getConnection().close(); + //\SILEX:test + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + AgronomicalObject agronomicalObject = children.get(bindingSet.getValue("child").stringValue()); + if (agronomicalObject != null) { //Il suffit juste de lui ajouter la propriété. + Property property = new Property(); + property.setValue(bindingSet.getValue("property").stringValue()); + property.setRelation(bindingSet.getValue("propertyRelation").stringValue()); + if (bindingSet.getValue("propertyType") != null) { + property.setTypeProperty(bindingSet.getValue("propertyType").stringValue()); + } + + agronomicalObject.addProperty(property); + } else { //Il n'est pas encore dans la liste, il faut le rajouter + agronomicalObject = new AgronomicalObject(); + agronomicalObject.setUri(bindingSet.getValue("child").stringValue()); + agronomicalObject.setTypeAgronomicalObject(bindingSet.getValue("type").stringValue()); + + Property property = new Property(); + property.setValue(bindingSet.getValue("property").stringValue()); + property.setRelation(bindingSet.getValue("propertyRelation").stringValue()); + if (bindingSet.getValue("propertyType") != null) { + property.setTypeProperty(bindingSet.getValue("propertyType").stringValue()); + } + agronomicalObject.addProperty(property); + } + + children.put(agronomicalObject.getUri(), agronomicalObject); + } + } + + //SILEX:INFO + //Pour l'instant, on ne récupère que les propriétés des AO de type plot, les premiers descendants de l'expérimentation. + //Pas les autres + //Si il faut aussi tous les descendants + if (ResourcesUtils.getStringBooleanValue(layerDTO.getDepth())) { + //Si c'est les descendants d'un essai, il y a un traitement particulier + if (layerDTO.getObjectType().equals(uriNamespaces.getObjectsProperty("cExperiment"))) { + //On recherche tous les fils des plots de l'experimentation, récupérés précédemment + //SILEX:test + //Pour les soucis de pool de connexion + rep = new HTTPRepository(SESAME_SERVER, REPOSITORY_ID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + //\SILEX:test + for (Entry child : children.entrySet()) { + + SPARQLQueryBuilder sparqlQuery = prepareSearchChildrenWithContains(child.getKey(), child.getValue().getTypeAgronomicalObject()); + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, sparqlQuery.toString()); + TupleQueryResult result = tupleQuery.evaluate(); + + + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + if (!children.containsKey(bindingSet.getValue("child").stringValue())) { + AgronomicalObject agronomicalObject = new AgronomicalObject(); + agronomicalObject.setUri(bindingSet.getValue("child").stringValue()); + agronomicalObject.setTypeAgronomicalObject(bindingSet.getValue("type").stringValue()); + + children.put(bindingSet.getValue("child").stringValue(), agronomicalObject); + } + } + } + //SILEX:test + //Pour les soucis de pool de connexion + getConnection().close(); + //\SILEX:test + } else { //Si c'est un objet classique + //SILEX:test + //Pour les soucis de pool de connexion + rep = new HTTPRepository(SESAME_SERVER, REPOSITORY_ID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + //\SILEX:test + SPARQLQueryBuilder sparqlQuery = prepareSearchChildrenWithContains(layerDTO.getObjectUri(), layerDTO.getObjectType()); + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, sparqlQuery.toString()); + TupleQueryResult result = tupleQuery.evaluate(); + //SILEX:test + //Pour les soucis de pool de connexion + getConnection().close(); + //\SILEX:test + + + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + AgronomicalObject agronomicalObject = new AgronomicalObject(); + agronomicalObject.setUri(bindingSet.getValue("child").stringValue()); + agronomicalObject.setTypeAgronomicalObject(bindingSet.getValue("type").stringValue()); + + children.put(bindingSet.getValue("child").stringValue(), agronomicalObject); + } + } + + } else if (!layerDTO.getObjectType().equals(uriNamespaces.getObjectsProperty("cExperiment"))) { //S'il ne faut que les enfants directs et que ce n'est pas une expérimentation + //SILEX:test + //Pour les soucis de pool de connexion + rep = new HTTPRepository(SESAME_SERVER, REPOSITORY_ID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + //\SILEX:test + SPARQLQueryBuilder sparqlQuery = prepareSearchFirstChildrenWithContains(layerDTO.getObjectUri()); + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, sparqlQuery.toString()); + TupleQueryResult result = tupleQuery.evaluate(); + //SILEX:test + //Pour les soucis de pool de connexion + getConnection().close(); + //\SILEX:test + + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + AgronomicalObject agronomicalObject = new AgronomicalObject(); + agronomicalObject.setUri(bindingSet.getValue("child").stringValue()); + agronomicalObject.setTypeAgronomicalObject(bindingSet.getValue("type").stringValue()); + + children.put(bindingSet.getValue("child").stringValue(), agronomicalObject); + } + } + //\SILEX:INFO + + return children; + } + + /** + * + * @return liste d'objets agronomiques, résultat de la recherche, vide si pas de résultats + */ + public ArrayList allPaginate() { + SPARQLQueryBuilder sparqlQuery = prepareSearchQuery(); + + //SILEX:test + //Pour les soucis de pool de connexion + rep = new HTTPRepository(SESAME_SERVER, REPOSITORY_ID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + //\SILEX:test + + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, sparqlQuery.toString()); + ArrayList agronomicalObjects = new ArrayList<>(); + + try (TupleQueryResult result = tupleQuery.evaluate()) { + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + AgronomicalObject agronomicalObject = new AgronomicalObject(); + + if (uri != null) { + agronomicalObject.setUri(uri); + } else { + agronomicalObject.setUri(bindingSet.getValue("uri").stringValue()); + } + + if (experiment != null) { + agronomicalObject.setUriExperiment(experiment); + } else { + agronomicalObject.setUriExperiment(bindingSet.getValue("experimentURI").stringValue()); + } + + if (alias != null) { + agronomicalObject.setAlias(alias); + } else { + agronomicalObject.setAlias(bindingSet.getValue("alias").stringValue()); + } + + URINamespaces uriNamespaces = new URINamespaces(); + agronomicalObject.setTypeAgronomicalObject(uriNamespaces.getObjectsProperty("cPlot")); + + + agronomicalObjects.add(agronomicalObject); + } + } + + //SILEX:test + //Pour les soucis de pool de connexion + getConnection().close(); + //\SILEX:test + + return agronomicalObjects; + } + + @Override + protected SPARQLQueryBuilder prepareSearchQuery() { + //SILEX:INFO + //- il faudra par la suite pouvoir avoir plusieurs ao appartenant à une xp sans faire en fonction du type + //- il faudra ajouter les propriétés de l'objet agronomique + //\SILEX:INFO + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder sparqlQuery = new SPARQLQueryBuilder(); + + sparqlQuery.appendDistinct(true); + String agronomicalObjectURI; + + if (uri != null ) { + agronomicalObjectURI = "<" + uri + ">"; + } else { + agronomicalObjectURI = "?uri"; + sparqlQuery.appendSelect(" ?uri"); + } + + if (experiment != null) { + sparqlQuery.appendGraph(experiment); + } else { + sparqlQuery.appendSelect(" ?experimentURI"); + sparqlQuery.appendTriplet("?experimentURI", uriNamespaces.getRelationsProperty("rHasPlot"), agronomicalObjectURI, null); + } + + if (alias != null) { + sparqlQuery.appendTriplet(agronomicalObjectURI, uriNamespaces.getRelationsProperty("rHasAlias"), "\"" + alias + "\"", null); + } else { + sparqlQuery.appendSelect(" ?alias"); + sparqlQuery.appendTriplet(agronomicalObjectURI, uriNamespaces.getRelationsProperty("rHasAlias"), "?alias", null); + } + + sparqlQuery.appendTriplet(agronomicalObjectURI, "rdf:type", uriNamespaces.getObjectsProperty("cPlot"), null); + + LOGGER.trace("sparql select query : " + sparqlQuery.toString()); + + return sparqlQuery; + } + + @Override + public Integer count() throws RepositoryException, MalformedQueryException, QueryEvaluationException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/sesame/DocumentDaoSesame.java b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/DocumentDaoSesame.java new file mode 100644 index 000000000..9f78690de --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/DocumentDaoSesame.java @@ -0,0 +1,687 @@ +//********************************************************************************************** +// DocumentsDaoSesame.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 12 2017 (Ajout status sur les documents (linked/unlinked) +// Subject: A Dao specific to documents insert into triplestore +//*********************************************************************************************** + +// /!\ Suite à la maj de la semaine du 12 juin 2017, +// des insertions sont faites dans le triplestore (metadonnées) ET dans mongodb (document - Utilisation de DocumentDaoMongo) +// il faudra renommer la classe... + +//SILEX:conception +// Si l'objet concerné par le document n'existe pas dans le triplestore, on n'ajoute pas de triplet +// avec son type (element rdf:type elementType). C'est plus souple mais cela sera sûrement à modifier par la suite +//\SILEX:conception +package phis2ws.service.dao.sesame; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.query.Update; +import org.eclipse.rdf4j.repository.RepositoryException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.configuration.URINamespaces; +import phis2ws.service.dao.manager.DAOSesame; +import phis2ws.service.dao.mongo.DocumentDaoMongo; +import phis2ws.service.dao.phis.ExperimentDao; +import phis2ws.service.dao.phis.UserDaoPhisBrapi; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.model.User; +import phis2ws.service.resources.dto.ConcernItemDTO; +import phis2ws.service.resources.dto.DocumentMetadataDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.ResourcesUtils; +import phis2ws.service.utils.sparql.SPARQLQueryBuilder; +import phis2ws.service.utils.sparql.SPARQLUpdateBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.Document; +import phis2ws.service.view.model.phis.Experiment; + +public class DocumentDaoSesame extends DAOSesame { + final static Logger LOGGER = LoggerFactory.getLogger(DocumentDaoSesame.class); + public String uri; + public String documentType; + public String creator; + public String language; + public String title; + public String creationDate; + public String format; + public String comment; + public List concernedItemsUris = new ArrayList<>(); + public String status; + + public DocumentDaoSesame() { + super(); // Repository + resourceType = "documents"; // type de la ressource + } + + /** + * Verifie si les metadonnes du document sont correctes + * @param docsMetadata + * @return + */ + public POSTResultsReturn check(List docsMetadata) throws RepositoryException { + // Résultats attendus + POSTResultsReturn docsMetadataCheck = null; + // list des statuts retournés + List insertStatusList = new ArrayList<>(); // Echec ou Info + + boolean dataOk = true; + for (DocumentMetadataDTO documentMetadata : docsMetadata) { + // Vérification des docsMetadata + if ((boolean) documentMetadata.isOk().get("state")) { // Données attendues reçues + //1. Récupération des types de documents de l'ontologie + ArrayList documentsSchemasUri = null; + try { + documentsSchemasUri = getDocumentsTypes(); + } catch (RepositoryException | MalformedQueryException | QueryEvaluationException ex) { + LOGGER.error(ex.getMessage(), ex); + } + + //2. Vérification du type du document + if (documentsSchemasUri != null && !documentsSchemasUri.contains(documentMetadata.getDocumentType())) { + dataOk = false; + insertStatusList.add(new Status("Wrong value", StatusCodeMsg.ERR, "Wrong document type value. Authorized document type values : " + documentsSchemasUri.toString())); + } + + //3. Vérification du status (doit être égal à "linked" ou "unlinked") + if (!(documentMetadata.getStatus().equals("linked") || documentMetadata.getStatus().equals("unlinked"))) { + dataOk = false; + insertStatusList.add(new Status("Wrong value", StatusCodeMsg.ERR, + "Wrong status value given : " + documentMetadata.getStatus() + ". Expected : \"linked\" or \"unlinked\"" )); + } + } else { + // Format des données non attendu par rapport au schéma demandé + dataOk = false; + documentMetadata.isOk().remove("state"); + insertStatusList.add(new Status("Bad data format", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.MISSINGFIELDS).append(documentMetadata.isOk()).toString())); + } + } + docsMetadataCheck = new POSTResultsReturn(dataOk, null, dataOk); + docsMetadataCheck.statusList = insertStatusList; + return docsMetadataCheck; + } + + /** + * + * @param filePath le chemin du fichier à enregistrer en mongodb + * @return true si le document a été enregistré dans mongo + * false sinon + */ + private POSTResultsReturn saveFileInMongoDB(String filePath, String fileURI) { + DocumentDaoMongo documentDaoMongo = new DocumentDaoMongo(); + return documentDaoMongo.insertFile(filePath, fileURI); + } + + public POSTResultsReturn insert(List listAnnotations) { + // nom du document + String name = null; + // list des statuts retournés + List insertStatusList = new ArrayList<>(); // Failed or Info + List createdResourcesURIList = new ArrayList<>(); // Failed or Info + + POSTResultsReturn results = null; + + boolean resultState = false; // Pour savoir si les données étaient bonnes et on bien été insérées + + boolean docsMetadataState = true; // Si toutes les données étaient bonnes ok ! + boolean AnnotationInsert = true; // Si l'insertion a bien été réalisée + + final Iterator itAnot = listAnnotations.iterator(); + + while (itAnot.hasNext() && AnnotationInsert) { + DocumentMetadataDTO annotObject = itAnot.next(); + //1. Enreg dans mongoDB du document + + //Uri du document (en évitant les doublons de noms) + //SILEX:conception + final URINamespaces uriNS = new URINamespaces(); + boolean nameExist = true; + final String uniqueID = ResourcesUtils.getUniqueID(); + final String documents = uriNS.getContextsProperty("documents"); + while (nameExist) { + name = new StringBuilder("document").append(uniqueID).toString(); + try { + nameExist = exist(documents + "/" + name, null, null); + } catch (RepositoryException ex) { + LOGGER.error("Triplestore access error. ", ex); + insertStatusList.add(new Status("Triplestore access error", StatusCodeMsg.ERR, "Triplestore access error : " + ex.getMessage())); + break; + } catch (MalformedQueryException | QueryEvaluationException ex) { + LOGGER.error(ex.getMessage(), ex); + break; + } + } + final String documentName = documents + "/" + name; + //\SILEX:conception + + POSTResultsReturn saveFileResult = saveFileInMongoDB(annotObject.getServerFilePath(), documentName); + insertStatusList.addAll(saveFileResult.statusList); + + if (saveFileResult.getResultState()) { + //2. Enreg du rdf associé au document + Map anotOk = annotObject.isOk(); //Vérification de la structure + + docsMetadataState = (boolean) anotOk.get("state"); + //SILEX:conception + // C'est ici qu'il faudrait faire l'ajout du triplet correspondant à l'entité concernée + // par le document s'il n'existe pas + //\SILEX:conception + + if (docsMetadataState) { + //3. Insertion des métadonnées dans le triplestore + SPARQLUpdateBuilder spqlInsert = new SPARQLUpdateBuilder(); + spqlInsert.appendPrefix("dc", uriNS.getContextsProperty("pxDublinCore")); + + spqlInsert.appendGraphURI(uriNS.getContextsProperty("documents")); //Le document est mis dans un graphe nommé + spqlInsert.appendTriplet(documentName, "rdf:type", annotObject.getDocumentType(), null); + spqlInsert.appendTriplet(documentName, "dc:creator", "\"" + annotObject.getCreator() + "\"", null); + spqlInsert.appendTriplet(documentName, "dc:language", "\"" + annotObject.getLanguage() + "\"", null); + spqlInsert.appendTriplet(documentName, "dc:title", "\"" + annotObject.getTitle() + "\"", null); + spqlInsert.appendTriplet(documentName, "dc:date", "\"" + annotObject.getCreationDate() + "\"", null); + spqlInsert.appendTriplet(documentName, "dc:format", "\"" + annotObject.getExtension() + "\"", null); + spqlInsert.appendTriplet(documentName, uriNS.getRelationsProperty("rStatus"), "\"" + annotObject.getStatus() + "\"", null); + + if (annotObject.getComment() != null) { + spqlInsert.appendTriplet(documentName, "rdfs:comment", "\"" + annotObject.getComment() + "\"", null); + } + + if (!(annotObject.getConcern() == null) && !annotObject.getConcern().isEmpty()) { + for (ConcernItemDTO concernedItem : annotObject.getConcern()) { + spqlInsert.appendTriplet(documentName, uriNS.getRelationsProperty("rConcern"), concernedItem.getUri(), null); + spqlInsert.appendTriplet(concernedItem.getUri(),"rdf:type", concernedItem.getTypeURI(), null); + } + } + + try { + // début de la transaction : vérification de la requête + this.getConnection().begin(); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, spqlInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareUpdate.execute(); + + createdResourcesURIList.add(documentName); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + AnnotationInsert = false; + insertStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed insertion query: " + e.getMessage())); + } + } else { + // JSON malformé de quelque sorte que ce soit + docsMetadataState = false; + anotOk.remove("state"); + insertStatusList.add(new Status("Missing field error", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.MISSINGFIELDS).append(anotOk).toString())); + } + + // JSON bien formé et pas de problème avant l'insertion + if (AnnotationInsert && docsMetadataState) { + resultState = true; + try { + this.getConnection().commit(); + } catch (RepositoryException ex) { + LOGGER.error("Error during commit Triplestore statements: ", ex); + } + } else { + // retour en arrière sur la transaction + try { + this.getConnection().rollback(); + } catch (RepositoryException ex) { + LOGGER.error("Error during rollback Triplestore statements : ", ex); + } + } + } + } + + results = new POSTResultsReturn(resultState, AnnotationInsert, docsMetadataState); + results.statusList = insertStatusList; + if (resultState && !createdResourcesURIList.isEmpty()) { + results.createdResources = createdResourcesURIList; + results.statusList.add(new Status("Resources created", StatusCodeMsg.INFO, createdResourcesURIList.size() + " new resource(s) created.")); + } + + return results; + } + /** + * Retourne les types de documents disponibles + * @return List de concepts de document + * @throws RepositoryException + * @throws MalformedQueryException + * @throws QueryEvaluationException + */ + public ArrayList getDocumentsTypes() throws RepositoryException, MalformedQueryException, QueryEvaluationException { + URINamespaces uriNS = new URINamespaces(); + ArrayList documentsSchemasUri = new ArrayList<>(); + // phis query + SPARQLQueryBuilder sparqlQ = new SPARQLQueryBuilder(); + sparqlQ.appendDistinct(true); + sparqlQ.appendSelect("?documentType"); + sparqlQ.appendTriplet("?documentType", "rdfs:subClassOf*", uriNS.getObjectsProperty("cDocuments"), null); + sparqlQ.appendFilter("?documentType != <" + uriNS.getObjectsProperty("cDocuments") +">"); + LOGGER.trace(sparqlQ.toString()); + TupleQuery tupleQueryTo = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, sparqlQ.toString()); + + try (TupleQueryResult resultTo = tupleQueryTo.evaluate()) { + while (resultTo.hasNext()) { + BindingSet bindingSet = resultTo.next(); + if (bindingSet.getValue("documentType") != null) { + documentsSchemasUri.add(bindingSet.getValue("documentType").stringValue()); + } + } + } + return documentsSchemasUri; + } + + @Override + protected SPARQLQueryBuilder prepareSearchQuery() { + final URINamespaces uriNS = new URINamespaces(); + SPARQLQueryBuilder sparqlQuery = new SPARQLQueryBuilder(); + sparqlQuery.appendPrefix("dc", uriNS.getContextsProperty("pxDublinCore")); + sparqlQuery.appendDistinct(true); + sparqlQuery.appendGraph(uriNS.getContextsProperty("documents")); + String select; + if (uri != null) { + select = "<" + uri + ">"; + sparqlQuery.appendSelect(""); + } else { + select = "?documentUri"; + sparqlQuery.appendSelect(select); + } + + if (documentType != null) { + sparqlQuery.appendTriplet(select, "rdf:type", documentType, null); + } else { + sparqlQuery.appendSelect(" ?documentType"); + sparqlQuery.appendTriplet(select, "rdf:type", "?documentType", null); + } + + if (creator != null) { + sparqlQuery.appendTriplet(select, "dc:creator", "\"" + creator + "\"", null); + } else { + sparqlQuery.appendSelect(" ?creator"); + sparqlQuery.appendTriplet(select, "dc:creator", "?creator", null); + } + + if (language != null) { + sparqlQuery.appendTriplet(select, "dc:language", "\"" + language + "\"", null); + } else { + sparqlQuery.appendSelect(" ?language"); + sparqlQuery.appendTriplet(select, "dc:language", "?language", null); + } + + if (title != null) { + sparqlQuery.appendTriplet(select, "dc:title", "\"" + title + "\"", null); + } else { + sparqlQuery.appendSelect(" ?title"); + sparqlQuery.appendTriplet(select, "dc:title", "?title", null); + } + + if (creationDate != null) { + sparqlQuery.appendTriplet(select, "dc:date", "\"" + creationDate + "\"", null); + } else { + sparqlQuery.appendSelect(" ?date"); + sparqlQuery.appendTriplet(select, "dc:date", "?date", null); + } + + if (format != null) { + sparqlQuery.appendTriplet(select, "dc:format", "\"" + format + "\"", null); + } else { + sparqlQuery.appendSelect(" ?format"); + sparqlQuery.appendTriplet(select, "dc:format", "?format", null); + } + + if (!concernedItemsUris.isEmpty() && concernedItemsUris.size() > 0) { + for (String concernedItemUri : concernedItemsUris) { + sparqlQuery.appendTriplet(select, uriNS.getRelationsProperty("rConcern"), concernedItemUri, null); + } + } + + if (status != null) { + sparqlQuery.appendTriplet(select, uriNS.getRelationsProperty("rStatus"), "\"" + status + "\"", null); + } else { + sparqlQuery.appendSelect(" ?status"); + sparqlQuery.appendTriplet(select, uriNS.getRelationsProperty("rStatus"), "?status", null); + } + + //else { +// sparqlQuery.appendSelect(" ?concern"); +// sparqlQuery.appendTriplet(select, uriNS.getRelationsProperty("rConcern"), "?concern", null); +// } + LOGGER.trace("sparql select query : " + sparqlQuery.toString()); + + + return sparqlQuery; + } + + /** + * + * @param uriDocument + * @return la requête permettant de récupérer le commentaire du document + */ + private SPARQLQueryBuilder prepareSearchCommentQuery(String uriDocument) { + final URINamespaces uriNS = new URINamespaces(); + SPARQLQueryBuilder sparqlQuery = new SPARQLQueryBuilder(); + sparqlQuery.appendDistinct(true); + sparqlQuery.appendGraph(uriNS.getContextsProperty("documents")); + sparqlQuery.appendSelect("?comment"); + sparqlQuery.appendTriplet(uriDocument, "rdfs:comment", "?comment", null); + + LOGGER.trace("sparql select query : " + sparqlQuery.toString()); + return sparqlQuery; + } + + /** + * + * @param uriDocument + * @return la requête permettant de lister les entités liées au document (dans le triplestore) + */ + private SPARQLQueryBuilder prepareSearchConcernQuery(String uriDocument) { + final URINamespaces uriNS = new URINamespaces(); + SPARQLQueryBuilder sparqlQuery = new SPARQLQueryBuilder(); + sparqlQuery.appendDistinct(true); + sparqlQuery.appendGraph(uriNS.getContextsProperty("documents")); + + sparqlQuery.appendSelect(" ?concern ?typeConcern"); + sparqlQuery.appendTriplet(uriDocument, uriNS.getRelationsProperty("rConcern"), "?concern", null); + sparqlQuery.appendTriplet("?concern", "rdf:type", "?typeConcern", null); + + LOGGER.trace("sparql select query : " + sparqlQuery.toString()); + + return sparqlQuery; + } + + @Override + public Integer count() throws RepositoryException, MalformedQueryException, QueryEvaluationException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * + * @param u + * @param document + * @return true si l'utilisateur peut voir le document, false sinon + */ + private boolean canUserSeeDocument(User u, Document document) { + UserDaoPhisBrapi userDao = new UserDaoPhisBrapi(); + userDao.isAdmin(u); + if (u.getAdmin().equals("t") || u.getAdmin().equals("true")) { + return true; + } else { + ExperimentDao experimentDao = new ExperimentDao(); + URINamespaces uriNs = new URINamespaces(); + for (ConcernItemDTO concernItem : document.getConcernedItems()) { + if (concernItem.getTypeURI().equals(uriNs.getContextsProperty("pVoc2017") + "#Experiment")) { + Experiment experiment = new Experiment(concernItem.getUri()); + + if (experimentDao.canUserSeeExperiment(u, experiment)) { + return true; + } + } + } + + return false; + } + } + + /** + * + * @return liste de documents, résultat de la recherche, vide si pas de résultats. + */ + public ArrayList allPaginate() { + SPARQLQueryBuilder sparqlQuery = prepareSearchQuery(); + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, sparqlQuery.toString()); + ArrayList documents = new ArrayList<>(); + + try (TupleQueryResult result = tupleQuery.evaluate()) { + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + Document document = new Document(); + + if (uri != null) { + document.setUri(uri); + } else { + document.setUri(bindingSet.getValue("documentUri").stringValue()); + } + + if (documentType != null) { + document.setDocumentType(documentType); + } else { + document.setDocumentType(bindingSet.getValue("documentType").stringValue()); + } + + if (creator != null) { + document.setCreator(creator); + } else { + document.setCreator(bindingSet.getValue("creator").stringValue()); + } + + if (language != null) { + document.setLanguage(language); + } else { + document.setLanguage(bindingSet.getValue("language").stringValue()); + } + + if (title != null) { + document.setTitle(title); + } else { + document.setTitle(bindingSet.getValue("title").stringValue()); + } + + if (creationDate != null) { + document.setCreationDate(creationDate); + } else { + document.setCreationDate(bindingSet.getValue("date").stringValue()); + } + + if (format != null) { + document.setFormat(format); + } else { + document.setFormat(bindingSet.getValue("format").stringValue()); + } + + if (status != null) { + document.setStatus(status); + } else { + document.setStatus(bindingSet.getValue("status").stringValue()); + } + + //Si l'utilisateur a bien les droits d'accès sur le document, on récupère les informations manquantes et on ajoute le document + + + //Après avoir récupéré les métadonnées obligatoires associées au document, + //on regarde s'il a un rdfs:comment + SPARQLQueryBuilder sparqlQueryComment = prepareSearchCommentQuery(document.getUri()); + TupleQuery tupleQueryComment = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, sparqlQueryComment.toString()); + TupleQueryResult resultComment = tupleQueryComment.evaluate(); + while (resultComment.hasNext()) { + BindingSet bindingSetComment = resultComment.next(); + if (bindingSetComment.getValue("comment") != null) { + document.setComment(bindingSetComment.getValue("comment").stringValue()); + } + } + + //Après avoir récupéré les métadonnées associées au document, on regarde dans + //le triplestore si le document n'est pas rattaché à des éléments + SPARQLQueryBuilder sparqlQueryConcern = prepareSearchConcernQuery(document.getUri()); + TupleQuery tupleQueryConcern = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, sparqlQueryConcern.toString()); + TupleQueryResult resultConcern = tupleQueryConcern.evaluate(); + while (resultConcern.hasNext()) { + BindingSet bindingSetConcern = resultConcern.next(); + if (bindingSetConcern.getValue("concern") != null) { + ConcernItemDTO concernedItem = new ConcernItemDTO(); + concernedItem.setTypeURI(bindingSetConcern.getValue("typeConcern").stringValue()); + concernedItem.setUri(bindingSetConcern.getValue("concern").stringValue()); + document.addConcernedItem(concernedItem); + } + } + + if (canUserSeeDocument(user, document)) { + documents.add(document); + } + } + + } + + return documents; + } + + //SILEX:todo + //Faire une méthode commune de check des données pour l'insert et le update + //\SILEX:todo + private POSTResultsReturn checkAndUpdateDocumentsMetadataList(List documentsMetadata) { + + // list des statuts retournés + List updateStatusList = new ArrayList<>(); // Failed or Info + List updatedResourcesURIList = new ArrayList<>(); // Failed or Info + POSTResultsReturn results; + + boolean annotationUpdate = true; // Si l'insertion a bien été réalisée + boolean docsMetadataState = true; + boolean resultState = false; // Pour savoir si les données étaient bonnes et on bien été mises à jour + + for (DocumentMetadataDTO documentMetadata : documentsMetadata) { + + //Vérification que les données fournies soient les bonnes + Map metadataOk = documentMetadata.isOk(); + boolean documentMetadataState = (boolean) metadataOk.get("state"); + + if (!(documentMetadata.getStatus().equals("linked") || documentMetadata.getStatus().equals("unlinked"))) { + documentMetadataState = false; + updateStatusList.add(new Status("Wrong value", StatusCodeMsg.ERR, + "Wrong status value given : " + documentMetadata.getStatus() + ". Expected : \"linked\" or \"unlinked\"" )); + } + + if (documentMetadataState) { + //1.Suppression des métadonnées actuellement présente + //1.1 Récupération des infos qui seront modifiées (pour supprimer les triplets) + DocumentDaoSesame docDaoSesame = new DocumentDaoSesame(); + docDaoSesame.user = user; + docDaoSesame.uri = documentMetadata.getUri(); + ArrayList documentsCorresponding = docDaoSesame.allPaginate(); + URINamespaces uriNamespaces = new URINamespaces(); + + String deleteQuery = null; + //1.2 Suppression des métadonnées associées à l'URI + if (documentsCorresponding.size() > 0) { + //SILEX:conception + //De la même façon qu'un querybuilder pour les insert existe, il faudra + //développer un querybuilder pour le delete et l'utiliser ici + deleteQuery = "PREFIX dc: <" + uriNamespaces.getContextsProperty("pxDublinCore") + "#> " + + "DELETE WHERE { " + + "<" + documentsCorresponding.get(0).getUri() + "> dc:creator \"" + documentsCorresponding.get(0).getCreator() + "\" . " + + "<" + documentsCorresponding.get(0).getUri() + "> dc:language \"" + documentsCorresponding.get(0).getLanguage() + "\" . " + + "<" + documentsCorresponding.get(0).getUri() + "> dc:title \"" + documentsCorresponding.get(0).getTitle() + "\" . " + + "<" + documentsCorresponding.get(0).getUri() + "> dc:date \"" + documentsCorresponding.get(0).getCreationDate() + "\" . " + + "<" + documentsCorresponding.get(0).getUri() + "> rdf:type <" + documentsCorresponding.get(0).getDocumentType() +"> . " + + "<" + documentsCorresponding.get(0).getUri() + "> <" + uriNamespaces.getRelationsProperty("rStatus") + "> \"" + documentsCorresponding.get(0).getStatus() + "\" . "; + + if (documentsCorresponding.get(0).getComment() != null) { + deleteQuery += "<" + documentsCorresponding.get(0).getUri() + "> rdfs:comment \"" + documentsCorresponding.get(0).getComment() + "\" . "; + } + + for (ConcernItemDTO concernedItem : documentsCorresponding.get(0).getConcernedItems()) { + deleteQuery += "<" + documentsCorresponding.get(0).getUri() + "> <" + uriNamespaces.getRelationsProperty("rConcern") + "> <" + concernedItem.getUri() + "> . "; + } + deleteQuery += "}"; + //\SILEX:conception + } + + //2. Insertion des nouvelles métadonnées + SPARQLUpdateBuilder spqlInsert = new SPARQLUpdateBuilder(); + spqlInsert.appendPrefix("dc", uriNamespaces.getContextsProperty("pxDublinCore")); + spqlInsert.appendGraphURI(uriNamespaces.getContextsProperty("documents")); + spqlInsert.appendTriplet(documentMetadata.getUri(), "rdf:type", documentMetadata.getDocumentType(), null); + spqlInsert.appendTriplet(documentMetadata.getUri(), "dc:creator", "\"" + documentMetadata.getCreator() + "\"", null); + spqlInsert.appendTriplet(documentMetadata.getUri(), "dc:language", "\"" + documentMetadata.getLanguage() + "\"", null); + spqlInsert.appendTriplet(documentMetadata.getUri(), "dc:title", "\"" + documentMetadata.getTitle() + "\"", null); + spqlInsert.appendTriplet(documentMetadata.getUri(), "dc:date", "\"" + documentMetadata.getCreationDate() + "\"", null); + spqlInsert.appendTriplet(documentMetadata.getUri(), uriNamespaces.getRelationsProperty("rStatus"), "\"" + documentMetadata.getStatus() + "\"", null); + + if (documentMetadata.getComment() != null) { + spqlInsert.appendTriplet(documentMetadata.getUri(), "rdfs:comment", "\"" + documentMetadata.getComment() + "\"", null); + } + + if (documentMetadata.getConcern() != null && !documentMetadata.getConcern().isEmpty() && documentMetadata.getConcern().size() > 0) { + for (ConcernItemDTO concernedItem : documentMetadata.getConcern()) { + spqlInsert.appendTriplet(documentMetadata.getUri(), uriNamespaces.getRelationsProperty("rConcern"), concernedItem.getUri(), null); + spqlInsert.appendTriplet(concernedItem.getUri(), "rdf:type", concernedItem.getTypeURI(), null); + } + } + + try { + // début de la transaction : vérification de la requête + this.getConnection().begin(); + Update prepareDelete = this.getConnection().prepareUpdate(deleteQuery); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, spqlInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareDelete.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareDelete.execute(); + prepareUpdate.execute(); + + updatedResourcesURIList.add(documentMetadata.getUri()); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationUpdate = false; + updateStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed update query: " + e.getMessage())); + } + + } else { + // JSON malformé de quelque sorte que ce soit + docsMetadataState = false; + metadataOk.remove("state"); + updateStatusList.add(new Status("Missing field error", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.MISSINGFIELDS).append(metadataOk).toString())); + } + + // JSON bien formé et pas de problème avant l'insertion + if (annotationUpdate && docsMetadataState) { + resultState = true; + try { + this.getConnection().commit(); + } catch (RepositoryException ex) { + LOGGER.error("Error during commit Triplestore statements: ", ex); + } + } else { + // retour en arrière sur la transaction + try { + this.getConnection().rollback(); + } catch (RepositoryException ex) { + LOGGER.error("Error during rollback Triplestore statements : ", ex); + } + } + } + results = new POSTResultsReturn(resultState, annotationUpdate, docsMetadataState); + results.statusList = updateStatusList; + if (resultState && !updatedResourcesURIList.isEmpty()) { + results.createdResources = updatedResourcesURIList; + results.statusList.add(new Status("Resources created", StatusCodeMsg.INFO, updatedResourcesURIList.size() + " new resource(s) created.")); + } + + return results; + } + + /** + * @action modifie les metadonnées des documents + * @param objectsToUpdate + * @return + */ + public POSTResultsReturn checkAndUpdateList(List objectsToUpdate) { + POSTResultsReturn postResult; + + postResult = this.checkAndUpdateDocumentsMetadataList(objectsToUpdate); + + return postResult; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/sesame/MethodDaoSesame.java b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/MethodDaoSesame.java new file mode 100644 index 000000000..49ba838fd --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/MethodDaoSesame.java @@ -0,0 +1,500 @@ +//********************************************************************************************** +// MethodDaoSesame.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 17 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 17 2017 +// Subject: A specific DAO to retrieve data on methods +//*********************************************************************************************** +package phis2ws.service.dao.sesame; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.query.Update; +import org.eclipse.rdf4j.repository.RepositoryException; +import org.eclipse.rdf4j.repository.http.HTTPRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.configuration.URINamespaces; +import phis2ws.service.dao.manager.DAOSesame; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.MethodDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sparql.SPARQLQueryBuilder; +import phis2ws.service.utils.sparql.SPARQLUpdateBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.Method; +import phis2ws.service.view.model.phis.OntologyReference; + +public class MethodDaoSesame extends DAOSesame { + + final static Logger LOGGER = LoggerFactory.getLogger(MethodDaoSesame.class); + + public String uri; + public String label; + public String comment; + public ArrayList ontologiesReferences = new ArrayList<>(); + + public MethodDaoSesame() { + } + + @Override + protected SPARQLQueryBuilder prepareSearchQuery() { + //SILEX:todo + //Ajouter la recherche par référence vers d'autres ontologies aussi + //\SILEX:todo + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + query.appendDistinct(Boolean.TRUE); + query.appendGraph(uriNamespaces.getContextsProperty("variables")); + String traitURI; + if (uri != null) { + traitURI = "<" + uri + ">"; + } else { + traitURI = "?uri"; + query.appendSelect("?uri"); + } + query.appendTriplet(traitURI, "rdf:type", uriNamespaces.getObjectsProperty("cMethod"), null); + + if (label != null) { + query.appendTriplet(traitURI, "rdfs:label","\"" + label + "\"", null); + } else { + query.appendSelect(" ?label"); + query.appendTriplet(traitURI, "rdfs:label", "?label", null); + } + + if (comment != null) { + query.appendTriplet(traitURI, "rdfs:comment", "\"" + comment + "\"", null); + } else { + query.appendSelect(" ?comment"); + query.appendTriplet(traitURI, "rdfs:comment", " ?comment", null); + } + + LOGGER.trace("sparql select query : " + query.toString()); + return query; + } + + @Override + public Integer count() throws RepositoryException, MalformedQueryException, QueryEvaluationException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * Vérifie si les méthodes sont corrects + * @param methodsDTO + * @return + */ + public POSTResultsReturn check(List methodsDTO) { + //Résultats attendus + POSTResultsReturn traitsCheck = null; + //Liste des status retournés + List checkStatusList = new ArrayList<>(); + boolean dataOk = true; + URINamespaces uriNamespaces = new URINamespaces(); + + //Vérification des méthodes + for (MethodDTO methodDTO : methodsDTO) { + if ((boolean) methodDTO.isOk().get("state")) { + //Vérification des relations d'ontologies de référence + for (OntologyReference ontologyReference : methodDTO.getOntologiesReferences()) { + if (!ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rExactMatch")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rCloseMatch")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rNarrower")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rBroader"))) { + dataOk = false; + checkStatusList.add(new Status("Wrong value", StatusCodeMsg.ERR, + "Bad property relation given. Must be one of the following : " + uriNamespaces.getRelationsProperty("rExactMatch") + + ", " + uriNamespaces.getRelationsProperty("rCloseMatch") + + ", " + uriNamespaces.getRelationsProperty("rNarrower") + + ", " + uriNamespaces.getRelationsProperty("rBroader") + +". Given : " + ontologyReference.getProperty())); + } + } + } else { //Données attendues non reçues + dataOk = false; + methodDTO.isOk().remove("state"); + checkStatusList.add(new Status("Bad data format", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.MISSINGFIELDS).append(methodDTO.isOk()).toString())); + } + } + + traitsCheck = new POSTResultsReturn(dataOk, null, dataOk); + traitsCheck.statusList = checkStatusList; + return traitsCheck; + } + + /** + * + * @return la requête permettant de connaitre le nombre de méthodes + */ + private SPARQLQueryBuilder prepareGetMethodsNumber() { + URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder spqlQuery = new SPARQLQueryBuilder(); + spqlQuery.appendGraph(uriNamespaces.getContextsProperty("variables")); + spqlQuery.appendSelect("(count(?trait) as ?count)"); + spqlQuery.appendTriplet("?trait", "rdf:type", uriNamespaces.getObjectsProperty("cMethod"), null); + + LOGGER.trace("sparql select query : " + spqlQuery.toString()); + + return spqlQuery; + } + + /** + * + * @return le nombre de méthodes présentes dans le triplestore + */ + public int getNumerOfMethods() { + SPARQLQueryBuilder spqlQuery = prepareGetMethodsNumber(); + + //SILEX:test + //Pour les soucis de pool de connexion + rep = new HTTPRepository(SESAME_SERVER, REPOSITORY_ID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + //\SILEX:test + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, spqlQuery.toString()); + TupleQueryResult result = tupleQuery.evaluate(); + //SILEX:test + //Pour les soucis de pool de connexion + getConnection().close(); + //\SILEX:test + + BindingSet bindingSet = result.next(); + String countResult = bindingSet.getValue("count").stringValue(); + + return Integer.parseInt(countResult); + } + + /** + * génère les uris des méthodes + * @param methodsDTO + * @return la liste des méthodes, avec les URIs en plus + */ + private ArrayList generateURIs(List methodsDTO) { + URINamespaces uriNamespaces = new URINamespaces(); + String baseURI = uriNamespaces.getNamespaceProperty("methods"); + ArrayList toReturn = new ArrayList<>(); + + //Récupération du numéro de la dernière uri de la méthode (pour l'autoincrement) + int numberOfMethods = getNumerOfMethods(); + + for (MethodDTO methodDTO : methodsDTO) { + numberOfMethods++; + + //On calcule le nombre de 0 à ajouter (l'id de la méthode doit être du type : 001) + //avec 3 chiffres. + String methodNb; + String numberOfMethodsString = Integer.toString(numberOfMethods); + switch (numberOfMethodsString.length()) { + case 1: + methodNb = "00" + numberOfMethodsString; + break; + case 2: + methodNb = "0" + numberOfMethodsString; + break; + default: + methodNb = numberOfMethodsString; + break; + } + + methodDTO.setUri(baseURI + "/m" + methodNb); + toReturn.add(methodDTO); + } + + return toReturn; + } + + private SPARQLUpdateBuilder prepareInsertQuery(MethodDTO methodDTO) { + SPARQLUpdateBuilder spql = new SPARQLUpdateBuilder(); + final URINamespaces uriNamespaces = new URINamespaces(); + + spql.appendGraphURI(uriNamespaces.getContextsProperty("variables")); + spql.appendTriplet(methodDTO.getUri(), "rdf:type", uriNamespaces.getObjectsProperty("cMethod"), null); + spql.appendTriplet(methodDTO.getUri(), "rdfs:label", "\"" + methodDTO.getLabel() + "\"", null); + spql.appendTriplet(methodDTO.getUri(), "rdfs:comment", "\"" + methodDTO.getComment() + "\"", null); + + for (OntologyReference ontologyReference : methodDTO.getOntologiesReferences()) { + spql.appendTriplet(methodDTO.getUri(), ontologyReference.getProperty(), ontologyReference.getObject(), null); + spql.appendTriplet(ontologyReference.getObject(), "rdfs:seeAlso", "\"" + ontologyReference.getSeeAlso() + "\"", null); + } + + return spql; + } + + /** + * insère les données dans le triplestore + * On suppose que la vérification de leur intégrité a été faite auparavent, via l'appel à la méthode check + * @param methodsDTO + * @return + */ + public POSTResultsReturn insert(List methodsDTO) { + List insertStatusList = new ArrayList<>(); + List createdResourcesURI = new ArrayList<>(); + + POSTResultsReturn results; + boolean resultState = false; //Pour savoir si les données sont bonnes et ont bien été insérées + boolean annotationInsert = true; //Si l'insertion a bien été faite + + methodsDTO = generateURIs(methodsDTO); + final Iterator iteratorMethodDTO = methodsDTO.iterator(); + + while (iteratorMethodDTO.hasNext() && annotationInsert) { + MethodDTO methodDTO = iteratorMethodDTO.next(); + + //Enregistrement dans le triplestore + SPARQLUpdateBuilder spqlInsert = prepareInsertQuery(methodDTO); + + try { + //SILEX:test + //Toute la notion de connexion au triplestore sera à revoir. + //C'est un hot fix qui n'est pas propre + String sesameServer = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "sesameServer"); + String repositoryID = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "repositoryID"); + rep = new HTTPRepository(sesameServer, repositoryID); //Stockage triplestore Sesame + rep.initialize(); + this.setConnection(rep.getConnection()); + this.getConnection().begin(); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, spqlInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareUpdate.execute(); + //\SILEX:test + + createdResourcesURI.add(methodDTO.getUri()); + + if (annotationInsert) { + resultState = true; + getConnection().commit(); + } else { + getConnection().rollback(); + } + } catch (RepositoryException ex) { + LOGGER.error("Error during commit or rolleback Triplestore statements: ", ex); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationInsert = false; + insertStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed insertion query: " + e.getMessage())); + } + } + + results = new POSTResultsReturn(resultState, annotationInsert, true); + results.statusList = insertStatusList; + results.setCreatedResources(createdResourcesURI); + if (resultState && !createdResourcesURI.isEmpty()) { + results.createdResources = createdResourcesURI; + results.statusList.add(new Status("Resources created", StatusCodeMsg.INFO, createdResourcesURI.size() + " new resource(s) created.")); + } + + return results; + } + + /** + * Vérifie les données et les insère dans le triplestore. + * @param methodsDTO + * @return POSTResultsReturn le résultat de la tentative d'insertion + */ + public POSTResultsReturn checkAndInsert(List methodsDTO) { + POSTResultsReturn checkResult = check(methodsDTO); + if (checkResult.getDataState()) { + return insert(methodsDTO); + } else { //Les données ne sont pas bonnes + return checkResult; + } + } + + /** + * + * @param uri + * @return la liste des liens vers d'autres ontologies + */ + private SPARQLQueryBuilder prepareSearchOntologiesReferencesQuery(String uri) { + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + + query.appendDistinct(Boolean.TRUE); + query.appendGraph(uriNamespaces.getContextsProperty("variables")); + + if (ontologiesReferences.isEmpty()) { + query.appendSelect(" ?property ?object ?seeAlso"); + query.appendTriplet(uri, "?property", "?object", null); + query.appendOptional("{?object rdfs:seeAlso ?seeAlso}"); + query.appendFilter("?property IN(<" + uriNamespaces.getRelationsProperty("rCloseMatch") + ">, <" + + uriNamespaces.getRelationsProperty("rExactMatch") + ">, <" + + uriNamespaces.getRelationsProperty("rNarrower") + ">, <" + + uriNamespaces.getRelationsProperty("rBroader") + ">)"); + } else { + for (OntologyReference ontologyReference : ontologiesReferences) { + query.appendTriplet(uri, ontologyReference.getProperty(), ontologyReference.getObject(), null); + query.appendTriplet(ontologyReference.getObject(), "rdfs:seeAlso", ontologyReference.getSeeAlso(), null); + } + } + + LOGGER.trace("SPARQL select query : " + query.toString()); + return query; + } + + /** + * + * @return la liste des méthodes correspondant à la recherche + */ + public ArrayList allPaginate() { + SPARQLQueryBuilder query = prepareSearchQuery(); + TupleQuery tupleQuery = getConnection().prepareTupleQuery(QueryLanguage.SPARQL, query.toString()); + ArrayList methods = new ArrayList<>(); + + try (TupleQueryResult result = tupleQuery.evaluate()) { + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + Method method = new Method(); + + if (uri != null) { + method.setUri(uri); + } else { + method.setUri(bindingSet.getValue("uri").stringValue()); + } + + if (label != null) { + method.setLabel(label); + } else { + method.setLabel(bindingSet.getValue("label").stringValue()); + } + + if (comment != null) { + method.setComment(comment); + } else { + method.setComment(bindingSet.getValue("comment").stringValue()); + } + + //On récupère maintenant la liste des références vers des ontologies... + SPARQLQueryBuilder queryOntologiesReferences = prepareSearchOntologiesReferencesQuery(method.getUri()); + TupleQuery tupleQueryOntologiesReferences = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, queryOntologiesReferences.toString()); + TupleQueryResult resultOntologiesReferences = tupleQueryOntologiesReferences.evaluate(); + while (resultOntologiesReferences.hasNext()) { + BindingSet bindingSetOntologiesReferences = resultOntologiesReferences.next(); + if (bindingSetOntologiesReferences.getValue("object") != null + && bindingSetOntologiesReferences.getValue("property") != null) { + OntologyReference ontologyReference = new OntologyReference(); + ontologyReference.setObject(bindingSetOntologiesReferences.getValue("object").toString()); + ontologyReference.setProperty(bindingSetOntologiesReferences.getValue("property").toString()); + if (bindingSetOntologiesReferences.getValue("seeAlso") != null) { + ontologyReference.setSeeAlso(bindingSetOntologiesReferences.getValue("seeAlso").toString()); + } + + method.addOntologyReference(ontologyReference); + } + } + + methods.add(method); + } + } + + return methods; + } + + private String prepareDeleteQuery(Method method) { + String deleteQuery; + deleteQuery = "DELETE WHERE {" + + "<" + method.getUri() + "> rdfs:label \"" + method.getLabel() + "\" . " + + "<" + method.getUri() + "> rdfs:comment \"" + method.getComment() + "\" . "; + + for (OntologyReference ontologyReference : method.getOntologiesReferences()) { + deleteQuery += "<" + method.getUri() + "> <" + ontologyReference.getProperty() + "> <" + ontologyReference.getObject() + "> . "; + if (ontologyReference.getSeeAlso() != null) { + deleteQuery += "<" + ontologyReference.getObject() + "> rdfs:seeAlso " + ontologyReference.getSeeAlso() + " . "; + } + } + + deleteQuery += "}"; + + return deleteQuery; + } + + private POSTResultsReturn update(List methodsDTO) { + List updateStatusList = new ArrayList<>(); + List updatedResourcesURIList = new ArrayList<>(); + POSTResultsReturn results; + + boolean annotationUpdate = true; // Si l'insertion a bien été réalisée + boolean resultState = false; // Pour savoir si les données étaient bonnes et on bien été mises à jour + + for (MethodDTO methodDTO : methodsDTO) { + //1. Suppression des données déjà existantes + //1.1 Récupération des infos qui seront modifiées (pour supprimer les bons triplets) + uri = methodDTO.getUri(); + ArrayList methodsCorresponding = allPaginate(); + if (methodsCorresponding.size() > 0) { + String deleteQuery = prepareDeleteQuery(methodsCorresponding.get(0)); + + //2. Insertion des nouvelles données + SPARQLUpdateBuilder queryInsert = prepareInsertQuery(methodDTO); + try { + // début de la transaction : vérification de la requête + this.getConnection().begin(); + Update prepareDelete = this.getConnection().prepareUpdate(deleteQuery); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, queryInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareDelete.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareDelete.execute(); + prepareUpdate.execute(); + + updatedResourcesURIList.add(methodDTO.getUri()); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationUpdate = false; + updateStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed update query: " + e.getMessage())); + } + } else { + annotationUpdate = false; + updateStatusList.add(new Status("Unknown instance", StatusCodeMsg.ERR, "Unknown method " + methodDTO.getUri())); + } + } + + if (annotationUpdate) { + resultState = true; + try { + this.getConnection().commit(); + } catch (RepositoryException ex) { + LOGGER.error("Error during commit Triplestore statements: ", ex); + } + } else { + // retour en arrière sur la transaction + try { + this.getConnection().rollback(); + } catch (RepositoryException ex) { + LOGGER.error("Error during rollback Triplestore statements : ", ex); + } + } + + results = new POSTResultsReturn(resultState, annotationUpdate, true); + results.statusList = updateStatusList; + if (resultState && !updatedResourcesURIList.isEmpty()) { + results.createdResources = updatedResourcesURIList; + results.statusList.add(new Status("Resources updated", StatusCodeMsg.INFO, updatedResourcesURIList.size() + " resources updated")); + } + + return results; + } + + /** + * Vérifie les données et met à jour le triplestore + * @param methodsDTO + * @return POSTResultsReturn le résultat de la tentative de modification des données + */ + public POSTResultsReturn checkAndUpdate(List methodsDTO) { + POSTResultsReturn checkResult = check(methodsDTO); + if (checkResult.getDataState()) { + return update(methodsDTO); + } else { //Les données ne sont pas bonnes + return checkResult; + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/sesame/TraitDaoSesame.java b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/TraitDaoSesame.java new file mode 100644 index 000000000..7f544e89c --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/TraitDaoSesame.java @@ -0,0 +1,499 @@ +//********************************************************************************************** +// TraitDaoSesame.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 17 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 17 2017 +// Subject: A specific DAO to retrieve data on traits +//*********************************************************************************************** +package phis2ws.service.dao.sesame; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.query.Update; +import org.eclipse.rdf4j.repository.RepositoryException; +import org.eclipse.rdf4j.repository.http.HTTPRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.configuration.URINamespaces; +import phis2ws.service.dao.manager.DAOSesame; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.TraitDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sparql.SPARQLQueryBuilder; +import phis2ws.service.utils.sparql.SPARQLUpdateBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.OntologyReference; +import phis2ws.service.view.model.phis.Trait; + +public class TraitDaoSesame extends DAOSesame { + final static Logger LOGGER = LoggerFactory.getLogger(TraitDaoSesame.class); + + public String uri; + public String label; + public String comment; + public ArrayList ontologiesReferences = new ArrayList<>(); + + public TraitDaoSesame() { + } + + @Override + protected SPARQLQueryBuilder prepareSearchQuery() { + //SILEX:todo + //Ajouter la recherche par référence vers d'autres ontologies aussi + //\SILEX:todo + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + query.appendDistinct(Boolean.TRUE); + query.appendGraph(uriNamespaces.getContextsProperty("variables")); + String traitURI; + if (uri != null) { + traitURI = "<" + uri + ">"; + } else { + traitURI = "?uri"; + query.appendSelect("?uri"); + } + query.appendTriplet(traitURI, "rdf:type", uriNamespaces.getObjectsProperty("cTrait"), null); + + if (label != null) { + query.appendTriplet(traitURI, "rdfs:label","\"" + label + "\"", null); + } else { + query.appendSelect(" ?label"); + query.appendTriplet(traitURI, "rdfs:label", "?label", null); + } + + if (comment != null) { + query.appendTriplet(traitURI, "rdfs:comment", "\"" + comment + "\"", null); + } else { + query.appendSelect(" ?comment"); + query.appendTriplet(traitURI, "rdfs:comment", " ?comment", null); + } + + LOGGER.trace("sparql select query : " + query.toString()); + return query; + } + + @Override + public Integer count() throws RepositoryException, MalformedQueryException, QueryEvaluationException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * Vérifie si les traits sont corrects + * @param traitsDTO + * @return + */ + public POSTResultsReturn check(List traitsDTO) { + //Résultats attendus + POSTResultsReturn traitsCheck = null; + //Liste des status retournés + List checkStatusList = new ArrayList<>(); + boolean dataOk = true; + URINamespaces uriNamespaces = new URINamespaces(); + + //Vérification des traits + for (TraitDTO traitDTO : traitsDTO) { + if ((boolean) traitDTO.isOk().get("state")) { + //Vérification des relations d'ontologies de référence + for (OntologyReference ontologyReference : traitDTO.getOntologiesReferences()) { + if (!ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rExactMatch")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rCloseMatch")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rNarrower")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rBroader"))) { + dataOk = false; + checkStatusList.add(new Status("Wrong value", StatusCodeMsg.ERR, + "Bad property relation given. Must be one of the following : " + uriNamespaces.getRelationsProperty("rExactMatch") + + ", " + uriNamespaces.getRelationsProperty("rCloseMatch") + + ", " + uriNamespaces.getRelationsProperty("rNarrower") + + ", " + uriNamespaces.getRelationsProperty("rBroader") + +". Given : " + ontologyReference.getProperty())); + } + } + } else { //Données attendues non reçues + dataOk = false; + traitDTO.isOk().remove("state"); + checkStatusList.add(new Status("Bad data format", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.MISSINGFIELDS).append(traitDTO.isOk()).toString())); + } + } + + traitsCheck = new POSTResultsReturn(dataOk, null, dataOk); + traitsCheck.statusList = checkStatusList; + return traitsCheck; + } + + /** + * + * @return la requête permettant de connaitre le nombre de traits + */ + private SPARQLQueryBuilder prepareGetTraitsNumber() { + URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder spqlQuery = new SPARQLQueryBuilder(); + spqlQuery.appendGraph(uriNamespaces.getContextsProperty("variables")); + spqlQuery.appendSelect("(count(?trait) as ?count)"); + spqlQuery.appendTriplet("?trait", "rdf:type", uriNamespaces.getObjectsProperty("cTrait"), null); + + LOGGER.trace("sparql select query : " + spqlQuery.toString()); + + return spqlQuery; + } + + /** + * + * @return le nombre de traits présents dans le triplestore + */ + public int getNumerOfTraits() { + SPARQLQueryBuilder spqlQuery = prepareGetTraitsNumber(); + + //SILEX:test + //Pour les soucis de pool de connexion + rep = new HTTPRepository(SESAME_SERVER, REPOSITORY_ID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + //\SILEX:test + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, spqlQuery.toString()); + TupleQueryResult result = tupleQuery.evaluate(); + //SILEX:test + //Pour les soucis de pool de connexion + getConnection().close(); + //\SILEX:test + + BindingSet bindingSet = result.next(); + String countResult = bindingSet.getValue("count").stringValue(); + + return Integer.parseInt(countResult); + } + + /** + * génère les uris des traits + * @param traitsDTO + * @return la liste des traits, avec les URIs en plus + */ + private ArrayList generateURIs(List traitsDTO) { + URINamespaces uriNamespaces = new URINamespaces(); + String baseURI = uriNamespaces.getNamespaceProperty("traits"); + ArrayList toReturn = new ArrayList<>(); + + //Récupération du numéro de la dernière uri du trait (pour l'autoincrement) + int numberOfTraits = getNumerOfTraits(); + + for (TraitDTO traitDTO : traitsDTO) { + numberOfTraits++; + + //On calcule le nombre de 0 à ajouter (l'id du trait doit être du type : 001) + //avec 3 chiffres. + String traitNb; + String numberOfTraitsString = Integer.toString(numberOfTraits); + switch (numberOfTraitsString.length()) { + case 1: + traitNb = "00" + numberOfTraitsString; + break; + case 2: + traitNb = "0" + numberOfTraitsString; + break; + default: + traitNb = numberOfTraitsString; + break; + } + + traitDTO.setUri(baseURI + "/t" + traitNb); + toReturn.add(traitDTO); + } + + return toReturn; + } + + private SPARQLUpdateBuilder prepareInsertQuery(TraitDTO traitDTO) { + SPARQLUpdateBuilder spql = new SPARQLUpdateBuilder(); + final URINamespaces uriNamespaces = new URINamespaces(); + + spql.appendGraphURI(uriNamespaces.getContextsProperty("variables")); + spql.appendTriplet(traitDTO.getUri(), "rdf:type", uriNamespaces.getObjectsProperty("cTrait"), null); + spql.appendTriplet(traitDTO.getUri(), "rdfs:label", "\"" + traitDTO.getLabel() + "\"", null); + spql.appendTriplet(traitDTO.getUri(), "rdfs:comment", "\"" + traitDTO.getComment() + "\"", null); + + for (OntologyReference ontologyReference : traitDTO.getOntologiesReferences()) { + spql.appendTriplet(traitDTO.getUri(), ontologyReference.getProperty(), ontologyReference.getObject(), null); + spql.appendTriplet(ontologyReference.getObject(), "rdfs:seeAlso", "\"" + ontologyReference.getSeeAlso() + "\"", null); + } + + return spql; + } + + /** + * insère les données dans le triplestore + * On suppose que la vérification de leur intégrité a été faite auparavent, via l'appel à la méthode check + * @param traitsDTO + * @return + */ + public POSTResultsReturn insert(List traitsDTO) { + List insertStatusList = new ArrayList<>(); + List createdResourcesURI = new ArrayList<>(); + + POSTResultsReturn results; + boolean resultState = false; //Pour savoir si les données sont bonnes et ont bien été insérées + boolean annotationInsert = true; //Si l'insertion a bien été faite + + traitsDTO = generateURIs(traitsDTO); + final Iterator iteratorTraitDTO = traitsDTO.iterator(); + + while (iteratorTraitDTO.hasNext() && annotationInsert) { + TraitDTO traitDTO = iteratorTraitDTO.next(); + + //Enregistrement dans le triplestore + SPARQLUpdateBuilder spqlInsert = prepareInsertQuery(traitDTO); + + try { + //SILEX:test + //Toute la notion de connexion au triplestore sera à revoir. + //C'est un hot fix qui n'est pas propre + String sesameServer = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "sesameServer"); + String repositoryID = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "repositoryID"); + rep = new HTTPRepository(sesameServer, repositoryID); //Stockage triplestore Sesame + rep.initialize(); + this.setConnection(rep.getConnection()); + this.getConnection().begin(); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, spqlInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareUpdate.execute(); + //\SILEX:test + + createdResourcesURI.add(traitDTO.getUri()); + + if (annotationInsert) { + resultState = true; + getConnection().commit(); + } else { + getConnection().rollback(); + } + } catch (RepositoryException ex) { + LOGGER.error("Error during commit or rolleback Triplestore statements: ", ex); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationInsert = false; + insertStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed insertion query: " + e.getMessage())); + } + } + + results = new POSTResultsReturn(resultState, annotationInsert, true); + results.statusList = insertStatusList; + results.setCreatedResources(createdResourcesURI); + if (resultState && !createdResourcesURI.isEmpty()) { + results.createdResources = createdResourcesURI; + results.statusList.add(new Status("Resources created", StatusCodeMsg.INFO, createdResourcesURI.size() + " new resource(s) created.")); + } + + return results; + } + + /** + * + * @param uri + * @return la liste des liens vers d'autres ontologies + */ + private SPARQLQueryBuilder prepareSearchOntologiesReferencesQuery(String uri) { + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + + query.appendDistinct(Boolean.TRUE); + query.appendGraph(uriNamespaces.getContextsProperty("variables")); + + if (ontologiesReferences.isEmpty()) { + query.appendSelect(" ?property ?object ?seeAlso"); + query.appendTriplet(uri, "?property", "?object", null); + query.appendOptional("{?object rdfs:seeAlso ?seeAlso}"); + query.appendFilter("?property IN(<" + uriNamespaces.getRelationsProperty("rCloseMatch") + ">, <" + + uriNamespaces.getRelationsProperty("rExactMatch") + ">, <" + + uriNamespaces.getRelationsProperty("rNarrower") + ">, <" + + uriNamespaces.getRelationsProperty("rBroader") + ">)"); + } else { + for (OntologyReference ontologyReference : ontologiesReferences) { + query.appendTriplet(uri, ontologyReference.getProperty(), ontologyReference.getObject(), null); + query.appendTriplet(ontologyReference.getObject(), "rdfs:seeAlso", ontologyReference.getSeeAlso(), null); + } + } + + LOGGER.trace("SPARQL select query : " + query.toString()); + return query; + } + + /** + * + * @return la liste des traits correspondant à la recherche + */ + public ArrayList allPaginate() { + SPARQLQueryBuilder query = prepareSearchQuery(); + TupleQuery tupleQuery = getConnection().prepareTupleQuery(QueryLanguage.SPARQL, query.toString()); + ArrayList traits = new ArrayList<>(); + + try (TupleQueryResult result = tupleQuery.evaluate()) { + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + Trait trait = new Trait(); + + if (uri != null) { + trait.setUri(uri); + } else { + trait.setUri(bindingSet.getValue("uri").stringValue()); + } + + if (label != null) { + trait.setLabel(label); + } else { + trait.setLabel(bindingSet.getValue("label").stringValue()); + } + + if (comment != null) { + trait.setComment(comment); + } else { + trait.setComment(bindingSet.getValue("comment").stringValue()); + } + + //On récupère maintenant la liste des références vers des ontologies... + SPARQLQueryBuilder queryOntologiesReferences = prepareSearchOntologiesReferencesQuery(trait.getUri()); + TupleQuery tupleQueryOntologiesReferences = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, queryOntologiesReferences.toString()); + TupleQueryResult resultOntologiesReferences = tupleQueryOntologiesReferences.evaluate(); + while (resultOntologiesReferences.hasNext()) { + BindingSet bindingSetOntologiesReferences = resultOntologiesReferences.next(); + if (bindingSetOntologiesReferences.getValue("object") != null + && bindingSetOntologiesReferences.getValue("property") != null) { + OntologyReference ontologyReference = new OntologyReference(); + ontologyReference.setObject(bindingSetOntologiesReferences.getValue("object").toString()); + ontologyReference.setProperty(bindingSetOntologiesReferences.getValue("property").toString()); + if (bindingSetOntologiesReferences.getValue("seeAlso") != null) { + ontologyReference.setSeeAlso(bindingSetOntologiesReferences.getValue("seeAlso").toString()); + } + + trait.addOntologyReference(ontologyReference); + } + } + + traits.add(trait); + } + } + + return traits; + } + + private String prepareDeleteQuery(Trait trait) { + String deleteQuery; + deleteQuery = "DELETE WHERE {" + + "<" + trait.getUri() + "> rdfs:label \"" + trait.getLabel() + "\" . " + + "<" + trait.getUri() + "> rdfs:comment \"" + trait.getComment() + "\" . "; + + for (OntologyReference ontologyReference : trait.getOntologiesReferences()) { + deleteQuery += "<" + trait.getUri() + "> <" + ontologyReference.getProperty() + "> <" + ontologyReference.getObject() + "> . "; + if (ontologyReference.getSeeAlso() != null) { + deleteQuery += "<" + ontologyReference.getObject() + "> rdfs:seeAlso " + ontologyReference.getSeeAlso() + " . "; + } + } + + deleteQuery += "}"; + + return deleteQuery; + } + + private POSTResultsReturn update(List traitsDTO) { + List updateStatusList = new ArrayList<>(); + List updatedResourcesURIList = new ArrayList<>(); + POSTResultsReturn results; + + boolean annotationUpdate = true; // Si l'insertion a bien été réalisée + boolean resultState = false; // Pour savoir si les données étaient bonnes et on bien été mises à jour + + for (TraitDTO traitDTO : traitsDTO) { + //1. Suppression des données déjà existantes + //1.1 Récupération des infos qui seront modifiées (pour supprimer les bons triplets) + uri = traitDTO.getUri(); + ArrayList traitsCorresponding = allPaginate(); + if (traitsCorresponding.size() > 0) { + String deleteQuery = prepareDeleteQuery(traitsCorresponding.get(0)); + + //2. Insertion des nouvelles données + SPARQLUpdateBuilder queryInsert = prepareInsertQuery(traitDTO); + try { + // début de la transaction : vérification de la requête + this.getConnection().begin(); + Update prepareDelete = this.getConnection().prepareUpdate(deleteQuery); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, queryInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareDelete.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareDelete.execute(); + prepareUpdate.execute(); + + updatedResourcesURIList.add(traitDTO.getUri()); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationUpdate = false; + updateStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed update query: " + e.getMessage())); + } + } else { + annotationUpdate = false; + updateStatusList.add(new Status("Unknown instance", StatusCodeMsg.ERR, "Unknown trait " + traitDTO.getUri())); + } + } + + if (annotationUpdate) { + resultState = true; + try { + this.getConnection().commit(); + } catch (RepositoryException ex) { + LOGGER.error("Error during commit Triplestore statements: ", ex); + } + } else { + // retour en arrière sur la transaction + try { + this.getConnection().rollback(); + } catch (RepositoryException ex) { + LOGGER.error("Error during rollback Triplestore statements : ", ex); + } + } + + results = new POSTResultsReturn(resultState, annotationUpdate, true); + results.statusList = updateStatusList; + if (resultState && !updatedResourcesURIList.isEmpty()) { + results.createdResources = updatedResourcesURIList; + results.statusList.add(new Status("Resources updated", StatusCodeMsg.INFO, updatedResourcesURIList.size() + " resources updated")); + } + + return results; + } + + /** + * Vérifie les données et les insère dans le triplestore. + * @param traitsDTO + * @return POSTResultsReturn le résultat de la tentative d'insertion + */ + public POSTResultsReturn checkAndInsert(List traitsDTO) { + POSTResultsReturn checkResult = check(traitsDTO); + if (checkResult.getDataState()) { + return insert(traitsDTO); + } else { //Les données ne sont pas bonnes + return checkResult; + } + } + + /** + * Vérifie les données et met à jour le triplestore + * @param traitsDTO + * @return POSTResultsReturn le résultat de la tentative de modification des données + */ + public POSTResultsReturn checkAndUpdate(List traitsDTO) { + POSTResultsReturn checkResult = check(traitsDTO); + if (checkResult.getDataState()) { + return update(traitsDTO); + } else { //Les données ne sont pas bonnes + return checkResult; + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/sesame/UnitDaoSesame.java b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/UnitDaoSesame.java new file mode 100644 index 000000000..6dfd58ffd --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/UnitDaoSesame.java @@ -0,0 +1,500 @@ +//********************************************************************************************** +// UnitDaoSesame.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 18 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 18 2017 +// Subject: A specific DAO to retrieve data on units +//*********************************************************************************************** +package phis2ws.service.dao.sesame; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.query.Update; +import org.eclipse.rdf4j.repository.RepositoryException; +import org.eclipse.rdf4j.repository.http.HTTPRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.configuration.URINamespaces; +import phis2ws.service.dao.manager.DAOSesame; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.UnitDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sparql.SPARQLQueryBuilder; +import phis2ws.service.utils.sparql.SPARQLUpdateBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.OntologyReference; +import phis2ws.service.view.model.phis.Unit; + +public class UnitDaoSesame extends DAOSesame { + final static Logger LOGGER = LoggerFactory.getLogger(UnitDaoSesame.class); + + public String uri; + public String label; + public String comment; + public ArrayList ontologiesReferences = new ArrayList<>(); + + public UnitDaoSesame() { + } + + @Override + protected SPARQLQueryBuilder prepareSearchQuery() { + //SILEX:todo + //Ajouter la recherche par référence vers d'autres ontologies aussi + //\SILEX:todo + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + query.appendDistinct(Boolean.TRUE); + query.appendGraph(uriNamespaces.getContextsProperty("variables")); + String traitURI; + if (uri != null) { + traitURI = "<" + uri + ">"; + } else { + traitURI = "?uri"; + query.appendSelect("?uri"); + } + query.appendTriplet(traitURI, "rdf:type", uriNamespaces.getObjectsProperty("cUnit"), null); + + if (label != null) { + query.appendTriplet(traitURI, "rdfs:label","\"" + label + "\"", null); + } else { + query.appendSelect(" ?label"); + query.appendTriplet(traitURI, "rdfs:label", "?label", null); + } + + if (comment != null) { + query.appendTriplet(traitURI, "rdfs:comment", "\"" + comment + "\"", null); + } else { + query.appendSelect(" ?comment"); + query.appendTriplet(traitURI, "rdfs:comment", " ?comment", null); + } + + LOGGER.trace("sparql select query : " + query.toString()); + return query; + } + + @Override + public Integer count() throws RepositoryException, MalformedQueryException, QueryEvaluationException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * Vérifie si les unités sont correctes + * @param unitsDTO + * @return + */ + public POSTResultsReturn check(List unitsDTO) { + //Résultats attendus + POSTResultsReturn traitsCheck = null; + //Liste des status retournés + List checkStatusList = new ArrayList<>(); + boolean dataOk = true; + URINamespaces uriNamespaces = new URINamespaces(); + + //Vérification des unités + for (UnitDTO unitDTO : unitsDTO) { + if ((boolean) unitDTO.isOk().get("state")) { + //Vérification des relations d'ontologies de référence + for (OntologyReference ontologyReference : unitDTO.getOntologiesReferences()) { + if (!ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rExactMatch")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rCloseMatch")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rNarrower")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rBroader"))) { + dataOk = false; + checkStatusList.add(new Status("Wrong value", StatusCodeMsg.ERR, + "Bad property relation given. Must be one of the following : " + uriNamespaces.getRelationsProperty("rExactMatch") + + ", " + uriNamespaces.getRelationsProperty("rCloseMatch") + + ", " + uriNamespaces.getRelationsProperty("rNarrower") + + ", " + uriNamespaces.getRelationsProperty("rBroader") + +". Given : " + ontologyReference.getProperty())); + } + } + } else { //Données attendues non reçues + dataOk = false; + unitDTO.isOk().remove("state"); + checkStatusList.add(new Status("Bad data format", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.MISSINGFIELDS).append(unitDTO.isOk()).toString())); + } + } + + traitsCheck = new POSTResultsReturn(dataOk, null, dataOk); + traitsCheck.statusList = checkStatusList; + return traitsCheck; + } + + /** + * + * @return la requête permettant de connaitre le nombre d'unités + */ + private SPARQLQueryBuilder prepareGetUnitsNumber() { + URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder spqlQuery = new SPARQLQueryBuilder(); + spqlQuery.appendGraph(uriNamespaces.getContextsProperty("variables")); + spqlQuery.appendSelect("(count(?unit) as ?count)"); + spqlQuery.appendTriplet("?unit", "rdf:type", uriNamespaces.getObjectsProperty("cUnit"), null); + + LOGGER.trace("sparql select query : " + spqlQuery.toString()); + + return spqlQuery; + } + + /** + * + * @return le nombre d'unités présentes dans le triplestore + */ + public int getNumerOfUnits() { + SPARQLQueryBuilder spqlQuery = prepareGetUnitsNumber(); + + //SILEX:test + //Pour les soucis de pool de connexion + rep = new HTTPRepository(SESAME_SERVER, REPOSITORY_ID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + //\SILEX:test + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, spqlQuery.toString()); + TupleQueryResult result = tupleQuery.evaluate(); + //SILEX:test + //Pour les soucis de pool de connexion + getConnection().close(); + //\SILEX:test + + BindingSet bindingSet = result.next(); + String countResult = bindingSet.getValue("count").stringValue(); + + return Integer.parseInt(countResult); + } + + /** + * génère les uris des unit"s + * @param unitsDTO + * @return la liste des unités, avec les URIs en plus + */ + private ArrayList generateURIs(List unitsDTO) { + URINamespaces uriNamespaces = new URINamespaces(); + String baseURI = uriNamespaces.getNamespaceProperty("units"); + ArrayList toReturn = new ArrayList<>(); + + //Récupération du numéro de la dernière uri de l'unité (pour l'autoincrement) + int numberOfUnits = getNumerOfUnits(); + + for (UnitDTO unitDTO : unitsDTO) { + numberOfUnits++; + + //On calcule le nombre de 0 à ajouter (l'id de l'unité doit être du type : 001) + //avec 3 chiffres. + String unitNb; + String numberOfUnitsString = Integer.toString(numberOfUnits); + switch (numberOfUnitsString.length()) { + case 1: + unitNb = "00" + numberOfUnitsString; + break; + case 2: + unitNb = "0" + numberOfUnitsString; + break; + default: + unitNb = numberOfUnitsString; + break; + } + + unitDTO.setUri(baseURI + "/u" + unitNb); + toReturn.add(unitDTO); + } + + return toReturn; + } + + private SPARQLUpdateBuilder prepareInsertQuery(UnitDTO unitDTO) { + SPARQLUpdateBuilder spql = new SPARQLUpdateBuilder(); + final URINamespaces uriNamespaces = new URINamespaces(); + + spql.appendGraphURI(uriNamespaces.getContextsProperty("variables")); + spql.appendTriplet(unitDTO.getUri(), "rdf:type", uriNamespaces.getObjectsProperty("cUnit"), null); + spql.appendTriplet(unitDTO.getUri(), "rdfs:label", "\"" + unitDTO.getLabel() + "\"", null); + spql.appendTriplet(unitDTO.getUri(), "rdfs:comment", "\"" + unitDTO.getComment() + "\"", null); + + for (OntologyReference ontologyReference : unitDTO.getOntologiesReferences()) { + spql.appendTriplet(unitDTO.getUri(), ontologyReference.getProperty(), ontologyReference.getObject(), null); + spql.appendTriplet(ontologyReference.getObject(), "rdfs:seeAlso", "\"" + ontologyReference.getSeeAlso() + "\"", null); + } + + return spql; + } + + /** + * insère les données dans le triplestore + * On suppose que la vérification de leur intégrité a été faite auparavent, via l'appel à la méthode check + * @param unitsDTO + * @return + */ + public POSTResultsReturn insert(List unitsDTO) { + List insertStatusList = new ArrayList<>(); + List createdResourcesURI = new ArrayList<>(); + + POSTResultsReturn results; + boolean resultState = false; //Pour savoir si les données sont bonnes et ont bien été insérées + boolean annotationInsert = true; //Si l'insertion a bien été faite + + unitsDTO = generateURIs(unitsDTO); + final Iterator iteratorUnitDTO = unitsDTO.iterator(); + + while (iteratorUnitDTO.hasNext() && annotationInsert) { + UnitDTO unitDTO = iteratorUnitDTO.next(); + + //Enregistrement dans le triplestore + SPARQLUpdateBuilder spqlInsert = prepareInsertQuery(unitDTO); + + try { + //SILEX:test + //Toute la notion de connexion au triplestore sera à revoir. + //C'est un hot fix qui n'est pas propre + String sesameServer = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "sesameServer"); + String repositoryID = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "repositoryID"); + rep = new HTTPRepository(sesameServer, repositoryID); //Stockage triplestore Sesame + rep.initialize(); + this.setConnection(rep.getConnection()); + this.getConnection().begin(); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, spqlInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareUpdate.execute(); + //\SILEX:test + + createdResourcesURI.add(unitDTO.getUri()); + + if (annotationInsert) { + resultState = true; + getConnection().commit(); + } else { + getConnection().rollback(); + } + } catch (RepositoryException ex) { + LOGGER.error("Error during commit or rolleback Triplestore statements: ", ex); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationInsert = false; + insertStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed insertion query: " + e.getMessage())); + } + } + + results = new POSTResultsReturn(resultState, annotationInsert, true); + results.statusList = insertStatusList; + results.setCreatedResources(createdResourcesURI); + if (resultState && !createdResourcesURI.isEmpty()) { + results.createdResources = createdResourcesURI; + results.statusList.add(new Status("Resources created", StatusCodeMsg.INFO, createdResourcesURI.size() + " new resource(s) created.")); + } + + return results; + } + + /** + * Vérifie les données et les insère dans le triplestore. + * @param unitsDTO + * @return POSTResultsReturn le résultat de la tentative d'insertion + */ + public POSTResultsReturn checkAndInsert(List unitsDTO) { + POSTResultsReturn checkResult = check(unitsDTO); + if (checkResult.getDataState()) { + return insert(unitsDTO); + } else { //Les données ne sont pas bonnes + return checkResult; + } + } + + /** + * + * @param uri + * @return la liste des liens vers d'autres ontologies + */ + private SPARQLQueryBuilder prepareSearchOntologiesReferencesQuery(String uri) { + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + + query.appendDistinct(Boolean.TRUE); + query.appendGraph(uriNamespaces.getContextsProperty("variables")); + + if (ontologiesReferences.isEmpty()) { + query.appendSelect(" ?property ?object ?seeAlso"); + query.appendTriplet(uri, "?property", "?object", null); + query.appendOptional("{?object rdfs:seeAlso ?seeAlso}"); + query.appendFilter("?property IN(<" + uriNamespaces.getRelationsProperty("rCloseMatch") + ">, <" + + uriNamespaces.getRelationsProperty("rExactMatch") + ">, <" + + uriNamespaces.getRelationsProperty("rNarrower") + ">, <" + + uriNamespaces.getRelationsProperty("rBroader") + ">)"); + } else { + for (OntologyReference ontologyReference : ontologiesReferences) { + query.appendTriplet(uri, ontologyReference.getProperty(), ontologyReference.getObject(), null); + query.appendTriplet(ontologyReference.getObject(), "rdfs:seeAlso", ontologyReference.getSeeAlso(), null); + } + } + + LOGGER.trace("SPARQL select query : " + query.toString()); + return query; + } + + /** + * + * @return la liste des unités correspondant à la recherche + */ + public ArrayList allPaginate() { + SPARQLQueryBuilder query = prepareSearchQuery(); + TupleQuery tupleQuery = getConnection().prepareTupleQuery(QueryLanguage.SPARQL, query.toString()); + ArrayList units = new ArrayList<>(); + + try (TupleQueryResult result = tupleQuery.evaluate()) { + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + Unit unit = new Unit(); + + if (uri != null) { + unit.setUri(uri); + } else { + unit.setUri(bindingSet.getValue("uri").stringValue()); + } + + if (label != null) { + unit.setLabel(label); + } else { + unit.setLabel(bindingSet.getValue("label").stringValue()); + } + + if (comment != null) { + unit.setComment(comment); + } else { + unit.setComment(bindingSet.getValue("comment").stringValue()); + } + + //On récupère maintenant la liste des références vers des ontologies... + SPARQLQueryBuilder queryOntologiesReferences = prepareSearchOntologiesReferencesQuery(unit.getUri()); + TupleQuery tupleQueryOntologiesReferences = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, queryOntologiesReferences.toString()); + TupleQueryResult resultOntologiesReferences = tupleQueryOntologiesReferences.evaluate(); + while (resultOntologiesReferences.hasNext()) { + BindingSet bindingSetOntologiesReferences = resultOntologiesReferences.next(); + if (bindingSetOntologiesReferences.getValue("object") != null + && bindingSetOntologiesReferences.getValue("property") != null) { + OntologyReference ontologyReference = new OntologyReference(); + ontologyReference.setObject(bindingSetOntologiesReferences.getValue("object").toString()); + ontologyReference.setProperty(bindingSetOntologiesReferences.getValue("property").toString()); + if (bindingSetOntologiesReferences.getValue("seeAlso") != null) { + ontologyReference.setSeeAlso(bindingSetOntologiesReferences.getValue("seeAlso").toString()); + } + + unit.addOntologyReference(ontologyReference); + } + } + + units.add(unit); + } + } + + return units; + } + + private String prepareDeleteQuery(Unit unit) { + String deleteQuery; + deleteQuery = "DELETE WHERE {" + + "<" + unit.getUri() + "> rdfs:label \"" + unit.getLabel() + "\" . " + + "<" + unit.getUri() + "> rdfs:comment \"" + unit.getComment() + "\" . "; + + for (OntologyReference ontologyReference : unit.getOntologiesReferences()) { + deleteQuery += "<" + unit.getUri() + "> <" + ontologyReference.getProperty() + "> <" + ontologyReference.getObject() + "> . "; + if (ontologyReference.getSeeAlso() != null) { + deleteQuery += "<" + ontologyReference.getObject() + "> rdfs:seeAlso " + ontologyReference.getSeeAlso() + " . "; + } + } + + deleteQuery += "}"; + + return deleteQuery; + } + + private POSTResultsReturn update(List unitsDTO) { + List updateStatusList = new ArrayList<>(); + List updatedResourcesURIList = new ArrayList<>(); + POSTResultsReturn results; + + boolean annotationUpdate = true; // Si l'insertion a bien été réalisée + boolean resultState = false; // Pour savoir si les données étaient bonnes et on bien été mises à jour + + for (UnitDTO unitDTO : unitsDTO) { + //1. Suppression des données déjà existantes + //1.1 Récupération des infos qui seront modifiées (pour supprimer les bons triplets) + uri = unitDTO.getUri(); + ArrayList unitsCorresponding = allPaginate(); + if (unitsCorresponding.size() > 0) { + String deleteQuery = prepareDeleteQuery(unitsCorresponding.get(0)); + + //2. Insertion des nouvelles données + SPARQLUpdateBuilder queryInsert = prepareInsertQuery(unitDTO); + try { + // début de la transaction : vérification de la requête + this.getConnection().begin(); + Update prepareDelete = this.getConnection().prepareUpdate(deleteQuery); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, queryInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareDelete.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareDelete.execute(); + prepareUpdate.execute(); + + updatedResourcesURIList.add(unitDTO.getUri()); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationUpdate = false; + updateStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed update query: " + e.getMessage())); + } + } else { + annotationUpdate = false; + updateStatusList.add(new Status("Unknown instance", StatusCodeMsg.ERR, "Unknown unit " + unitDTO.getUri())); + } + } + + if (annotationUpdate) { + resultState = true; + try { + this.getConnection().commit(); + } catch (RepositoryException ex) { + LOGGER.error("Error during commit Triplestore statements: ", ex); + } + } else { + // retour en arrière sur la transaction + try { + this.getConnection().rollback(); + } catch (RepositoryException ex) { + LOGGER.error("Error during rollback Triplestore statements : ", ex); + } + } + + results = new POSTResultsReturn(resultState, annotationUpdate, true); + results.statusList = updateStatusList; + if (resultState && !updatedResourcesURIList.isEmpty()) { + results.createdResources = updatedResourcesURIList; + results.statusList.add(new Status("Resources updated", StatusCodeMsg.INFO, updatedResourcesURIList.size() + " resources updated")); + } + + return results; + } + + /** + * Vérifie les données et met à jour le triplestore + * @param unitsDTO + * @return POSTResultsReturn le résultat de la tentative de modification des données + */ + public POSTResultsReturn checkAndUpdate(List unitsDTO) { + POSTResultsReturn checkResult = check(unitsDTO); + if (checkResult.getDataState()) { + return update(unitsDTO); + } else { //Les données ne sont pas bonnes + return checkResult; + } + } +} + diff --git a/phis2-ws/src/main/java/phis2ws/service/dao/sesame/VariableDaoSesame.java b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/VariableDaoSesame.java new file mode 100644 index 000000000..3b424fb78 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/dao/sesame/VariableDaoSesame.java @@ -0,0 +1,570 @@ +//********************************************************************************************** +// VariableDaoSesame.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 16 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 16 2017 +// Subject: A specific DAO to retreive data on variables +//*********************************************************************************************** +package phis2ws.service.dao.sesame; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.query.Update; +import org.eclipse.rdf4j.repository.RepositoryException; +import org.eclipse.rdf4j.repository.http.HTTPRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.configuration.URINamespaces; +import phis2ws.service.dao.manager.DAOSesame; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.resources.dto.VariableDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.sparql.SPARQLQueryBuilder; +import phis2ws.service.utils.sparql.SPARQLUpdateBuilder; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.model.phis.InstanceDefinition; +import phis2ws.service.view.model.phis.Method; +import phis2ws.service.view.model.phis.OntologyReference; +import phis2ws.service.view.model.phis.Trait; +import phis2ws.service.view.model.phis.Unit; +import phis2ws.service.view.model.phis.Variable; + +public class VariableDaoSesame extends DAOSesame { + + final static Logger LOGGER = LoggerFactory.getLogger(VariableDaoSesame.class); + + public String trait; + public String method; + public String unit; + public String uri; + public String label; + public String comment; + public ArrayList ontologiesReferences = new ArrayList<>(); + + public VariableDaoSesame() { + + } + + @Override + protected SPARQLQueryBuilder prepareSearchQuery() { + //SILEX:todo + //Ajouter la recherche par référence vers d'autres ontologies aussi + //\SILEX:todo + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + query.appendDistinct(Boolean.TRUE); + query.appendGraph(uriNamespaces.getContextsProperty("variables")); + String variableURI; + if (uri != null) { + variableURI = "<" + uri + ">"; + } else { + variableURI = "?uri"; + query.appendSelect("?uri"); + } + query.appendTriplet(variableURI, "rdf:type", uriNamespaces.getObjectsProperty("cVariable"), null); + + if (label != null) { + query.appendTriplet(variableURI, "rdfs:label","\"" + label + "\"", null); + } else { + query.appendSelect(" ?label"); + query.appendTriplet(variableURI, "rdfs:label", "?label", null); + } + + if (comment != null) { + query.appendTriplet(variableURI, "rdfs:comment", "\"" + comment + "\"", null); + } else { + query.appendSelect(" ?comment"); + query.appendTriplet(variableURI, "rdfs:comment", " ?comment", null); + } + + if (trait != null) { + query.appendTriplet(variableURI, uriNamespaces.getRelationsProperty("rHasTrait"), trait, null); + } else { + query.appendSelect(" ?trait"); + query.appendTriplet(variableURI, uriNamespaces.getRelationsProperty("rHasTrait"), "?trait", null); + } + + if (method != null) { + query.appendTriplet(variableURI, uriNamespaces.getRelationsProperty("rHasMethod"), method, null); + } else { + query.appendSelect(" ?method"); + query.appendTriplet(variableURI, uriNamespaces.getRelationsProperty("rHasMethod"), "?method", null); + } + + if (unit != null) { + query.appendTriplet(variableURI, uriNamespaces.getRelationsProperty("rHasUnit"), unit, null); + } else { + query.appendSelect(" ?unit"); + query.appendTriplet(variableURI, uriNamespaces.getRelationsProperty("rHasUnit"), "?unit", null); + } + + LOGGER.trace("sparql select query : " + query.toString()); + return query; + } + + @Override + public Integer count() throws RepositoryException, MalformedQueryException, QueryEvaluationException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + /** + * Vérifie si les variables sont correctes + * @param variablesDTO + * @return + */ + public POSTResultsReturn check(List variablesDTO) { + //Résultats attendus + POSTResultsReturn variablesCheck = null; + //Liste des status retournés + List checkStatusList = new ArrayList<>(); + boolean dataOk = true; + + URINamespaces uriNamespaces = new URINamespaces(); + + for (VariableDTO variableDTO : variablesDTO) { + //Vérification des variables + if ((boolean) variableDTO.isOk().get("state")) { //Données attendues reçues + //On vérifie que le trait, la méthode et l'unité sont bien dans la base de données + if (!existObject(variableDTO.getMethod()) + || !existObject(variableDTO.getTrait()) + || !existObject(variableDTO.getUnit())) { + dataOk = false; + checkStatusList.add(new Status("Wrong value", StatusCodeMsg.ERR, "Unknown trait(" + variableDTO.getTrait() + ") or method (" + variableDTO.getMethod() + ") or unit (" + variableDTO.getUnit() + ")")); + } else { + //Vérification des relations d'ontologies reference + for (OntologyReference ontologyReference : variableDTO.getOntologiesReferences()) { + if (!ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rExactMatch")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rCloseMatch")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rNarrower")) + && !ontologyReference.getProperty().equals(uriNamespaces.getRelationsProperty("rBroader"))) { + dataOk = false; + checkStatusList.add(new Status("Wrong value", StatusCodeMsg.ERR, + "Bad property relation given. Must be one of the following : " + uriNamespaces.getRelationsProperty("rExactMatch") + + ", " + uriNamespaces.getRelationsProperty("rCloseMatch") + + ", " + uriNamespaces.getRelationsProperty("rNarrower") + + ", " + uriNamespaces.getRelationsProperty("rBroader") + +". Given : " + ontologyReference.getProperty())); + } + } + } + } else { + dataOk = false; + variableDTO.isOk().remove("state"); + checkStatusList.add(new Status("Bad data format", StatusCodeMsg.ERR, new StringBuilder().append(StatusCodeMsg.MISSINGFIELDS).append(variableDTO.isOk()).toString())); + } + } + + variablesCheck = new POSTResultsReturn(dataOk, null, dataOk); + variablesCheck.statusList = checkStatusList; + return variablesCheck; + } + + /** + * + * @return la requete permettant de connaitre le nombre de variables + */ + private SPARQLQueryBuilder prepareGetVariablesNumber() { + URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder spqlQuery = new SPARQLQueryBuilder(); + spqlQuery.appendGraph(uriNamespaces.getContextsProperty("variables")); + spqlQuery.appendSelect("(count(?variable) as ?count)"); + spqlQuery.appendTriplet("?variable", "rdf:type", uriNamespaces.getObjectsProperty("cVariable"), null); + + LOGGER.trace("sparql select query : " + spqlQuery.toString()); + + return spqlQuery; + } + + public int getNumberOfVariables() { + SPARQLQueryBuilder spqlQuery = prepareGetVariablesNumber(); + + //SILEX:test + //Pour les soucis de pool de connexion + rep = new HTTPRepository(SESAME_SERVER, REPOSITORY_ID); //Stockage triplestore Sesame + rep.initialize(); + setConnection(rep.getConnection()); + //\SILEX:test + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, spqlQuery.toString()); + TupleQueryResult result = tupleQuery.evaluate(); + //SILEX:test + //Pour les soucis de pool de connexion + getConnection().close(); + //\SILEX:test + + BindingSet bindingSet = result.next(); + String countResult = bindingSet.getValue("count").stringValue(); + + return Integer.parseInt(countResult); + } + + /** + * génère les uris des variables + * @param variablesDTO + * @return la liste des variables, avec les URIs en plus + */ + private ArrayList generateURIs(List variablesDTO) { + URINamespaces uriNamespaces = new URINamespaces(); + String baseURI = uriNamespaces.getNamespaceProperty("variables"); + ArrayList toReturn = new ArrayList<>(); + + //Récupération du numéro de la dernière uri de variable (pour l'autoincrement) + int numberOfVariables = getNumberOfVariables(); + + for (VariableDTO variableDTO : variablesDTO) { + numberOfVariables++; + + //On calcule le nombre de 0 à ajouter (l'id de la variable doit être du type : 001) + //avec 3 chiffres. + String variableNb; + String numberOfVariablesString = Integer.toString(numberOfVariables); + switch (numberOfVariablesString.length()) { + case 1: + variableNb = "00" + numberOfVariablesString; + break; + case 2: + variableNb = "0" + numberOfVariablesString; + break; + default: + variableNb = numberOfVariablesString; + break; + } + + variableDTO.setUri(baseURI + "/v" + variableNb); + toReturn.add(variableDTO); + } + + return toReturn; + } + + private SPARQLUpdateBuilder prepareInsertQuery(VariableDTO variable) { + SPARQLUpdateBuilder spql = new SPARQLUpdateBuilder(); + final URINamespaces uriNamespaces = new URINamespaces(); + + spql.appendGraphURI(uriNamespaces.getContextsProperty("variables")); + spql.appendTriplet(variable.getUri(), "rdf:type", uriNamespaces.getObjectsProperty("cVariable"), null); + spql.appendTriplet(variable.getUri(), "rdfs:label", "\"" + variable.getLabel() + "\"", null); + spql.appendTriplet(variable.getUri(), "rdfs:comment", "\"" + variable.getComment() + "\"", null); + spql.appendTriplet(variable.getUri(), uriNamespaces.getRelationsProperty("rHasTrait"), variable.getTrait(), null); + spql.appendTriplet(variable.getUri(), uriNamespaces.getRelationsProperty("rHasMethod"), variable.getMethod(), null); + spql.appendTriplet(variable.getUri(), uriNamespaces.getRelationsProperty("rHasUnit"), variable.getUnit(), null); + + for (OntologyReference ontologyReference : variable.getOntologiesReferences()) { + spql.appendTriplet(variable.getUri(), ontologyReference.getProperty(), ontologyReference.getObject(), null); + spql.appendTriplet(ontologyReference.getObject(), "rdfs:seeAlso", "\"" + ontologyReference.getSeeAlso() + "\"", null); + } + + return spql; + } + + /** + * insère les données dans le triplestore. + * On suppose que la vérification de leur intégrité a été faite auparavent, via l'appel à la méthode check + * @param variablesDTO + * @return + */ + public POSTResultsReturn insert(List variablesDTO) { + List insertStatusList = new ArrayList<>(); + List createdResourcesURIList = new ArrayList<>(); + + POSTResultsReturn results; + + boolean resultState = false; //Pour savoir si les données sont bonnes et ont bien été insérées + boolean annotationInsert = true; //Si l'insertion a bien été effectuée + + variablesDTO = generateURIs(variablesDTO); + final Iterator iteratorVariablesDTO = variablesDTO.iterator(); + + while (iteratorVariablesDTO.hasNext() && annotationInsert) { + VariableDTO variableDTO = iteratorVariablesDTO.next(); + + //Enregistrement dans le triplestore + SPARQLUpdateBuilder spqlInsert = prepareInsertQuery(variableDTO); + try { + //SILEX:test + //Toute la notion de connexion au triplestore sera à revoir. + //C'est un hot fix qui n'est pas propre + String sesameServer = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "sesameServer"); + String repositoryID = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILENAME, "repositoryID"); + rep = new HTTPRepository(sesameServer, repositoryID); //Stockage triplestore Sesame + rep.initialize(); + this.setConnection(rep.getConnection()); + this.getConnection().begin(); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, spqlInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareUpdate.execute(); + //\SILEX:test + + createdResourcesURIList.add(variableDTO.getUri()); + + if (annotationInsert) { + resultState = true; + getConnection().commit(); + } else { + getConnection().rollback(); + } + } catch (RepositoryException ex) { + LOGGER.error("Error during commit or rolleback Triplestore statements: ", ex); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationInsert = false; + insertStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed insertion query: " + e.getMessage())); + } + } + + results = new POSTResultsReturn(resultState, annotationInsert, true); + results.statusList = insertStatusList; + results.setCreatedResources(createdResourcesURIList); + if (resultState && !createdResourcesURIList.isEmpty()) { + results.createdResources = createdResourcesURIList; + results.statusList.add(new Status("Resources created", StatusCodeMsg.INFO, createdResourcesURIList.size() + " new resource(s) created.")); + } + + return results; + } + + /** + * Vérifie les données et les insère dans le triplestore + * @param variablesDTO + * @return POSTResultsReturn le résultat de la tentative d'insertion + */ + public POSTResultsReturn checkAndInsert(List variablesDTO) { + POSTResultsReturn checkResult = check(variablesDTO); + if (checkResult.getDataState()) { + return insert(variablesDTO); + } else { //Les données ne sont pas bonnes + return checkResult; + } + } + + /** + * + * @param uri + * @return la liste des liens vers d'autres ontologies + */ + private SPARQLQueryBuilder prepareSearchOntologiesReferencesQuery(String uri) { + final URINamespaces uriNamespaces = new URINamespaces(); + SPARQLQueryBuilder query = new SPARQLQueryBuilder(); + + query.appendDistinct(Boolean.TRUE); + query.appendGraph(uriNamespaces.getContextsProperty("variables")); + + if (ontologiesReferences.isEmpty()) { + query.appendSelect(" ?property ?object ?seeAlso"); + query.appendTriplet(uri, "?property", "?object", null); + query.appendOptional("{?object rdfs:seeAlso ?seeAlso}"); + query.appendFilter("?property IN(<" + uriNamespaces.getRelationsProperty("rCloseMatch") + ">, <" + + uriNamespaces.getRelationsProperty("rExactMatch") + ">, <" + + uriNamespaces.getRelationsProperty("rNarrower") + ">, <" + + uriNamespaces.getRelationsProperty("rBroader") + ">)"); + } else { + for (OntologyReference ontologyReference : ontologiesReferences) { + query.appendTriplet(uri, ontologyReference.getProperty(), ontologyReference.getObject(), null); + query.appendTriplet(ontologyReference.getObject(), "rdfs:seeAlso", ontologyReference.getSeeAlso(), null); + } + } + + LOGGER.trace("SPARQL select query : " + query.toString()); + return query; + } + + /** + * + * @return la liste des variables correspondant à la recherche + */ + public ArrayList allPaginate() { + SPARQLQueryBuilder query = prepareSearchQuery(); + TupleQuery tupleQuery = getConnection().prepareTupleQuery(QueryLanguage.SPARQL, query.toString()); + ArrayList variables = new ArrayList<>(); + + try (TupleQueryResult result = tupleQuery.evaluate()) { + while (result.hasNext()) { + BindingSet bindingSet = result.next(); + Variable variable = new Variable(); + + if (uri != null) { + variable.setUri(uri); + } else { + variable.setUri(bindingSet.getValue("uri").stringValue()); + } + + if (label != null) { + variable.setLabel(label); + } else { + variable.setLabel(bindingSet.getValue("label").stringValue()); + } + + if (comment != null) { + variable.setComment(comment); + } else { + variable.setComment(bindingSet.getValue("comment").stringValue()); + } + + TraitDaoSesame traitDaoSesame = new TraitDaoSesame(); + if (trait != null) { + traitDaoSesame.uri = trait; + } else { + traitDaoSesame.uri = bindingSet.getValue("trait").stringValue(); + } + + MethodDaoSesame methodDaoSesame = new MethodDaoSesame(); + if (method != null) { + methodDaoSesame.uri = method; + } else { + methodDaoSesame.uri = bindingSet.getValue("method").stringValue(); + } + + UnitDaoSesame unitDaoSesame = new UnitDaoSesame(); + if (unit != null) { + unitDaoSesame.uri = unit; + } else { + unitDaoSesame.uri = bindingSet.getValue("unit").stringValue(); + } + + //On récupère maintenant la liste des références vers des ontologies... + SPARQLQueryBuilder queryOntologiesReferences = prepareSearchOntologiesReferencesQuery(variable.getUri()); + TupleQuery tupleQueryOntologiesReferences = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, queryOntologiesReferences.toString()); + TupleQueryResult resultOntologiesReferences = tupleQueryOntologiesReferences.evaluate(); + while (resultOntologiesReferences.hasNext()) { + BindingSet bindingSetOntologiesReferences = resultOntologiesReferences.next(); + if (bindingSetOntologiesReferences.getValue("object") != null + && bindingSetOntologiesReferences.getValue("property") != null) { + OntologyReference ontologyReference = new OntologyReference(); + ontologyReference.setObject(bindingSetOntologiesReferences.getValue("object").toString()); + ontologyReference.setProperty(bindingSetOntologiesReferences.getValue("property").toString()); + if (bindingSetOntologiesReferences.getValue("seeAlso") != null) { + ontologyReference.setSeeAlso(bindingSetOntologiesReferences.getValue("seeAlso").toString()); + } + + variable.addOntologyReference(ontologyReference); + } + } + + //On récupère les informations du trait, de la méthode et de l'unité + ArrayList traits = traitDaoSesame.allPaginate(); + ArrayList methods = methodDaoSesame.allPaginate(); + ArrayList units = unitDaoSesame.allPaginate(); + variable.setTrait(traits.get(0)); + variable.setMethod(methods.get(0)); + variable.setUnit(units.get(0)); + + variables.add(variable); + } + } + + return variables; + } + + private String prepareDeleteQuery(Variable variable) { + URINamespaces uriNamespaces = new URINamespaces(); + String deleteQuery; + deleteQuery = "DELETE WHERE {" + + "<" + variable.getUri() + "> rdfs:label \"" + variable.getLabel() + "\" . " + + "<" + variable.getUri() + "> rdfs:comment \"" + variable.getComment() + "\" . " + + "<" + variable.getUri() + "> <" + uriNamespaces.getRelationsProperty("rHasTrait") + "> <" + variable.getTrait() + "> . " + + "<" + variable.getUri() + "> <" + uriNamespaces.getRelationsProperty("rHasMethod") + "> <" + variable.getMethod() + "> . " + + "<" + variable.getUri() + "> <" + uriNamespaces.getRelationsProperty("rHasUnit") + "> <" + variable.getUnit() + "> . "; + + for (OntologyReference ontologyReference : variable.getOntologiesReferences()) { + deleteQuery += "<" + variable.getUri() + "> <" + ontologyReference.getProperty() + "> <" + ontologyReference.getObject() + "> . "; + if (ontologyReference.getSeeAlso() != null) { + deleteQuery += "<" + ontologyReference.getObject() + "> rdfs:seeAlso " + ontologyReference.getSeeAlso() + " . "; + } + } + + deleteQuery += "}"; + + return deleteQuery; + } + + private POSTResultsReturn update(List variablesDTO) { + List updateStatusList = new ArrayList<>(); + List updatedResourcesURIList = new ArrayList<>(); + POSTResultsReturn results; + + boolean annotationUpdate = true; // Si l'insertion a bien été réalisée + boolean resultState = false; // Pour savoir si les données étaient bonnes et on bien été mises à jour + + for (VariableDTO variableDTO : variablesDTO) { + //1. Suppression des données déjà existantes + //1.1 Récupération des infos qui seront modifiées (pour supprimer les bons triplets) + uri = variableDTO.getUri(); + ArrayList variablesCorresponding = allPaginate(); + if (variablesCorresponding.size() > 0) { + String deleteQuery = prepareDeleteQuery(variablesCorresponding.get(0)); + + //2. Insertion des nouvelles données + SPARQLUpdateBuilder queryInsert = prepareInsertQuery(variableDTO); + try { + // début de la transaction : vérification de la requête + this.getConnection().begin(); + Update prepareDelete = this.getConnection().prepareUpdate(deleteQuery); + Update prepareUpdate = this.getConnection().prepareUpdate(QueryLanguage.SPARQL, queryInsert.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareDelete.toString()); + LOGGER.trace(getTraceabilityLogs() + " query : " + prepareUpdate.toString()); + prepareDelete.execute(); + prepareUpdate.execute(); + + updatedResourcesURIList.add(variableDTO.getUri()); + } catch (MalformedQueryException e) { + LOGGER.error(e.getMessage(), e); + annotationUpdate = false; + updateStatusList.add(new Status("Query error", StatusCodeMsg.ERR, "Malformed update query: " + e.getMessage())); + } + } else { + annotationUpdate = false; + updateStatusList.add(new Status("Unknown instance", StatusCodeMsg.ERR, "Unknown variable " + variableDTO.getUri())); + } + } + + if (annotationUpdate) { + resultState = true; + try { + this.getConnection().commit(); + } catch (RepositoryException ex) { + LOGGER.error("Error during commit Triplestore statements: ", ex); + } + } else { + // retour en arrière sur la transaction + try { + this.getConnection().rollback(); + } catch (RepositoryException ex) { + LOGGER.error("Error during rollback Triplestore statements : ", ex); + } + } + + results = new POSTResultsReturn(resultState, annotationUpdate, true); + results.statusList = updateStatusList; + if (resultState && !updatedResourcesURIList.isEmpty()) { + results.createdResources = updatedResourcesURIList; + results.statusList.add(new Status("Resources updated", StatusCodeMsg.INFO, updatedResourcesURIList.size() + " resources updated")); + } + + return results; + } + + /** + * Vérifie les données et met à jour le triplestore + * @param variablesDTO + * @return POSTResultsReturn le résultat de la tentative de modification des données + */ + public POSTResultsReturn checkAndUpdate(List variablesDTO) { + POSTResultsReturn checkResult = check(variablesDTO); + if (checkResult.getDataState()) { + return update(variablesDTO); + } else { //Les données ne sont pas bonnes + return checkResult; + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/documentation/DocumentationAnnotation.java b/phis2-ws/src/main/java/phis2ws/service/documentation/DocumentationAnnotation.java new file mode 100644 index 000000000..479ca5b2c --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/documentation/DocumentationAnnotation.java @@ -0,0 +1,160 @@ +//********************************************************************************************** +// DocumentationAnnotation.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact: arnaud.charleroy@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: February, 2017 +// Subject: A class which group documentation informations ( try message bundle for the next version) +//*********************************************************************************************** +package phis2ws.service.documentation; + +import javax.inject.Singleton; +import phis2ws.service.PropertiesFileManager; + +@Singleton +public final class DocumentationAnnotation { + + /** + * Par defaut la page est 0 + */ + public static final String PAGE = "Current page number"; + /** + * A mettre a jour si changement de la variable dans service properties + * + * @see service.properties + */ + public static final String PAGE_SIZE = "Number of elements per page (limited to 150000)"; + + public static final String PAGE_SIZE_MONGO = "Number of elements per page (limited to 1000000)"; + + // user messages + public static final String ERROR_SEND_DATA = "Server error. Cannot send data."; + public static final String ERROR_FETCH_DATA = "Server error. Cannot fetch data."; + public static final String BAD_USER_INFORMATION = "Bad informations send by user"; + public static final String ACCES_TOKEN = "Access token given"; + public static final String SQL_ERROR_FETCH_DATA = "SQL Error can't fetch results"; + public static final String NO_RESULTS = "No data found"; + public static final String USER_NOT_AUTHORIZED = "You aren't authorized to fetch the result of this ressource call"; + + public static final String SWAGGER_DOCUMENTATION_HEADER = + "This page describes the methods allowed by this web service.

You must read the paragraph below before use it !

" + + "
" + + "
    " + + "
  1. " + + " 1. You must first retrieve an acces token using the \"token\" call (fill with your PHIS-SILEX username and password) and after you will be able to use other service calls.
  2. " + + "
  3. " + + " 2. You must fill the sessionId parameter with the created access token on each call.

    This token is available during " + Integer.valueOf(PropertiesFileManager.getConfigFileProperty("service", "sessionTime")) + " seconds.

    This time will be reload at each use in order to keep the token valid without retrieve a new one.
  4. " + + "
" + + "
" + + "The response call example values shown in this api documentation represent the data array which is located in the response result object

except for the token call.


" + + "The token also include the response object header. " + + "For more information, the Response object definition is available at Brapi response object."; + + // defintions des variables + public static final String EXPERIMENT_URI_DEFINITION = "An experiment URI (Unique Resource Identifier)"; + public static final String EXPERIMENT_POST_DATA_DEFINITION = "JSON format of experiment data"; + + public static final String PROJECT_URI_DEFINITION = "A project URI (Unique Resource Identifier)"; + public static final String PROJECT_POST_DATA_DEFINITION = "JSON format of project data"; + + public static final String AGRONOMICAL_OBJECT_URI_DEFINITION = "Agronomical object URI (Unique Resource Identifier)"; + public static final String AGRONOMICAL_OBJECT_POST_DATA_DEFINITION = "JSON format of agronomical object data"; + + public static final String GROUP_URI_DEFINITION = "A group uri"; + public static final String GROUP_POST_DATA_DEFINITION = "JSON format of group data"; + + public static final String USER_EMAIL_DEFINITION = "A user email"; + public static final String USER_POST_DATA_DEFINITION = "JSON format of user data"; + + public static final String VARIABLE_POST_DATA_DEFINITION = "JSON format of variable data"; + public static final String VARIABLE_URI_DEFINITION = "A variable URI (Unique Resource Identifier)"; + + public static final String TRAIT_POST_DATA_DEFINITION = "JSON format of trait"; + public static final String TRAIT_URI_DEFINITION = "A trait URI (Unique Resource Identifier)"; + + public static final String METHOD_POST_DATA_DEFINITION = "JSON format of method"; + public static final String METHOD_URI_DEFINITION = "A method URI (Unique Resource Identifier)"; + + public static final String UNIT_POST_DATA_DEFINITION = "JSON format of unit"; + public static final String UNIT_URI_DEFINITION = "A unit URI (Unique Resource Identifier)"; + + public static final String VARIABLES_DEFINITION = "A variable or comma-separated variables list"; + + public static final String ADMIN_ONLY_NOTES = "This can only be done by a PHIS-SILEX admin."; + public static final String USER_ONLY_NOTES = "This can only be done by a PHIS-SILEX user."; + + public static final String DOCUMENT_URI_DEFINITION = "A document URI (Unique Resource Identifier)"; + + public static final String LAYER_POST_DATA_DEFINITION = "JSON format of requested layer"; + + public static final String RAW_DATA_POST_DATA_DEFINITION = "JSON format of raw data"; + + // Exemples de variables + public static final String EXAMPLE_EXPERIMENT_URI = "http://phenome-fppn.fr/diaphen/DIA2012-1"; + public static final String EXAMPLE_EXPERIMENT_START_DATE = "2015-07-07 00:00:00+02"; + public static final String EXAMPLE_EXPERIMENT_END_DATE = "2015-08-07 00:00:00+02"; + public static final String EXAMPLE_EXPERIMENT_FIELD = "field"; + public static final String EXAMPLE_EXPERIMENT_PLACE = "place"; + public static final String EXAMPLE_EXPERIMENT_ALIAS = "alias"; + public static final String EXAMPLE_EXPERIMENT_KEYWORDS = "keywords"; + public static final String EXAMPLE_EXPERIMENT_CAMPAIGN = "2012"; + + public static final String EXAMPLE_AGRONOMICAL_OBJECT_URI = "http://phenome-fppn.fr/phis_field/ao1"; + + public static final String EXAMPLE_PROJECT_URI = "http://phenome-fppn.fr/phis_field/projectTest"; + public static final String EXAMPLE_PROJECT_NAME = "projectTest"; + public static final String EXAMPLE_PROJECT_ACRONYME = "P T"; + public static final String EXAMPLE_PROJECT_SUBPROJECT_TYPE = "subproject type"; + public static final String EXAMPLE_PROJECT_FINANCIAL_SUPPORT = "financial support"; + public static final String EXAMPLE_PROJECT_FINANCIAL_NAME = "financial name"; + public static final String EXAMPLE_PROJECT_DATE_START = "2015-07-07"; + public static final String EXAMPLE_PROJECT_DATE_END = "2016-07-07"; + public static final String EXAMPLE_PROJECT_KEYWORDS = "keywords"; + public static final String EXAMPLE_PROJECT_PARENT_PROJECT = "parent project"; + public static final String EXAMPLE_PROJECT_SCIENTIFIC_CONTACT = "Morgane Vidal"; + public static final String EXAMPLE_PROJECT_ADMINISTRATIVE_CONTACT = "Morgane Vidal"; + public static final String EXAMPLE_PROJECT_PROJECT_COORDINATOR = "Morgane Vidal"; + public static final String EXAMPLE_PROJECT_WEBSITE = "http://example.com"; + public static final String EXAMPLE_PROJECT_TYPE = "project type"; + + public static final String EXAMPLE_GROUP_URI = "http://phenome-fppn.fr/mauguio/INRA-MISTEA-GAMMA"; + public static final String EXAMPLE_GROUP_NAME = "INRA-MISTEA-GAMMA"; + public static final String EXAMPLE_GROUP_LEVEL = "Owner"; + public static final String EXAMPLE_GROUP_AVAILABLE = "true"; + + public static final String EXAMPLE_USER_EMAIL = "morgane.vidal@inra.fr"; + public static final String EXAMPLE_USER_PASSWORD = "b30aebada8cb09d2cb686a1fdaec38fd"; + public static final String EXAMPLE_USER_FIRST_NAME = "Morgane"; + public static final String EXAMPLE_USER_FAMILY_NAME = "Vidal"; + public static final String EXAMPLE_USER_ADDRESS = "2 place Pierre Viala, Bat 29, Montpellier"; + public static final String EXAMPLE_USER_PHONE = "0400000000"; + public static final String EXAMPLE_USER_AFFILIATION = "affiliation"; + public static final String EXAMPLE_USER_ORCID = "orcid"; + public static final String EXAMPLE_USER_ADMIN = "true"; + public static final String EXAMPLE_USER_AVAILABLE = "true"; + + public static final String EXAMPLE_DOCUMENT_URI = "http://www.phenome-fppn.fr/phis_field/documents/documente597f57ba71d421a86277d830f4b9885"; + public static final String EXAMPLE_DOCUMENT_TYPE = "http://www.phenome-fppn.fr/vocabulary/2017#ScientificDocument"; + public static final String EXAMPLE_DOCUMENT_CREATOR = "John Doe"; + public static final String EXAMPLE_DOCUMENT_LANGUAGE = "fr"; + public static final String EXAMPLE_DOCUMENT_CREATION_DATE = "2017-07-07"; + public static final String EXAMPLE_DOCUMENT_EXTENSION = "png"; + public static final String EXAMPLE_DOCUMENT_TITLE = "title"; + public static final String EXAMPLE_DOCUMENT_CONCERNED_TYPE_URI = "http://www.phenome-fppn.fr/vocabulary/2017#Experiment"; + public static final String EXAMPLE_DOCUMENT_STATUS = "linked"; + + public static final String EXAMPLE_TRAIT_URI = "http://www.phenome-fppn.fr/phis_field/id/traits/t001"; + public static final String EXAMPLE_TRAIT_LABEL = "Height"; + + public static final String EXAMPLE_METHOD_URI = "http://www.phenome-fppn.fr/phis_field/id/methods/m001"; + public static final String EXAMPLE_METHOD_LABEL = "comptage"; + + public static final String EXAMPLE_UNIT_URI = "http://www.phenome-fppn.fr/phis_field/id/units/u001"; + public static final String EXAMPLE_UNIT_LABEL = "cm"; + + public static final String EXAMPLE_VARIABLE_URI = "http://www.phenome-fppn.fr/diaphen/id/variable/v0000001"; + public static final String EXAMPLE_VARIABLE_LABEL = "LAI"; +} diff --git a/phis2-ws/src/main/java/phis2ws/service/documentation/StatusCodeMsg.java b/phis2-ws/src/main/java/phis2ws/service/documentation/StatusCodeMsg.java new file mode 100644 index 000000000..0c6deadde --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/documentation/StatusCodeMsg.java @@ -0,0 +1,28 @@ +//********************************************************************************************** +// StatusCodeMsg.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which group possible status message return +// @see phenomeapi.service.view.brapi.Status +//*********************************************************************************************** +package phis2ws.service.documentation; + +import javax.inject.Singleton; + +/** + * Définit des messages de retour régulièrement utilisés + * @author Arnaud CHARLEROY + */ +@Singleton +public final class StatusCodeMsg { + public static final String ERR = "Error"; + public static final String INFO = "Info"; + public static final String ERRPG = "PostgresSQL Error"; + public static final String MISSINGFIELDS = "Missing Field(s) : "; + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/injection/SessionFactory.java b/phis2-ws/src/main/java/phis2ws/service/injection/SessionFactory.java new file mode 100644 index 000000000..b6d405a73 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/injection/SessionFactory.java @@ -0,0 +1,57 @@ +//********************************************************************************************** +// SessionFactory.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: custom HK2 Factory implementation that knows how to extract from http session +// @see https://jersey.java.net/documentation/latest/ioc.html +//*********************************************************************************************** +package phis2ws.service.injection; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import org.glassfish.hk2.api.Factory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.authentication.TokenManager; +import phis2ws.service.configuration.GlobalWebserviceValues; + +/** + * Extrait les donnees de la requete de l'utilisateur + * et les lie à l'objet de l'annotation personnalisee @SessionInject + * @author A. CHARLEROY + */ +public class SessionFactory implements Factory { + + final static Logger logger = LoggerFactory.getLogger(SessionFactory.class); + + private final HttpServletRequest request; + + @Inject + public SessionFactory(HttpServletRequest request) { + this.request = request; + } + + @Override + public Session provide() { + if (request != null) { + String sessionId = request.getHeader(GlobalWebserviceValues.AUTHORIZATION_PROPERTY); + if (sessionId != null) { + sessionId = sessionId.replace(GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ", ""); + if (TokenManager.Instance().checkAuthentification(sessionId)) { + return TokenManager.Instance().getSession(sessionId); + } + } + } + return null; + } + + @Override + public void dispose(Session t) { + } +} \ No newline at end of file diff --git a/phis2-ws/src/main/java/phis2ws/service/injection/SessionInject.java b/phis2-ws/src/main/java/phis2ws/service/injection/SessionInject.java new file mode 100644 index 000000000..e647d4a36 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/injection/SessionInject.java @@ -0,0 +1,32 @@ +//********************************************************************************************** +// SessionInject.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Annotation created to inject user data in webservice resources classes +// Custom Injection Provider +// see https://jersey.java.net/documentation/latest/ioc.html +//*********************************************************************************************** +package phis2ws.service.injection; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation pour recevoir les informations d'une session en cours (Ex : + * client, adresse Ip etc ...) + * + * @see https://jersey.java.net/documentation/latest/ioc.html + * @date 07/16 + * @author A. CHARLEROY + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SessionInject { +} diff --git a/phis2-ws/src/main/java/phis2ws/service/injection/SessionInjectResolver.java b/phis2-ws/src/main/java/phis2ws/service/injection/SessionInjectResolver.java new file mode 100644 index 000000000..0a9853cf7 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/injection/SessionInjectResolver.java @@ -0,0 +1,52 @@ +//********************************************************************************************** +// SessionInjectResolver.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Recognize sessionInject utilisation in jersey resources +// The SessionInjectResolver above just delegates to the default HK2 system injection resolver to do the actual work. +// see https://jersey.java.net/documentation/latest/ioc.html +//*********************************************************************************************** +package phis2ws.service.injection; + +import javax.inject.Inject; +import javax.inject.Named; +import org.glassfish.hk2.api.Injectee; +import org.glassfish.hk2.api.InjectionResolver; +import org.glassfish.hk2.api.ServiceHandle; +import phis2ws.service.authentication.Session; + +/** + * Permet de repérer l'utilisation de l'annotation Session Inject + * @see https://jersey.java.net/documentation/latest/ioc.html + * @author A. CHARLEROY + */ +public class SessionInjectResolver implements InjectionResolver { + + @Inject + @Named(InjectionResolver.SYSTEM_RESOLVER_NAME) + InjectionResolver SYSTEM_INJECTION_RESOLVER; + + @Override + public Object resolve(Injectee injectee, ServiceHandle handle) { + if (Session.class == injectee.getRequiredType()) { + return SYSTEM_INJECTION_RESOLVER.resolve(injectee, handle); + } + + return null; + } + + @Override + public boolean isConstructorParameterIndicator() { + return false; + } + + @Override + public boolean isMethodParameterIndicator() { + return false; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/json/CustomJsonWriterReader.java b/phis2-ws/src/main/java/phis2ws/service/json/CustomJsonWriterReader.java new file mode 100644 index 000000000..89a0eec6e --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/json/CustomJsonWriterReader.java @@ -0,0 +1,189 @@ +//********************************************************************************************** +// CustomJsonWriterReader.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Define the way to read incoming JSON and write output JSON +//*********************************************************************************************** +package phis2ws.service.json; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import static java.nio.charset.StandardCharsets.UTF_8; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.model.phis.Phenotype; + +/** + * Classe permettant de surcharger la classe qui permet la sérialization et la + * deserialisation du JSON dans Jersey + * + * @author Arnaud CHARLEROY + * @date 05/16 + * @param + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Provider +public final class CustomJsonWriterReader implements MessageBodyWriter, + MessageBodyReader { + + /** + * Récupération des erreurs + */ + final static Logger logger = LoggerFactory.getLogger(CustomJsonWriterReader.class); + + /** + * Permet de filtrer les classes qui sont lisibles + * + * @param type + * @param genericType + * @param annotations + * @param mediaType + * @return + */ + @Override + public boolean isReadable(Class type, Type genericType, + java.lang.annotation.Annotation[] annotations, MediaType mediaType) { + return true; + } + + /** + * Définit la façon de lire les éléments + * + * @param type + * @param genericType + * @param annotations + * @param mediaType + * @param httpHeaders + * @param entityStream + * @return + * @throws IOException + * @throws WebApplicationException + */ + @Override + public T readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) + throws IOException, WebApplicationException { + final Gson g = new Gson(); + try { + final BufferedReader reader = new BufferedReader(new InputStreamReader(entityStream)); + return g.fromJson(reader, genericType); + } catch (Exception e) { + logger.warn(e.getMessage(), e); + final ResponseFormPOST postResponse = new ResponseFormPOST(new Status("Unexpected JSON format", StatusCodeMsg.ERR, e.getMessage())); + throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build()); + } + } + + /** + * Indique si une classe peut être sérialisee par défaut elles le sont + * toutes ! + * + * @param type + * @param genericType + * @param annotations + * @param mediaType + * @return + */ + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return true; + } + + @Override + public long getSize(T object, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return 0; + } + + /** + * Définit comment sérialiser les classes + * + * @param t + * @param type + * @param genericType + * @param annotations + * @param mediaType + * @param httpHeaders + * @param entityStream + * @throws IOException + * @throws WebApplicationException + * @see + * https://github.com/plantbreeding/documentation/wiki/Best-Practices-and-Conventions + */ + @Override + public void writeTo(T t, Class type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) + throws IOException, WebApplicationException { + try { + Gson g = new GsonBuilder() + .registerTypeAdapter(Phenotype.class, new PhenotypeSerializer()) + + .serializeNulls() // Pour serialiser les variables null en nulll json + // see https://github.com/plantbreeding/documentation/wiki/Best-Practices-and-Conventions + .create(); + + // //Suppression de champs non voulus + // Put @Expose on field that you don't want serialize + // .addSerializationExclusionStrategy(new ExclusionStrategy() { + // @Override + // public boolean shouldSkipField(FieldAttributes fieldAttributes) { + // final Expose expose = fieldAttributes.getAnnotation(Expose.class); + // return expose != null && !expose.serialize(); + // } + // + // @Override + // public boolean shouldSkipClass(Class aClass) { + // return false; + // } + // }) + // .addDeserializationExclusionStrategy(new ExclusionStrategy() { + // @Override + // public boolean shouldSkipField(FieldAttributes fieldAttributes) { + // final Expose expose = fieldAttributes.getAnnotation(Expose.class); + // return expose != null && !expose.deserialize(); + // } + // + // @Override + // public boolean shouldSkipClass(Class aClass) { + // return false; + // } + // }) + try (OutputStreamWriter writer = new OutputStreamWriter(entityStream, UTF_8)) { + g.toJson(t, genericType, writer); + } + } catch (Exception gsonEx) { + logger.error(gsonEx.getMessage(), gsonEx); + throw new ProcessingException( + "Error serializing a " + type + " to the output stream", gsonEx); + } + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/json/PhenotypeSerializer.java b/phis2-ws/src/main/java/phis2ws/service/json/PhenotypeSerializer.java new file mode 100644 index 000000000..02c46e165 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/json/PhenotypeSerializer.java @@ -0,0 +1,86 @@ +//********************************************************************************************** +// PhenotypeSerializer.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: October, 23 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 23 2017 +// Subject: Serialize a Phenotype instance to JSON, +// used to have a different return from the model class for the GET phenotype +//*********************************************************************************************** +package phis2ws.service.json; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import phis2ws.service.view.model.phis.Data; +import phis2ws.service.view.model.phis.Phenotype; + +public class PhenotypeSerializer implements JsonSerializer { + /*Format souhaité : + { + agronomicalObject: "http://.....", + experiment: "http://....", + data: [ + { + date: "....", + value: "...", + variable: "http://...." + } + ] + }*/ + @Override + public JsonElement serialize(Phenotype src, Type typeOfSrc, JsonSerializationContext context) { + Map phenotypesDataToReturn = new HashMap<>(); + for (Data d : src.getData()) { + if (phenotypesDataToReturn.containsKey(d.getAgronomicalObject())) { + JsonObject agronomicalObjectData = new JsonObject(); + if (src.getVariableURI() != null) { + agronomicalObjectData.add("variable", new JsonPrimitive(src.getVariableURI())); + } else { + agronomicalObjectData.add("variable", new JsonPrimitive(d.getVariable())); + } + agronomicalObjectData.add("date", new JsonPrimitive(d.getDate())); + agronomicalObjectData.add("value", new JsonPrimitive(d.getValue())); + + phenotypesDataToReturn.get(d.getAgronomicalObject()).add(agronomicalObjectData); + } else { + JsonObject agronomicalObjectData = new JsonObject(); + if (src.getVariableURI() != null) { + agronomicalObjectData.add("variable", new JsonPrimitive(src.getVariableURI())); + } else { + agronomicalObjectData.add("variable", new JsonPrimitive(d.getVariable())); + } + agronomicalObjectData.add("date", new JsonPrimitive(d.getDate())); + agronomicalObjectData.add("value", new JsonPrimitive(d.getValue())); + + JsonArray agronomicalObjectPhenotypes = new JsonArray(); + agronomicalObjectPhenotypes.add(agronomicalObjectData); + phenotypesDataToReturn.put(d.getAgronomicalObject(), agronomicalObjectPhenotypes); + } + } + + JsonArray finalJson = new JsonArray(); + + for (Entry entry : phenotypesDataToReturn.entrySet()) { + JsonObject agronomicalObjectData = new JsonObject(); + agronomicalObjectData.add("agronomicalObject", new JsonPrimitive(entry.getKey())); + if (src.getExperiment() != null) { + agronomicalObjectData.add("experiment", new JsonPrimitive(src.getExperiment())); + } + agronomicalObjectData.add("data", entry.getValue()); + finalJson.add(agronomicalObjectData); + } + return finalJson; + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/model/User.java b/phis2-ws/src/main/java/phis2ws/service/model/User.java new file mode 100644 index 000000000..e4d4d919c --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/model/User.java @@ -0,0 +1,146 @@ +//********************************************************************************************** +// User.java +// +// Author(s): Anne TIREAU, Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: may 2016 +// Contact:arnaud.charleroy@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr, +// morgane.vidal@inra.fr +// Last modification date: April, 2017 +// Subject: Represents a user +//*********************************************************************************************** +package phis2ws.service.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import phis2ws.service.view.model.phis.Group; + +/** + * + * @author tireau + * @update AC 07/16 Ajout des champ de classe user et implémentation du DAO USER + * @update [Morgane Vidal] 04/26 ajout des attributs phone, affiliation, orcid. + * suppression des attributs isAdmin, role, type + * a impacté la classe UserDaoPhisBrapi. Et ajout + * de la liste des groupes auxquels l'utilisateur + * appartient (utilisé pour la création d'un utilisateur) + */ +@ApiModel +public class User { + private String email; + private String password; + private String firstName; + private String familyName; + private String address; + private String phone; + private String affiliation; + private String orcid; + private String admin; + private String available; + + private ArrayList groups = new ArrayList<>(); + + public User(String email, String password) { + this.email = email; + this.password = password; + } + + public User(String email) { + this.email = email; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + @ApiModelProperty(hidden = true) + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getFamilyName() { + return familyName; + } + + public void setFamilyName(String familyName) { + this.familyName = familyName; + } + @ApiModelProperty(hidden = true) + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + @ApiModelProperty(hidden = true) + public String getAdmin() { + return admin; + } + + public void setAdmin(String admin) { + this.admin = admin; + } + + @ApiModelProperty(hidden = true) + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + @ApiModelProperty(hidden = true) + public String getAvailable() { + return available; + } + + public void setAvailable(String available) { + this.available = available; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAffiliation() { + return affiliation; + } + + public void setAffiliation(String affiliation) { + this.affiliation = affiliation; + } + + public String getOrcid() { + return orcid; + } + + public void setOrcid(String orcid) { + this.orcid = orcid; + } + + public ArrayList getGroups() { + return this.groups; + } + + public void addGroup(Group group) { + this.groups.add(group); + } + + public void setGroupList(ArrayList groups) { + this.groups = groups; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/AgronomicalObjectResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/AgronomicalObjectResourceService.java new file mode 100644 index 000000000..15f14175d --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/AgronomicalObjectResourceService.java @@ -0,0 +1,260 @@ +//********************************************************************************************** +// AgronomicalObjectResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: august 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: August, 30 2017 - update post - génération des uris +// Subject: Represents the agronomical object data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.phis.AgronomicalObjectDao; +import phis2ws.service.dao.sesame.AgronomicalObjectDaoSesame; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.AgronomicalObjectDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormAgronomicalObject; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.model.phis.AgronomicalObject; + +@Api("/agronomicalObjects") +@Path("agronomicalObjects") +public class AgronomicalObjectResourceService { + + final static Logger LOGGER = LoggerFactory.getLogger(AgronomicalObjectResourceService.class); + + //Session de l'utilisateur + @SessionInject + Session userSession; + + /** + * + * @param agronomicalObjectsDTO + * @return Une liste d'agronomical objects pour lesquels on a généré l'uri. + */ + private ArrayList generateURIs(ArrayList agronomicalObjectsDTO) { + String actualYear = Integer.toString(Calendar.getInstance().get(Calendar.YEAR)); + String baseURI = PropertiesFileManager.getConfigFileProperty("service", "baseURI"); + ArrayList agronomicalObjects = new ArrayList<>(); + HashMap numberByYear = new HashMap<>(); + //Récupération du numéro où l'on s'est arrêté + AgronomicalObjectDao agronomicalObjectDao = new AgronomicalObjectDao(); + String numberInYear = agronomicalObjectDao.getNumberOfAgronomicalObjectForYear(actualYear); + numberByYear.put(actualYear, Integer.parseInt(numberInYear)); + + //Modification des URIs de chaque agronomicalObject + for (AgronomicalObjectDTO agronomicalObject : agronomicalObjectsDTO) { + String year = actualYear; + if (agronomicalObject.getYear() != null) { + year = actualYear; + } + if (numberByYear.containsKey(year)) { + numberByYear.put(year, numberByYear.get(year) + 1); + } else { + numberByYear.put(year, 1); + } + + //On calcule le nombre de 0 à ajouter devant le numéro de l'ao (pour la cohérance des URI). + //au 30/08/17, il faut en tout 6 chiffres (ex : 000001) + String nbYear = numberByYear.get(year).toString(); + while (nbYear.length() < 6) { + nbYear = "0" + nbYear; + } + String uniqId = "o" + year.substring(2, 3) + nbYear; + AgronomicalObject ao = agronomicalObject.createObjectFromDTO(); + ao.setUri(baseURI + year + "/" + uniqId); + + agronomicalObjects.add(ao); + } + + return agronomicalObjects; + } + + /** + * Enregistre un ensemble d'objets agronomiques dans le triplestore et les associe à un essai s'il est renseigné + * @param agronomicalObjectsDTO liste des objets agronomiques à enregistrer + * @param context élément du contexte de la requête pour obtenir les + * informations de l'adresse ip de l'utilisateur + * @return + */ + @POST + @ApiOperation(value = "Post agronomical object(s)", + notes = "Register new agronomical object(s) in the database.") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Agronomical object(s) saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postAgronomicalObject( + @ApiParam(value = DocumentationAnnotation.AGRONOMICAL_OBJECT_POST_DATA_DEFINITION) ArrayList agronomicalObjectsDTO, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + //S'il y a au moins un objet agronomique + if (!agronomicalObjectsDTO.isEmpty()) { + try { + AgronomicalObjectDaoSesame agronomicalObjectDaoSesame = new AgronomicalObjectDaoSesame(); + if (context.getRemoteAddr() != null) { + agronomicalObjectDaoSesame.remoteUserAdress = context.getRemoteAddr(); + } + + agronomicalObjectDaoSesame.user = userSession.getUser(); + + //Génération de toutes les URI + ArrayList agronomicalObjects = generateURIs(agronomicalObjectsDTO); + + //Vérification des objets agronomiques et insertion en sesame + POSTResultsReturn resultSesame = agronomicalObjectDaoSesame.checkAndInsert(agronomicalObjects, agronomicalObjectsDTO); + if (resultSesame.getHttpStatus().equals(Response.Status.CREATED)) { + //Code 201, agronomical objects insérés + AgronomicalObjectDao agronomicalObjectDao = new AgronomicalObjectDao(); + POSTResultsReturn result = agronomicalObjectDao.checkAndInsertListAO(agronomicalObjects, agronomicalObjectsDTO); + if (result.getHttpStatus().equals(Response.Status.CREATED)) { //201, agronomical objects insérés + postResponse = new ResponseFormPOST(result.statusList); + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (resultSesame.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || resultSesame.getHttpStatus().equals(Response.Status.OK) + || resultSesame.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(resultSesame.statusList); + } + return Response.status(resultSesame.getHttpStatus()).entity(postResponse).build(); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty agronomical objects list")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormAgronomicalObject getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the agronomical objects")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche d'objets agronomiques) + * @param agronomicalObjectDaoSesame + * @return la réponse pour l'utilisateur. Contient la liste des objets agronomiques + * correspondant à la recherche + * SILEX:TODO + * on ne peut chercher que par uri et experiment uri. Il faudra ajouter d'autres critères + * on récupère une liste d'ao composés d'un uri et d'un type. Il faudra compléter ce retour + * \SILEX:TODO + */ + private Response getAgronomicalObjectsData(AgronomicalObjectDaoSesame agronomicalObjectDaoSesame) { + ArrayList agronomicalObjects; + ArrayList statusList = new ArrayList<>(); + ResponseFormAgronomicalObject getResponse; + + agronomicalObjects = agronomicalObjectDaoSesame.allPaginate(); + + if (agronomicalObjects == null) { + getResponse = new ResponseFormAgronomicalObject(0, 0, agronomicalObjects, true); + return noResultFound(getResponse, statusList); + } else if (!agronomicalObjects.isEmpty()) { + getResponse = new ResponseFormAgronomicalObject(agronomicalObjectDaoSesame.getPageSize(), agronomicalObjectDaoSesame.getPage(), agronomicalObjects, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormAgronomicalObject(0, 0, agronomicalObjects, true); + return noResultFound(getResponse, statusList); + } + } + + @GET + @ApiOperation(value = "Get all AgronomicalObjects corresponding to the searched params given", + notes = "Retrieve all agronomical objects authorized for the user corresponding to the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all agronomical objects", response = AgronomicalObject.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getAgronomicalObjectsBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by URI", example = DocumentationAnnotation.EXAMPLE_AGRONOMICAL_OBJECT_URI) @QueryParam("uri") String uri, + @ApiParam(value = "Search by experiment URI", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_URI) @QueryParam("experiment") String experimentURI, + @ApiParam(value = "Search by alias", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_ALIAS) @QueryParam("alias") String alias + ) { + AgronomicalObjectDaoSesame agronomicalObjectDaoSesame = new AgronomicalObjectDaoSesame(); + + if (uri != null) { + agronomicalObjectDaoSesame.uri = uri; + } + if (experimentURI != null) { + agronomicalObjectDaoSesame.experiment = experimentURI; + } + if (alias != null) { + agronomicalObjectDaoSesame.alias = alias; + } + + agronomicalObjectDaoSesame.user = userSession.getUser(); + agronomicalObjectDaoSesame.setPage(page); + agronomicalObjectDaoSesame.setPageSize(limit); + + return getAgronomicalObjectsData(agronomicalObjectDaoSesame); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/DocumentResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/DocumentResourceService.java new file mode 100644 index 000000000..56d276a26 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/DocumentResourceService.java @@ -0,0 +1,537 @@ +//********************************************************************************************** +// DocumentResourceService.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: Represents the documents service +//*********************************************************************************************** +package phis2ws.service.resources; + +import com.jcraft.jsch.SftpException; +import com.twmacinta.util.MD5; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; +import org.eclipse.rdf4j.query.MalformedQueryException; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.repository.RepositoryException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.configuration.URINamespaces; +import phis2ws.service.dao.manager.DAOFactory; +import phis2ws.service.dao.mongo.DocumentDaoMongo; +import phis2ws.service.dao.sesame.DocumentDaoSesame; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.DocumentMetadataDTO; +import phis2ws.service.utils.DocumentWaitingCheck; +import phis2ws.service.utils.FileUploader; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.ResourcesUtils; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormDocumentMetadata; +import phis2ws.service.view.brapi.form.ResponseFormDocumentType; +import phis2ws.service.view.brapi.form.ResponseFormGET; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.model.phis.Document; + +@Api("/documents") +@Path("/documents") +public class DocumentResourceService { + @Context + UriInfo uri; + + @SessionInject + Session userSession; + + final static Logger logger = LoggerFactory.getLogger(DocumentResourceService.class); + + // Gère les annotations en attene + public final static ExecutorService threadPool = Executors.newCachedThreadPool(); + // Deux Maps qui contiennent les informations sur les annotations en attentes + public final static Map waitingAnnotFileCheck = new HashMap<>(); + public final static Map waitingAnnotInformation = new HashMap<>(); + + /** + * Vérifie un ensemble d'annotation au format JSON + * + * @param headers Entête de la requête + * @param documentsAnnotations documentsAnnotations + * @return + */ + @POST + @ApiOperation(value = "Save a file", notes = DocumentationAnnotation.USER_ONLY_NOTES) + @ApiResponses(value = { + @ApiResponse(code = 202, message = "Metadata verified and correct", response = DocumentMetadataDTO.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA)}) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postDocuments(@Context HttpHeaders headers, + @ApiParam(value = "JSON Document metadata", required = true) List documentsAnnotations) throws RepositoryException { + AbstractResultForm postResponse; + if (documentsAnnotations != null && !documentsAnnotations.isEmpty()) { + //Insertion du document + DocumentDaoSesame documentDao = new DocumentDaoSesame(); + documentDao.user = userSession.getUser(); + //Vérification des documentsAnnotations + final POSTResultsReturn checkAnnots = documentDao.check(documentsAnnotations); + + if (checkAnnots.statusList == null) { //Les annotations ne sont pas bonnes + postResponse = new ResponseFormPOST(); + } else if (checkAnnots.getDataState()) { // Si les annotations sont bonnes + List uriList = new ArrayList<>(); + Iterator itdocsMetadata = documentsAnnotations.iterator(); + while (itdocsMetadata.hasNext()) { + DocumentMetadataDTO docsM = itdocsMetadata.next(); + //Construction des URI + final UriBuilder uploadPath = uri.getBaseUriBuilder(); + String name = new StringBuilder("document").append(ResourcesUtils.getUniqueID()).toString(); // docsM + idUni + final URINamespaces uriNS = new URINamespaces(); + final String docsUri = uriNS.getContextsProperty("documents") + "/" + name; + final String uploadLink = uploadPath.path("documents").path("upload").queryParam("uri", docsUri).toString(); +// //Ajout URI en attente + uriList.add(uploadLink); + waitingAnnotFileCheck.put(docsUri, false); // fichier en attente + waitingAnnotInformation.put(docsUri, docsM); +// //Lancement THREAD pour le fichier en attente + threadPool.submit(new DocumentWaitingCheck(docsUri)); + } + final Status waitingTimeStatus = new Status("Timeout", StatusCodeMsg.INFO, " Timeout :" + PropertiesFileManager.getConfigFileProperty("service", "waitingFileTime") + " seconds"); + checkAnnots.statusList.add(waitingTimeStatus); + postResponse = new ResponseFormPOST(checkAnnots.statusList); + postResponse.getMetadata().setDatafiles(uriList); + } else { + postResponse = new ResponseFormPOST(checkAnnots.statusList); + } + return Response.status(checkAnnots.getHttpStatus()).entity(postResponse).build(); + } else { + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormPOST()).build(); + } + } + + /** + * Adresse du fichier a envoyer + * + * @param in Fichier + * @param docUri Uri de l'annotation + * @param headers Entête de la requête + * @param request + * @return + * @throws URISyntaxException + */ + @POST + @Path("upload") + @ApiOperation(value = "Post data file", notes = DocumentationAnnotation.USER_ONLY_NOTES + " Not working from this documentation. Implement a client or use Postman application.") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Document file and document metadata saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA)}) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_OCTET_STREAM) + @Produces(MediaType.APPLICATION_JSON) + public Response postDocumentFile( + @ApiParam(value = "File to upload") File in, + @ApiParam(value = "URI given from \"/documents\" path for upload") @QueryParam("uri") String docUri, + @Context HttpHeaders headers, + @Context HttpServletRequest request) throws URISyntaxException { + ResponseFormPOST postResponse = null; + List statusList = new ArrayList(); + + // Annotation présente + if (!waitingAnnotFileCheck.containsKey(docUri)) { + statusList.add(new Status("No waiting file", "Error", "No waiting file for the following uri : " + docUri)); + postResponse = new ResponseFormPOST(statusList); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + + if (headers != null && headers.getLength() <= 0) { + statusList.add(new Status("File error", "Error", "File Size : " + headers.getLength() + " octets")); + postResponse = new ResponseFormPOST(statusList); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + + // Vérification du checksum md5 + String hash = getHash(in); + if (hash != null && !waitingAnnotInformation.get(docUri).getChecksum().equals(hash)) { + statusList.add(new Status("MD5 error", "Error", "Checksum MD5 doesn't match. Corrupted File.")); + postResponse = new ResponseFormPOST(statusList); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + + String media = waitingAnnotInformation.get(docUri).getDocumentType(); + media = media.substring(media.lastIndexOf("#") + 1, media.length()); + FileUploader jsch = new FileUploader(); + try { + waitingAnnotFileCheck.put(docUri, Boolean.TRUE); // Traitement en cours du fichier + logger.debug(jsch.getSFTPWorkingDirectory() + "/" + media); + //SILEX:test + jsch.getChannelSftp().cd(jsch.getSFTPWorkingDirectory()); + //\SILEX:test + } catch (SftpException e) { + statusList.add(new Status("SftException", StatusCodeMsg.ERR, e.getMessage())); + logger.error(e.getMessage(), e); + } + + final String serverFileName = ResourcesUtils.getUniqueID() + "." + waitingAnnotInformation.get(docUri).getExtension(); + final String serverFilePath = jsch.getSFTPWorkingDirectory() + "/" + serverFileName; + + boolean fileTransfered = jsch.fileTransfer(in, serverFileName); + jsch.closeConnection(); + + if (!fileTransfered) { // Si le fichier n'a pas été enregistré + statusList.add(new Status("File upload error", "Error", "Problem during file upload. Try to submit it again " + docUri)); + postResponse = new ResponseFormPOST(statusList); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + + waitingAnnotInformation.get(docUri).setServerFilePath(serverFilePath); + DocumentDaoSesame documentsDao = DAOFactory.getSESAMEDAOFactory().getDocumentsDaoSesame(); + if (request.getRemoteAddr() != null) { + documentsDao.remoteUserAdress = request.getRemoteAddr(); + } + documentsDao.user = userSession.getUser(); + final POSTResultsReturn insertAnnotationJSON = documentsDao.insert(Arrays.asList(waitingAnnotInformation.get(docUri))); + + postResponse = new ResponseFormPOST(insertAnnotationJSON.statusList); + + if (insertAnnotationJSON.getDataState()) { // Etat du fichier JSON + waitingAnnotFileCheck.remove(docUri); + waitingAnnotInformation.remove(docUri); + if (insertAnnotationJSON.getHttpStatus() == Response.Status.CREATED) { + postResponse.getMetadata().setDatafiles((ArrayList) insertAnnotationJSON.createdResources); + final URI newUri = new URI(uri.getPath()); + return Response.status(insertAnnotationJSON.getHttpStatus()).location(newUri).entity(postResponse).build(); + } else { + return Response.status(insertAnnotationJSON.getHttpStatus()).entity(postResponse).build(); + } + } + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(new ResponseFormPOST()).build(); + } + + private String getHash(File in) { + String hash = null; + try { + hash = MD5.asHex(MD5.getHash(in)); // Ex : 106fa487baa1728083747de1c6df73e9 + } catch (IOException ex) { + logger.error(ex.getMessage(), ex); + } + return hash; + } + + @GET + @Path("types") + @ApiOperation(value = "Get all documents types", + notes = "Retrieve all documents types ") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all documents type"), +// , response = Experiment.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA)}) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getDocumentsType( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page) { + DocumentDaoSesame documentsDao = new DocumentDaoSesame(); + Status errorStatus = null; + try { + ArrayList documentCategories = documentsDao.getDocumentsTypes(); + if (documentCategories.isEmpty()) { + return Response.status(Response.Status.NOT_FOUND).entity(new ResponseFormGET()).build(); + } + return Response.status(Response.Status.OK).entity(new ResponseFormDocumentType(limit, page, documentCategories, false)).build(); + } catch (RepositoryException | MalformedQueryException | QueryEvaluationException ex) { + errorStatus = new Status("Error", StatusCodeMsg.ERR, ex.getMessage()); + } + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(new ResponseFormGET(errorStatus)).build(); + } + + /** + * + * @param limit + * @param page + * @param uri + * @param documentType + * @param creator + * @param language + * @param title + * @param creationDate + * @param extension + * @param concernedItem + * @param status + * @return le résultat de la requête + */ + @GET + @ApiOperation(value = "Get all documents metadata corresponding to the searched params given", + notes = "Retrieve all documents authorized for the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all documents ", response = DocumentMetadataDTO.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA)}) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getDocumentsMetadataBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by URI", example = DocumentationAnnotation.EXAMPLE_DOCUMENT_URI) @QueryParam("uri") String uri, + @ApiParam(value = "Search by document type", example = DocumentationAnnotation.EXAMPLE_DOCUMENT_TYPE) @QueryParam("documentType") String documentType, + @ApiParam(value = "Search by creator", example = DocumentationAnnotation.EXAMPLE_DOCUMENT_CREATOR) @QueryParam("creator") String creator, + @ApiParam(value = "Search by language", example = DocumentationAnnotation.EXAMPLE_DOCUMENT_LANGUAGE) @QueryParam("language") String language, + @ApiParam(value = "Search by title", example = DocumentationAnnotation.EXAMPLE_DOCUMENT_TITLE) @QueryParam("title") String title, + @ApiParam(value = "Search by creation date", example = DocumentationAnnotation.EXAMPLE_DOCUMENT_CREATION_DATE) @QueryParam("creationDate") String creationDate, + @ApiParam(value = "Search by extension", example = DocumentationAnnotation.EXAMPLE_DOCUMENT_EXTENSION) @QueryParam("extension") String extension, + @ApiParam(value = "Search by concerned item", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_URI) @QueryParam("concernedItem") String concernedItem, + @ApiParam(value = "Search by status", example = DocumentationAnnotation.EXAMPLE_DOCUMENT_STATUS) @QueryParam("status") String status) { + + //SILEX:conception + //Pour l'instant la recherche de documents liés à un élément se fait sur un seul élément. + //Par la suite il faudra la faire sur une liste d'éléments + //\SILEX:conception + + DocumentDaoSesame documentDao = new DocumentDaoSesame(); + + if (uri != null) { + documentDao.uri = uri; + } + if (documentType != null) { + documentDao.documentType = documentType; + } + if (creator != null) { + documentDao.creator = creator; + } + if (language != null) { + documentDao.language = language; + } + if (title != null) { + documentDao.title = title; + } + if (creationDate != null) { + documentDao.creationDate = creationDate; + } + if (extension != null) { + documentDao.format = extension; + } + if (concernedItem != null) { + documentDao.concernedItemsUris.add(concernedItem); + } + if (status != null) { + documentDao.status = status; + } + + documentDao.user = userSession.getUser(); + documentDao.setPage(page); + documentDao.setPageSize(limit); + + return getDocumentsMetadata(documentDao); + } + + /** + * + * @param documentURI l'uri du document à télécharger + * @return la réponse, avec le document si l'uri existe bien + */ + @GET + @Path("{documentURI}") + @ApiOperation(value = "Get a document (by receiving it's uri)", + notes = "Retrieve the document corresponding to the uri given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve document"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_OCTET_STREAM) + public Response getDocumentByUri(@ApiParam(value = DocumentationAnnotation.DOCUMENT_URI_DEFINITION, required = true, example = DocumentationAnnotation.EXAMPLE_DOCUMENT_URI) @PathParam("documentURI") String documentURI) { + //SILEX:conception + //Est-ce qu'il serait mieux d'envoyer directement l'InputStream récupéré dans mongoDB plutôt + //que d'écrire le fichier sur le disque avant de l'envoyer ? + if (documentURI == null) { + final Status status = new Status("Access error", StatusCodeMsg.ERR, "Empty document URI"); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(status)).build(); + } + + return getFile(documentURI); + //\SILEX:conception + } + + /** + * @action modifie une liste de métadonnées de documents en fonction des informations envoyées + * @param documentsMetadata + * @param context + * @return Response le resultat de la requête + */ + @PUT + @ApiOperation(value = "Update document metadata") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Document's metadata updated", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 404, message = "Document not found"), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response putDocumentMetadata( + @ApiParam(value = "Json document metadata") ArrayList documentsMetadata, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + if (documentsMetadata != null && !documentsMetadata.isEmpty()) { + DocumentDaoSesame documentDaoSesame = new DocumentDaoSesame(); + if (documentDaoSesame.remoteUserAdress != null) { + documentDaoSesame.remoteUserAdress = context.getRemoteAddr(); + } + documentDaoSesame.user = userSession.getUser(); + + //Vérification des données et update de la BD + POSTResultsReturn result = documentDaoSesame.checkAndUpdateList(documentsMetadata); + + if (result.getHttpStatus().equals(Response.Status.OK)) { //200 users modifiés + postResponse = new ResponseFormPOST(result.statusList); + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty document(s) to update")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormDocumentMetadata getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the documents")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + /** + * Collecte les métadonnées de documents issues d'une recherche utilisateur + * @param documentDao DocumentDaoSesame + * @return la réponse dédiée à l'utilisateur. + * Contient la liste des métadonnées de documents correspondant au résultat de la recherche + */ + private Response getDocumentsMetadata(DocumentDaoSesame documentDao) { + ArrayList documentsMetadata; + ArrayList statusList = new ArrayList<>(); + ResponseFormDocumentMetadata getResponse; + + documentDao.user = userSession.getUser(); + documentsMetadata = documentDao.allPaginate(); + if (documentsMetadata == null) { + getResponse = new ResponseFormDocumentMetadata(0, 0, documentsMetadata, true); + return noResultFound(getResponse, statusList); + } else if (!documentsMetadata.isEmpty()) { + getResponse = new ResponseFormDocumentMetadata(documentDao.getPageSize(), documentDao.getPage(), documentsMetadata, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormDocumentMetadata(0, 0, documentsMetadata, true); + return noResultFound(getResponse, statusList); + } + } + + /** + * + * @param documentURI l'URI du document à télécharger + * @return La réponse, contenant le document à télécharger s'il existe + */ + private Response getFile(String documentURI) { + DocumentDaoMongo documentDaoMongo = new DocumentDaoMongo(); + File file = documentDaoMongo.getDocument(documentURI); + + if (file == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } else { + return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM) + .header("Content-Disposition", "attachement; filename=\"" + file.getName() + "\"") + .build(); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/ExperimentResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/ExperimentResourceService.java new file mode 100644 index 000000000..28cb2558c --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/ExperimentResourceService.java @@ -0,0 +1,339 @@ +//********************************************************************************************** +// ExperimentResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: January 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 31 2017 : Passage de trial à experiment +// Subject: Represents the experiment data service +//*********************************************************************************************** + +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.phis.ExperimentDao; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.ExperimentDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormGET; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.brapi.form.ResponseFormExperiment; +import phis2ws.service.view.model.phis.Experiment; + +@Api("/experiments") +@Path("experiments") +public class ExperimentResourceService { + final static Logger LOGGER = LoggerFactory.getLogger(ExperimentResourceService.class); + + //Session de l'utilisateur + @SessionInject + Session userSession; + + + /** + * @param uri + * @param limit + * @param page + * @param startDate + * @param endDate + * @param field + * @param campaign + * @param place + * @param alias + * @param keywords + * @return liste des expérimentations correspondant aux différents critères de recherche + * (ou toutes les expérimentations si pas de critères) + * Le retour (dans "data") est de la forme : + * [ + * { description de l'expérimentation1 }, + * { description de l'expérimentation2 }, + * ] + */ + @GET + @ApiOperation(value = "Get all experiments corresponding to the searched params given", + notes = "Retrieve all experiments authorized for the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all experiments", response = Experiment.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA)}) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getExperimentsBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by uri", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_URI) @QueryParam("uri") String uri, + @ApiParam(value = "Search by start date", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_START_DATE) @QueryParam("startDate") String startDate, + @ApiParam(value = "Search by end date", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_END_DATE) @QueryParam("endDate") String endDate, + @ApiParam(value = "Search by field", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_FIELD) @QueryParam("field") String field, + @ApiParam(value = "Search by campaign", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_CAMPAIGN) @QueryParam("campaign") String campaign, + @ApiParam(value = "Search by place", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_PLACE) @QueryParam("place") String place, + @ApiParam(value = "Search by alias", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_ALIAS) @QueryParam("alias") String alias, + @ApiParam(value = "Search by keywords", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_KEYWORDS) @QueryParam("keywords") String keywords) { + + ExperimentDao experimentDao = new ExperimentDao(); + + if (uri != null) { + experimentDao.uri = uri; + } + if (startDate != null) { + experimentDao.startDate = startDate; + } + if (endDate != null) { + experimentDao.endDate = endDate; + } + if (field != null) { + experimentDao.field = field; + } + if (campaign != null) { + experimentDao.campaign = campaign; + } + if (place != null) { + experimentDao.place = place; + } + if (alias != null) { + experimentDao.alias = alias; + } + if (keywords != null) { + experimentDao.keyword = keywords; + } + + experimentDao.user = userSession.getUser(); + experimentDao.setPage(page); + experimentDao.setPageSize(limit); + + return getExperimentsData(experimentDao); + } + + /** + * + * @param experimentURI + * @param limit + * @param page + * @return l'expérimentation correspondant à l'uri donnée si elle existe + */ + @GET + @Path("{experiment}") + @ApiOperation(value = "Get an experiment", + notes = "Retrieve an experiment. Need URL encoded experiment URI (Unique resource identifier).") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve an experiment.", response = Experiment.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getExperimentDetail( + @ApiParam(value = DocumentationAnnotation.EXPERIMENT_URI_DEFINITION, required = true, example=DocumentationAnnotation.EXAMPLE_EXPERIMENT_URI) @PathParam("experiment") String experimentURI, + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page) { + if (experimentURI == null) { + final Status status = new Status("Access error", StatusCodeMsg.ERR, "Empty Experiment URI"); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(status)).build(); + } + + ExperimentDao experimentDao = new ExperimentDao(experimentURI); + experimentDao.setPageSize(limit); + experimentDao.setPage(page); + experimentDao.user = userSession.getUser(); + + return getExperimentsData(experimentDao); + } + + /** + * + * @param experiments + * @param context + * @return le résultat de la requête de création de l'expérimentation + */ + @POST + @ApiOperation(value = "Post a experiment", + notes = "Register a new experiment in the database") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Experiment saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postExperiment( + @ApiParam(value = DocumentationAnnotation.EXPERIMENT_POST_DATA_DEFINITION) ArrayList experiments, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + //Si dans les données envoyées il y a au moins une expérimentation + + if (experiments != null + && !experiments.isEmpty()) { + try { + ExperimentDao experimentDao = new ExperimentDao(); + if (experimentDao.remoteUserAdress != null) { + experimentDao.remoteUserAdress = context.getRemoteAddr(); + } + + experimentDao.user = userSession.getUser(); + + //Vérification des expérimentations et insertion dans la BD + POSTResultsReturn result = experimentDao.checkAndInsertList(experiments); + + if (result.getHttpStatus().equals(Response.Status.CREATED)) { + //Code 201, experiments insérés + postResponse = new ResponseFormPOST(result.statusList); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty experiment(s) to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + @PUT + @ApiOperation(value = "Update experiment") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Experiment updated", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 404, message = "Experiment not found"), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response putExperiment( + @ApiParam(value = DocumentationAnnotation.EXPERIMENT_POST_DATA_DEFINITION) ArrayList experiments, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + if (experiments != null && !experiments.isEmpty()) { + ExperimentDao experimentDao = new ExperimentDao(); + if (experimentDao.remoteUserAdress != null) { + experimentDao.remoteUserAdress = context.getRemoteAddr(); + } + experimentDao.user = userSession.getUser(); + + //Vérification des données et update de la BD + POSTResultsReturn result = experimentDao.checkAndUpdateList(experiments); + + if (result.getHttpStatus().equals(Response.Status.OK)) { //200 users modifiés + postResponse = new ResponseFormPOST(result.statusList); + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty experiment(s) to update")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormExperiment getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the experiments")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + private Response sqlError(ResponseFormExperiment getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("SQL error" ,StatusCodeMsg.ERR, "can't fetch result")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(getResponse).build(); + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche d'expérimentations) + * @param experimentDao ExperimentDao + * @return la réponse pour l'utilisateur. + * Contient la liste des expérimentations correspondant à la recherche + */ + private Response getExperimentsData(ExperimentDao experimentDao) { + ArrayList experiments = new ArrayList<>(); + ArrayList statusList = new ArrayList<>(); + ResponseFormExperiment getResponse; + Integer experimentsCount = experimentDao.count(); + + if (experimentsCount != null && experimentsCount == 0) { + getResponse = new ResponseFormExperiment(experimentDao.getPageSize(), experimentDao.getPage(), experiments, true); + return noResultFound(getResponse, statusList); + } else { + experiments = experimentDao.allPaginate(); + if (experiments == null) { + getResponse = new ResponseFormExperiment(0, 0, experiments, true); + return sqlError(getResponse, statusList); + } else if (!experiments.isEmpty() && experimentsCount != null) { + getResponse = new ResponseFormExperiment(experimentDao.getPageSize(), experimentDao.getPage(), experiments, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormExperiment(0, 0, experiments, true); + return noResultFound(getResponse, statusList); + } + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/GroupResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/GroupResourceService.java new file mode 100644 index 000000000..d2830dd07 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/GroupResourceService.java @@ -0,0 +1,309 @@ +//********************************************************************************************** +// GroupResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: April, 2017 +// Subject: Represents the group data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.phis.GroupDao; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.GroupDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormGET; +import phis2ws.service.view.brapi.form.ResponseFormGroup; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.model.phis.Group; + +@Api("/group") +@Path("groups") +public class GroupResourceService { + final static Logger LOGGER = LoggerFactory.getLogger(GroupResourceService.class); + + //Session de l'utilisateur + @SessionInject + Session userSession; + + /** + * + * @param limit + * @param page + * @param uri + * @param name + * @param level + * @return liste des groupes correspondant aux critères de recherche + * (ou tous les groupes si pas de critères) + * Le retour (dans "data") est de la forme : + * [ + * { description du groupe1 }, + * { description du groupe2 }, + * ... + * ] + */ + @GET + @ApiOperation(value = "Get all groups corresponding to the searched params given", + notes = "Retrieve all groups authorized for the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all groups", response = Group.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getGroupBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by uri", example = DocumentationAnnotation.EXAMPLE_GROUP_URI) @QueryParam("uri") String uri, + @ApiParam(value = "Search by name", example = DocumentationAnnotation.EXAMPLE_GROUP_NAME) @QueryParam("name") String name, + @ApiParam(value = "Search by level", example = DocumentationAnnotation.EXAMPLE_GROUP_LEVEL) @QueryParam("level") String level) { + GroupDao groupDao = new GroupDao(); + + if (uri != null) { + groupDao.uri = uri; + } + if (name != null) { + groupDao.name = name; + } + if (level != null) { + groupDao.level = level; + } + + groupDao.setPageSize(limit); + groupDao.setPage(page); + + return getGroupsData(groupDao); + } + + /** + * + * @param groupUri + * @param limit + * @param page + * @return le groupe correspondant au nom de groupe s'il existe + */ + @GET + @Path("{groupURI}") + @ApiOperation(value = "Get a group", + notes = "Retrieve a group. Need group name.") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve a group.", response = Group.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getGroupDetails( + @ApiParam(value = DocumentationAnnotation.GROUP_URI_DEFINITION, required = true, example = DocumentationAnnotation.EXAMPLE_GROUP_URI) @PathParam("groupURI") String groupUri, + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page) { + + if (groupUri == null) { + final Status status = new Status("Access error", StatusCodeMsg.ERR, "Empty Group uri"); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(status)).build(); + } + + GroupDao groupDao = new GroupDao(groupUri); + groupDao.setPageSize(limit); + groupDao.setPage(page); + + groupDao.user = userSession.getUser(); + + return getGroupsData(groupDao); + } + + /** + * permet l'enregistrement en base de données d'un nouveau groupe + * @param groups + * @param context + * @return le message de retour correspondant à cet ajout (erreur ou ok) + */ + @POST + @ApiOperation(value = "Post a group", + notes = "Register a new group in the database") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Group saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postGroup( + @ApiParam(value = DocumentationAnnotation.GROUP_POST_DATA_DEFINITION) ArrayList groups, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + //Si dans les données envoyées il y a au moins un groupe + if (groups != null && !groups.isEmpty()) { + GroupDao groupDao = new GroupDao(); + if (groupDao.remoteUserAdress != null) { + groupDao.remoteUserAdress = context.getRemoteAddr(); + } + + groupDao.user = userSession.getUser(); + + //Vérification des groupes et insertion en BD + POSTResultsReturn result = groupDao.checkAndInsertList(groups); + + if (result.getHttpStatus().equals(Response.Status.CREATED)) { //201, projets insérés + postResponse = new ResponseFormPOST(result.statusList); + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty group(s) to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + @PUT + @ApiOperation(value = "Update groups") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Group updated", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 404, message = "Group not found"), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response putGroup( + @ApiParam(value = DocumentationAnnotation.GROUP_POST_DATA_DEFINITION) ArrayList groups, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + if (groups != null && !groups.isEmpty()) { + GroupDao groupDao = new GroupDao(); + if (groupDao.remoteUserAdress != null) { + groupDao.remoteUserAdress = context.getRemoteAddr(); + } + groupDao.user = userSession.getUser(); + + //Vérification des données et update de la BD + POSTResultsReturn result = groupDao.checkAndUpdateList(groups); + + if (result.getHttpStatus().equals(Response.Status.OK)) { //200 users modifiés + postResponse = new ResponseFormPOST(result.statusList); + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + //TODO + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty group(s) to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormGroup getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the projects")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + private Response sqlError(ResponseFormGroup getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("SQL error ", StatusCodeMsg.ERR, "can't fetch result")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(getResponse).build(); + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche de groupes) + * @param groupDao GroupDao + * @return la réponse pour l'utilisateur. + * Contient la liste des groups correspondant à la recherche + */ + private Response getGroupsData(GroupDao groupDao) { + ArrayList groups = new ArrayList<>(); + ArrayList statusList = new ArrayList<>(); + ResponseFormGroup getResponse; + Integer groupsCount = groupDao.count(); + + if (groupsCount != null && groupsCount == 0) { + getResponse = new ResponseFormGroup(groupDao.getPageSize(), groupDao.getPage(), groups, true); + return noResultFound(getResponse, statusList); + } else { + groups = groupDao.allPaginate(); + if (groups == null) { + groups = new ArrayList<>(); + getResponse = new ResponseFormGroup(0, 0, groups, true); + return sqlError(getResponse, statusList); + } else if (!groups.isEmpty() && groupsCount != null) { + getResponse = new ResponseFormGroup(groupDao.getPageSize(), groupDao.getPage(), groups, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormGroup(0, 0, groups, true); + return noResultFound(getResponse, statusList); + } + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/LayerResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/LayerResourceService.java new file mode 100644 index 000000000..c7fe565a3 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/LayerResourceService.java @@ -0,0 +1,131 @@ +//********************************************************************************************** +// LayerResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: August 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: September, 6 2017 +// Subject: Represents the layer data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.phis.LayerDao; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.LayerDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.ResourcesUtils; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormPOST; + +@Api("/layers") +@Path("layers") +public class LayerResourceService { + final static Logger LOGGER = LoggerFactory.getLogger(LayerResourceService.class); + + //Session de l'utilisateur + @SessionInject + Session userSession; + + + /** + * + * @param layers + * @param context + * @return + */ + @POST + @ApiOperation(value = "Post a layer", + notes = "Create a geojson layer file") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Layer geojson file created", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postLayer( + @ApiParam(value = DocumentationAnnotation.LAYER_POST_DATA_DEFINITION) ArrayList layers, + @Context HttpServletRequest context) throws IOException { + AbstractResultForm postResponse = null; + + //SILEX:TODO + //générer une couche par élément de la liste et retourner les url de tous les fichiers générés + if (layers != null && !layers.isEmpty()) { + LayerDao layerDao = new LayerDao(); + List urlFilesList = new ArrayList<>(); + if (ResourcesUtils.getStringBooleanValue(layers.get(0).getGenerateFile())) { //On génère le fichier + + //SILEX:test + //Pour l'instant on le fait que pour une seule couche (la première + //de la liste envoyée. Il faudra par la suite le faire pour le reste) + POSTResultsReturn resultCreateFile = layerDao.createLayerFile(layers.get(0)); + + if (resultCreateFile.getHttpStatus().equals(Response.Status.CREATED)) { //PAS SURE DE ÇA, À VÉRIFIER + //Retour de base à retourner. Il me faut ajouter l'url du fichier + postResponse = new ResponseFormPOST(resultCreateFile.statusList); + urlFilesList.add(layerDao.fileWebPath); + postResponse.getMetadata().setDatafiles(urlFilesList); + return Response.status(resultCreateFile.getHttpStatus()).entity(postResponse).build(); + } else { + return Response.status(resultCreateFile.getHttpStatus()).entity(postResponse).build(); + } + } else { //On ne doit pas générer le fichier + String fileWebPath = layerDao.getObjectURILayerFilePath(layers.get(0).getObjectUri()); + File f = new File(fileWebPath); + + if (f.exists()) { //S'il existe, on retourne l'url + urlFilesList.add(layerDao.getObjectURILayerFileWebPath(layers.get(0).getObjectUri())); + List statusList = new ArrayList<>(); + statusList.add(new Status("File exist", StatusCodeMsg.INFO, fileWebPath)); + POSTResultsReturn layerFile = new POSTResultsReturn(true, true, true); + layerFile.statusList = statusList; + postResponse = new ResponseFormPOST(layerFile.statusList); + postResponse.getMetadata().setDatafiles(urlFilesList); + + return Response.status(layerFile.getHttpStatus()).entity(postResponse).build(); + } else { //Sinon, il faut retourner une erreur comme quoi le fichier n'existe pas. + return Response.status(Response.Status.NOT_FOUND).build(); + } + } + //\SILEX:test + } else { + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormPOST()).build(); + } + //\SILEX:TODO + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/MethodResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/MethodResourceService.java new file mode 100644 index 000000000..77edfd0b9 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/MethodResourceService.java @@ -0,0 +1,280 @@ +//********************************************************************************************** +// MethodResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 17 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 17 2017 +// Subject: Represents the method data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.sesame.MethodDaoSesame; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.MethodDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormGET; +import phis2ws.service.view.brapi.form.ResponseFormMethod; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.model.phis.Method; + +@Api("/methods") +@Path("methods") +public class MethodResourceService { + final static Logger LOGGER = LoggerFactory.getLogger(MethodResourceService.class); + + //Session utilisateur + @SessionInject + Session userSession; + + @POST + @ApiOperation(value = "Post method(s)", + notes = "Register new method(s) in the data base") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Method(s) saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postMethod(@ApiParam(value = DocumentationAnnotation.METHOD_POST_DATA_DEFINITION) ArrayList methods, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + if (methods != null && !methods.isEmpty()) { + MethodDaoSesame methodDaoSesame = new MethodDaoSesame(); + if (context.getRemoteAddr() != null) { + methodDaoSesame.remoteUserAdress = context.getRemoteAddr(); + } + + methodDaoSesame.user = userSession.getUser(); + + POSTResultsReturn result = methodDaoSesame.checkAndInsert(methods); + + if (result.getHttpStatus().equals(Response.Status.CREATED)) { + //Code 201, methodes insérées + postResponse = new ResponseFormPOST(result.statusList); + postResponse.getMetadata().setDatafiles(result.getCreatedResources()); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty method(s) to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + @PUT + @ApiOperation(value = "Update method") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Method updated", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 404, message = "Method not found"), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response putMethod( + @ApiParam(value = DocumentationAnnotation.METHOD_POST_DATA_DEFINITION) ArrayList methods, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + if (methods != null && !methods.isEmpty()) { + MethodDaoSesame methodDaoSesame = new MethodDaoSesame(); + if (context.getRemoteAddr() != null) { + methodDaoSesame.remoteUserAdress = context.getRemoteAddr(); + } + + methodDaoSesame.user = userSession.getUser(); + + POSTResultsReturn result = methodDaoSesame.checkAndUpdate(methods); + + if (result.getHttpStatus().equals(Response.Status.OK)) { + //Code 200, traits modifiés + postResponse = new ResponseFormPOST(result.statusList); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty method(s) to update")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormMethod getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the methods")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche de methodes) + * @param methodDaoSesame + * @return la réponse pour l'utilisateur. Contient la liste des méthodess + * correspondant à la recherche + * SILEX:TODO + * on ne peut chercher que par uri et label. Il faudra ajouter d'autres critères + * \SILEX:TODO + */ + private Response getMethodsData(MethodDaoSesame methodDaoSesame) { + ArrayList methods; + ArrayList statusList = new ArrayList<>(); + ResponseFormMethod getResponse; + + methods = methodDaoSesame.allPaginate(); + + if (methods == null) { + getResponse = new ResponseFormMethod(0, 0, methods, true); + return noResultFound(getResponse, statusList); + } else if (!methods.isEmpty()) { + getResponse = new ResponseFormMethod(methodDaoSesame.getPageSize(), methodDaoSesame.getPage(), methods, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormMethod(0, 0, methods, true); + return noResultFound(getResponse, statusList); + } + } + + /** + * + * @param limit + * @param page + * @param uri + * @param label + * @return + */ + @GET + @ApiOperation(value = "Get all methods corresponding to the searched params given", + notes = "Retrieve all methods authorized for the user corresponding to the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all methods", response = Method.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getMethodsBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by URI", example = DocumentationAnnotation.EXAMPLE_METHOD_URI) @QueryParam("uri") String uri, + @ApiParam(value = "Search by label", example = DocumentationAnnotation.EXAMPLE_METHOD_LABEL) @QueryParam("label") String label + ) { + MethodDaoSesame methodDaoSesame = new MethodDaoSesame(); + + if (uri != null) { + methodDaoSesame.uri = uri; + } + if (label != null) { + methodDaoSesame.label = label; + } + + methodDaoSesame.user = userSession.getUser(); + methodDaoSesame.setPage(page); + methodDaoSesame.setPageSize(limit); + + return getMethodsData(methodDaoSesame); + } + + /** + * + * @param method + * @param limit + * @param page + * @return la méthode correspondant à l'uri donnée si elle existe + */ + @GET + @Path("{method}") + @ApiOperation(value = "Get a method", + notes = "Retrieve a method. Need URL encoded method URI (Unique resource identifier).") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve a method.", response = Method.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getMethodDetails( + @ApiParam(value = DocumentationAnnotation.METHOD_URI_DEFINITION, required = true, example = DocumentationAnnotation.EXAMPLE_METHOD_URI) @PathParam("method") String method, + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page) { + + if (method == null) { + final Status status = new Status("Access error", StatusCodeMsg.ERR, "Empty method URI"); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(status)).build(); + } + + MethodDaoSesame methodDaoSesame = new MethodDaoSesame(); + methodDaoSesame.uri = method; + methodDaoSesame.setPageSize(limit); + methodDaoSesame.setPage(page); + methodDaoSesame.user = userSession.getUser(); + + return getMethodsData(methodDaoSesame); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/PhenotypeResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/PhenotypeResourceService.java new file mode 100644 index 000000000..54d82c7a5 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/PhenotypeResourceService.java @@ -0,0 +1,210 @@ +package phis2ws.service.resources; + +//********************************************************************************************** +// RawDataResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: September 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: September, 13 2017 +// Subject: Represents the raw data service +//*********************************************************************************************** + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import java.util.Arrays; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.mongo.PhenotypeDaoMongo; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.PhenotypeDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.brapi.form.ResponseFormPhenotype; +import phis2ws.service.view.model.phis.Phenotype; + +@Api("/phenotypes") +@Path("/phenotypes") +public class PhenotypeResourceService { + + final static Logger LOGGER = LoggerFactory.getLogger(ExperimentResourceService.class); + + //Session de l'utilisateur + @SessionInject + Session userSession; + + /** + * + * @param phenotypes + * @param context + * @return le résultat de la création du phénotype + */ + @POST + @ApiOperation(value = "Post phenotype data") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Phenotypes saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postPhenotypeData(@ApiParam(value = DocumentationAnnotation.RAW_DATA_POST_DATA_DEFINITION) ArrayList phenotypes, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + //Si dans les données envoyées, il y au moins une provenance (phenotypes) + if (phenotypes != null && !phenotypes.isEmpty()) { + PhenotypeDaoMongo phenotypesDaoMongo = new PhenotypeDaoMongo(); + phenotypesDaoMongo.user = userSession.getUser(); + + //Vérification des données et insertion dans MongoDb + POSTResultsReturn result = phenotypesDaoMongo.checkAndInsert(phenotypes); + + postResponse = new ResponseFormPOST(result.statusList); + + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty phenotypes to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche de phénotypes) + * @param phenotypeDaoMongo + * @return la réponse pour l'utilisateur, + * Contient la liste des phénotypes correspondant à la recherche (/!\ il y a de la pagination) + */ + private Response getPhenotypesData(PhenotypeDaoMongo phenotypeDaoMongo) { + //TODO reprendre Phenotype pour l'adapter au résultat attendu pour la recherche.. + ArrayList phenotypes = new ArrayList<>(); + ArrayList statusList = new ArrayList<>(); + ResponseFormPhenotype getResponse; + + phenotypes = phenotypeDaoMongo.allPaginate(); + + if (phenotypes == null) { + getResponse = new ResponseFormPhenotype(0, 0, phenotypes, true); + return noResultFound(getResponse, statusList); + } else if (!phenotypes.isEmpty()) { + getResponse = new ResponseFormPhenotype(phenotypeDaoMongo.getPageSize(), phenotypeDaoMongo.getPage(), phenotypes, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormPhenotype(0, 0, phenotypes, true); + return noResultFound(getResponse, statusList); + } + } + + private Response noResultFound(ResponseFormPhenotype getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the agronomical objects")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + /** + * + * @param pageSize + * @param page + * @param experiment + * @param variable + * @param agronomicalObjects + * @param startDate + * @param endDate + * @return liste des phenotypes correspondant aux différents critères de recherche + * (ou tous les phénotypes si pas de critères) + * Le retour (dans "data") est de la forme : + * [ + * { description du phenotype1 }, + * { description du phenotype2 }, + * ] + */ + @GET + @ApiOperation(value = "Get all phenotypes corresponding to the search params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all phenotypes", response = Phenotype.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getPhenotypesBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int pageSize, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by experiment", example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_URI) @QueryParam("experiment") String experiment, + @ApiParam(value = "Search by variable", example = DocumentationAnnotation.EXAMPLE_VARIABLE_URI) @QueryParam("variable") String variable, + @ApiParam(value = "Search by agronomical(s) object(s), séparated by coma", example = DocumentationAnnotation.EXAMPLE_AGRONOMICAL_OBJECT_URI + "," + DocumentationAnnotation.EXAMPLE_AGRONOMICAL_OBJECT_URI) @QueryParam("agronomicalObjects") String agronomicalObjects, + @ApiParam(value = "Search by interval - Start date", example = DocumentationAnnotation.EXAMPLE_PROJECT_DATE_START) @QueryParam("startDate") String startDate, + @ApiParam(value = "Search by interval - End date", example = DocumentationAnnotation.EXAMPLE_PROJECT_DATE_END) @QueryParam("endDate") String endDate) { + + PhenotypeDaoMongo phenotypeDaoMongo = new PhenotypeDaoMongo(); + + if (experiment != null) { + phenotypeDaoMongo.experiment = experiment; + } + if (variable != null) { + phenotypeDaoMongo.variable = variable; + } + if (startDate != null && endDate != null) { + //SILEX:todo + //Faire une vérification du format des dates etc. + phenotypeDaoMongo.startDate = startDate; + phenotypeDaoMongo.endDate = endDate; + //\SILEX:todo + } + if (agronomicalObjects != null) { + //les uris d'objets agronomiques doivent être séparées par des "," + String[] agronomicalObjectsURIs = agronomicalObjects.split(","); + phenotypeDaoMongo.agronomicalObjects.addAll(Arrays.asList(agronomicalObjectsURIs)); + } + + phenotypeDaoMongo.user = userSession.getUser(); + phenotypeDaoMongo.setPage(page); + phenotypeDaoMongo.setPageSize(pageSize); + + return getPhenotypesData(phenotypeDaoMongo); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/ProjectResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/ProjectResourceService.java new file mode 100644 index 000000000..2d1ac8514 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/ProjectResourceService.java @@ -0,0 +1,346 @@ +//********************************************************************************************** +// ProjectResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: Represents the project data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.phis.ProjectDao; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.ProjectDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormGET; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.brapi.form.ResponseFormProject; +import phis2ws.service.view.model.phis.Project; + +@Api("/projects") +@Path("projects") +public class ProjectResourceService { + final static Logger LOGGER = LoggerFactory.getLogger(ProjectResourceService.class); + + //Session de l'utilisateur + @SessionInject + Session userSession; + + /** + * + * @param limit + * @param page + * @param uri + * @param name + * @param acronyme + * @param subprojectType + * @param financialSupport + * @param financialName + * @param dateStart + * @param dateEnd + * @param keywords + * @param parentProject + * @param website + * @param type + * @return liste des projets correspondant aux différents critères de recherche + * (ou tous les projets si pas de critères) + * Le retour (dans "data") est de la forme : + * [ + * { description du projet1 }, + * { description du projet2 }, + * ... + * ] + */ + @GET + @ApiOperation(value = "Get all projects corresponding to the searched params given", + notes = "Retrieve all projects authorized for the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all projects", response = Project.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA)}) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ")}) + @Produces(MediaType.APPLICATION_JSON) + public Response getProjectsBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by URI", example = DocumentationAnnotation.EXAMPLE_PROJECT_URI) @QueryParam("uri") String uri, + @ApiParam(value = "Search by name", example = DocumentationAnnotation.EXAMPLE_PROJECT_NAME) @QueryParam("name") String name, + @ApiParam(value = "Search by acronyme", example = DocumentationAnnotation.EXAMPLE_PROJECT_ACRONYME) @QueryParam("acronyme") String acronyme, + @ApiParam(value = "Search by subproject type", example = DocumentationAnnotation.EXAMPLE_PROJECT_SUBPROJECT_TYPE) @QueryParam("subprojectType") String subprojectType, + @ApiParam(value = "Search by financial support", example = DocumentationAnnotation.EXAMPLE_PROJECT_FINANCIAL_SUPPORT) @QueryParam("financialSupport") String financialSupport, + @ApiParam(value = "Search by financial name", example = DocumentationAnnotation.EXAMPLE_PROJECT_FINANCIAL_NAME) @QueryParam("financialName") String financialName, + @ApiParam(value = "Search by date start", example = DocumentationAnnotation.EXAMPLE_PROJECT_DATE_START) @QueryParam("dateStart") String dateStart, + @ApiParam(value = "Search by date end", example = DocumentationAnnotation.EXAMPLE_PROJECT_DATE_END) @QueryParam("dateEnd") String dateEnd, + @ApiParam(value = "Searcg by keywords", example = DocumentationAnnotation.EXAMPLE_PROJECT_KEYWORDS) @QueryParam("keywords") String keywords, + @ApiParam(value = "Search by parent project", example = DocumentationAnnotation.EXAMPLE_PROJECT_PARENT_PROJECT) @QueryParam("parentProject") String parentProject, + @ApiParam(value = "Search by website", example = DocumentationAnnotation.EXAMPLE_PROJECT_WEBSITE) @QueryParam("website") String website) { + + ProjectDao projectDao = new ProjectDao(); + if (uri != null) { + projectDao.uri = uri; + } + if (name != null) { + projectDao.name = name; + } + if (acronyme != null) { + projectDao.acronyme = acronyme; + } + if (subprojectType != null) { + projectDao.subprojectType = subprojectType; + } + if (financialSupport != null) { + projectDao.financialSupport = financialSupport; + } + if (financialName != null) { + projectDao.financialName = financialName; + } + if (dateStart != null) { + projectDao.dateStart = dateStart; + } + if (dateEnd != null) { + projectDao.dateEnd = dateEnd; + } + if (keywords != null) { + projectDao.keywords = keywords; + } + if (parentProject != null) { + projectDao.parentProject = parentProject; + } + if (website != null) { + projectDao.website = website; + } + + projectDao.user = userSession.getUser(); + projectDao.setPageSize(limit); + projectDao.setPage(page); + + return getProjectsData(projectDao); + } + + /** + * + * @param projectURI + * @param limit + * @param page + * @return le projet correspondant à l'uri donnée s'il existe + */ + @GET + @Path("{projectURI}") + @ApiOperation(value = "Get a project", + notes = "Retrieve a project. Need URL encoded project URI (Unique Resource Identifier)") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve a project.", response = Project.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getProjectDetails( + @ApiParam(value = DocumentationAnnotation.PROJECT_URI_DEFINITION, required = true, example = DocumentationAnnotation.EXAMPLE_PROJECT_URI) @PathParam("projectURI") String projectURI, + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page) { + if (projectURI == null) { + final Status status = new Status("Access error", StatusCodeMsg.ERR, "Empty Project URI"); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(status)).build(); + } + + ProjectDao projectDao = new ProjectDao(projectURI); + projectDao.setPageSize(limit); + projectDao.setPage(page); + + projectDao.user = userSession.getUser(); + + return getProjectsData(projectDao); + } + + @POST + @ApiOperation(value = "Post a project", + notes = "Register a new project in the database") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Project saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA)}) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ")}) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postProject( + @ApiParam(value = DocumentationAnnotation.PROJECT_POST_DATA_DEFINITION) ArrayList projects, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + //Si dans les données envoyées, il y a au moins un projet + if (projects != null && !projects.isEmpty()) { + ProjectDao projectDao = new ProjectDao(); + if (projectDao.remoteUserAdress != null) { + projectDao.remoteUserAdress = context.getRemoteAddr(); + } + + projectDao.user = userSession.getUser(); + + //Vérification des projets et insertion en BD + POSTResultsReturn result = projectDao.checkAndInsertList(projects); + + if (result.getHttpStatus().equals(Response.Status.CREATED)) { //201, projets insérés + postResponse = new ResponseFormPOST(result.statusList); + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty projects(s) to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + /** + * @action modifie une liste de projets en fonction des modifs envoyées + * @param projects + * @param context + * @return Response le resultat de la requete + */ + @PUT + @ApiOperation(value = "Update project") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Project updated", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 404, message = "Project not found"), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response putProject( + @ApiParam(value = DocumentationAnnotation.PROJECT_POST_DATA_DEFINITION) ArrayList projects, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + if (projects != null && !projects.isEmpty()) { + ProjectDao projectDao = new ProjectDao(); + if (projectDao.remoteUserAdress != null) { + projectDao.remoteUserAdress = context.getRemoteAddr(); + } + projectDao.user = userSession.getUser(); + + //Vérification des données et update de la BD + POSTResultsReturn result = projectDao.checkAndUpdateList(projects); + + if (result.getHttpStatus().equals(Response.Status.OK)) { //200 users modifiés + postResponse = new ResponseFormPOST(result.statusList); + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty projects(s) to update")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormProject getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the projects")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + private Response sqlError(ResponseFormProject getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("SQL error ", StatusCodeMsg.ERR, "can't fetch result")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(getResponse).build(); + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche de projets) + * @param projectDao ProjectDao + * @return la réponse pour l'utilisateur + * contient la liste des projets correspondant à la recherche + */ + private Response getProjectsData(ProjectDao projectDao) { + ArrayList projects = new ArrayList<>(); + ArrayList statusList = new ArrayList<>(); + ResponseFormProject getResponse; + Integer projectsCount = projectDao.count(); + + if (projectsCount != null && projectsCount == 0) { + getResponse = new ResponseFormProject(projectDao.getPageSize(), projectDao.getPage(), projects, true); + return noResultFound(getResponse, statusList); + } else { + projects = projectDao.allPaginate(); + if (projects == null) { + projects = new ArrayList<>(); + getResponse = new ResponseFormProject(0, 0, projects, true); + return sqlError(getResponse, statusList); + } else if (!projects.isEmpty() && projectsCount != null) { + getResponse = new ResponseFormProject(projectDao.getPageSize(), projectDao.getPage(), projects, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormProject(0, 0, projects, true); + return noResultFound(getResponse, statusList); + } + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/TokenResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/TokenResourceService.java new file mode 100644 index 000000000..dd7291167 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/TokenResourceService.java @@ -0,0 +1,377 @@ +//********************************************************************************************** +// TokenResourceService.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2015 +// Creation date: november 2015 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Represents the token data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPublicKey; +import java.util.Date; +import java.sql.SQLException; +import java.text.ParseException; +import java.util.ArrayList; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.apache.commons.codec.binary.Hex; +import org.joda.time.DateTime; +import org.joda.time.Seconds; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.authentication.Session; +import phis2ws.service.dao.phis.UserDaoPhisBrapi; +import phis2ws.service.authentication.TokenManager; +import phis2ws.service.authentication.TokenResponseStructure; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.model.User; +import phis2ws.service.resources.dto.LogoutDTO; +import phis2ws.service.resources.dto.TokenDTO; +import phis2ws.service.utils.JsonConverter; +import phis2ws.service.utils.ResourcesUtils; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.brapi.form.ResponseUnique; + +/** + * REST Web Service + * + * TokenResourceService - Classe correspondant au chemin brapi/v1/token ou token + * du Web Service + * + * @version1.0 + * + * @author Samuël Chérimont + * @date 26/11/2015 + * @update 03/08/2016 Ajout du JWT + * @see https://jwt.io/introduction/ + * @see + * http://connect2id.com/products/nimbus-jose-jwt/examples/jwt-with-rsa-signature + */ +//@Path("{brapi/v1/token|token}") +//@RolesAllowed("ADMIN") +//@PermitAll +@Api(value = "/brapi/v1/token") +@Path("brapi/v1/token") +public class TokenResourceService { + + final static Logger logger = LoggerFactory.getLogger(TokenResourceService.class); + + /** + * getToken() - Méthode appelé par la requête HTTP GET suivi de l'URI de la + * classe TokenResourceService Crée un identifiant de session avec + * createId(), ou le récupère si il existe déja après avoir vérifié la + * validité des identifiants grâce à la méthode checkAuthentification + * + * exemple GET brapi/v1/jsonToken?username=true&password=true + * + * @param jsonToken + * @param ui + * @return un objet Response contenant le résultat en JSON ou une erreur de + * type BAD_REQUEST + * + * @see + * createId(),checkConnectionIds(),TokenManager.searchSession(),Sessionb + * @date 26/11/2015 + * @note La méthode GET doit être remplacée par POST pour des raisons de + * sécurité + * @update AC 05/2016 Ajout TokenDAO et modification des fichiers de + * propriétés + * @update AC 07/2016 Ajout JWT et modification des fichiers de propriétés + + * ajout DAO + * @update AC 08/2016 Moficiation des calls de l'APi BRAPI Exemple de Token + * + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Login", + notes = "Returns an access token when the user is known and it issuer too", + response = TokenResponseStructure.class) + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Access token created by user"), + @ApiResponse(code = 400, message = "Bad informations send by user"), + @ApiResponse(code = 200, message = "Access token already exist and send again to user")}) + public Response getToken(@ApiParam(value = "JSON object needed to login", required = true) TokenDTO jsonToken, @Context UriInfo ui) { + ArrayList statusList = new ArrayList<>(); + String jwt = null; + String username = jsonToken.getUsername(); + String password = null; + boolean validJWTToken = false; + if (jsonToken.getGrant_type().equals("jwt") && jsonToken.getClient_id() != null) { + jwt = jsonToken.getClient_id(); + SignedJWT signedJWT = null; + try { + signedJWT = SignedJWT.parse(jwt); + JWTClaimsSet jwtClaimsSet = signedJWT.getJWTClaimsSet(); + if (jwtClaimsSet.getIssuer().equals("GnpIS")) { + String FilePropertyName = PropertiesFileManager.getConfigFileProperty("service", "gnpisPublicKeyFileName"); +// LOGGER.debug(signedJWT.getPayload().toString()); +// LOGGER.debug(signedJWT.getHeader().toString()); + RSAPublicKey publicKey = PropertiesFileManager.parseBinaryPublicKey(FilePropertyName); + JWSVerifier verifier = new RSASSAVerifier(publicKey); + boolean validPublicKey = signedJWT.verify(verifier); + boolean strangeJWT = new Date().after(signedJWT.getJWTClaimsSet().getIssueTime()); + boolean expireJWT = new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime()); + boolean subjectMatch = jwtClaimsSet.getSubject().equals(username); + if (!subjectMatch) { + statusList.add(new Status("conflict with JWT name and username", StatusCodeMsg.ERR, null)); + } + if (!strangeJWT) { + statusList.add(new Status("invalid JWT", StatusCodeMsg.ERR, null)); + } + if (!validPublicKey) { + statusList.add(new Status("invalid JWT", StatusCodeMsg.ERR, null)); + } + if (!expireJWT) { + statusList.add(new Status("JWT expired", StatusCodeMsg.ERR, null)); + } + if (expireJWT && validPublicKey && strangeJWT && subjectMatch) { + validJWTToken = true; + } + } else { + statusList.add(new Status("Bad Issuer", StatusCodeMsg.ERR, null)); + } + + } catch (ParseException | JOSEException ex) { + logger.error(ex.getMessage(), ex); + statusList.add(new Status("JWT Error", StatusCodeMsg.ERR, ex.getMessage())); + } + } else if (password == null) { + password = jsonToken.getPassword(); + } + logger.debug(username); + logger.debug(password); +// LOGGER.debug(jwt); +// LOGGER.debug(Boolean.toString(validJWTToken)); + if ((password != null && username != null) || (jwt != null && validJWTToken && username != null)) { + //droit de connexion ? + User u = new User(username, password); + try { + if (jwt != null) { + u = checkAuthentification(u, false); + } else { + u = checkAuthentification(u, true); + } + + if (u == null) { + statusList.add(new Status("User/password doesn't exist", StatusCodeMsg.ERR, null)); + } else { + + //token existant ? + String id = TokenManager.Instance().searchSession(username); + Response.Status reponseStatus = Response.Status.OK; + String expires_in = null; + if (id == null) { + //creation d'un jsonToken + id = this.createId(username); + Session s = new Session(id, username, u); + TokenManager.Instance().createToken(s); + reponseStatus = Response.Status.CREATED; + } else { + Session session = TokenManager.Instance().getSession(id); + DateTime sessionStartDateTime = ResourcesUtils.convertStringToDateTime(session.getDateStart(), "yyyy-MM-dd HH:mm:ss"); + if (sessionStartDateTime != null) { + Seconds secondsBetween = Seconds.secondsBetween(sessionStartDateTime, new DateTime()); + expires_in = Integer.toString(Integer.valueOf(PropertiesFileManager.getConfigFileProperty("service", "sessionTime")) - secondsBetween.getSeconds()); + } + } + //envoi résultat + TokenResponseStructure res = new TokenResponseStructure(id, u.getFirstName() + " " + u.getFamilyName(), expires_in); + final URI uri = new URI(ui.getPath()); + return Response.status(reponseStatus).location(uri).entity(res).build(); + } + + } catch (NoSuchAlgorithmException | SQLException | URISyntaxException ex) { + logger.error(ex.getMessage(), ex); + statusList.add(new Status("SQL " + StatusCodeMsg.ERR, StatusCodeMsg.ERR, ex.getMessage())); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(new ResponseFormPOST(statusList)).build(); + } + } else { + if (jwt == null && password == null) { + statusList.add(new Status("Empty password", StatusCodeMsg.ERR, null)); + } + if (jwt == null && password == null) { + statusList.add(new Status("Empty username", StatusCodeMsg.ERR, null)); + } + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormPOST(statusList)).build(); + } + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormPOST(statusList)).build(); + } + + @DELETE + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Log out", + notes = "Disconnect a logged user", + response = ResponseUnique.class) + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Access token created by user"), + @ApiResponse(code = 400, message = "Bad informations send by user"), + @ApiResponse(code = 200, message = "Access token already exist and send again to user")}) + public Response logOut(@ApiParam(value = "JSON object needed to login", required = true) LogoutDTO logout, @Context UriInfo ui) { + ArrayList statusList = new ArrayList<>(); + if (logout == null) { + statusList.add(new Status("Empty json", StatusCodeMsg.ERR, null)); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormPOST(statusList)).build(); + } + String sessionId = logout.access_token; + if (sessionId == null) { + statusList.add(new Status("Empty access_token", StatusCodeMsg.ERR, null)); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormPOST(statusList)).build(); + } + Session session = TokenManager.Instance().getSession(sessionId); + if (session == null) { + statusList.add(new Status("No session linked to this access_token", StatusCodeMsg.ERR, null)); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormPOST(statusList)).build(); + } + + TokenManager.Instance().removeSession(sessionId); + statusList.clear(); + statusList.add(new Status("User has been logged out successfully", null)); + return Response.status(Response.Status.CREATED).entity(new ResponseFormPOST(statusList)).build(); + + } + + /** + * createId() - Méthode privée appelée dans getToken() Crée un chaine de + * caractère encodée et qui sera utilisé comme nouvel identifiant de session + * + * @param username Nom de l'utilisateur, utilisé pour l'encodage + * @return un objet String correspondant à l'identifiant + * + * @throws NoSuchAlgorithmException Dans le cas ou l'algorithme d'encodage + * n'existe pas ou plus + * @throws SQLException Dans le cas d'un probleme dans la colonne session de + * la BDD + * + * @see getConnection() + * @date 26/11/2015 + * + * @update AT 11/02/2016 fonction cré et renvoi un ID crypté (pas besoin de + * vérifier) + */ + private String createId(String username) throws NoSuchAlgorithmException, SQLException { +// SqlRequest sqlr = new SqlRequest(); +// boolean brk = false; + String id = null; +// while (brk == false) { + id = new String(Hex.encodeHex((MessageDigest.getInstance("MD5").digest((username + new Date().toString()).getBytes())))); +// ResultSet res = sqlr.getSQLRequest("SELECT * FROM session WHERE id = '" + id + "'", "phis"); +// if (!res.first()) { +// brk = true; +// } +// } + return id; + } + + private User checkAuthentification(User u, boolean verifPassword) { + UserDaoPhisBrapi uspb = new UserDaoPhisBrapi(); + final String password = u.getPassword(); + + try { + if (uspb.existInDB(u)) { + u.setPassword(uspb.getPasswordFromDb(u.getEmail())); + } else { + u = null; + } + if (verifPassword && u != null) { + if (password.equals(u.getPassword())) { + uspb.admin = uspb.isAdmin(u); + } else { + u = null; + } + } + logger.debug(JsonConverter.ConvertToJson(u)); + return u; + } catch (Exception ex) { + logger.error(ex.getMessage(), ex); + } + + return null; + } + +// public static void main(String[] args) { +// UserDaoPhisBrapi ubpd = new UserDaoPhisBrapi(); +// User u = new User("arnaud.charleroy@supagro.inra.fr", "pic3.14"); +// try { +// ubpd.find(u); +// } catch (Exception ex) { +// LOGGER.error(ex.getMessage(),ex); +// } +// ubpd.isAdmin(u); +// LOGGER.debug(JsonConverter.ConvertToJson(u)); +// System.exit(0); +// } +// public static void main(String[] args) throws NoSuchAlgorithmException { +// try { +// // RSA signatures require a public and private RSA key pair, the public key +// // must be made known to the JWS recipient in order to verify the signatures +// KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA"); +// keyGenerator.initialize(1024); +// +// java.security.KeyPair kp = keyGenerator.genKeyPair(); +// RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic(); +// RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate(); +// +// // Create RSA-signer with the private key +// JWSSigner signer = new RSASSASigner(privateKey); +// +// // Prepare JWT with claims set +// JWTClaimsSet claimsSet = new JWTClaimsSet.Builder() +// .claim("mdp", "pic3.14") +// .subject("arnaud.charleroy@supagro.inra.fr") +// .issuer("GnpIS") +// .issueTime(new DateTime().toDate()) +// .expirationTime(new DateTime(new DateTime().getMillis() + 60 * 1000).toDate()) +// .build(); +// +// SignedJWT signedJWT = new SignedJWT( +// new JWSHeader(JWSAlgorithm.RS256), +// claimsSet); +// signedJWT.sign(signer); +// LOGGER.debug(signedJWT.serialize()); +// // Read +//// String jwt ="eyJhbGciOiJSUzI1NiJ9" +//// + ".eyJzdWIiOiJndWlsbGF1bWUuY29ybnV0QHZlcnNhaWxsZXMuaW5yYS5mciIsImlzcyI6IkducElTIiwiaWF0IjoxNDY3MjEyODE2LCJleHAiOjE0NjcyMTI4NzZ9" +//// + ".VdIMlA6BXAoJ6V1EEOs1m4d75mtH2FDlilbGFpvgJy1e0xJw836NaRVHD766miUPpvEQjm6rSaXIZeYcrSAMaZpYYIGou89XxHBpxJ_3axYBQX3KycSekJJqq3qhPS6WrZp4qz0hMtOtpCOf2l2JVyMzjy_62Le-7AOq3hn1dZY"; +//// +//// +//// SignedJWT signedJWT = SignedJWT.parse(jwt); +//// JWTClaimsSet jwtClaimsSet = signedJWT.getJWTClaimsSet(); +//// LOGGER.debug(jwtClaimsSet.getStringClaim("sub")); +//// LOGGER.debug(Boolean.toString(new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime()))); +// } catch (Exception ex) { +// LOGGER.debug(ex.getMessage(), ex); +// }finally{ +// System.exit(0); +// } +// +// } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/TraitResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/TraitResourceService.java new file mode 100644 index 000000000..440d63c37 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/TraitResourceService.java @@ -0,0 +1,272 @@ +//********************************************************************************************** +// TraitResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 17 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 17 2017 +// Subject: Represents the trait data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.sesame.TraitDaoSesame; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.TraitDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormGET; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.brapi.form.ResponseFormTrait; +import phis2ws.service.view.model.phis.Trait; + +@Api("/traits") +@Path("traits") +public class TraitResourceService { + final static Logger LOGGER = LoggerFactory.getLogger(TraitResourceService.class); + + //Session utilisateur + @SessionInject + Session userSession; + + @POST + @ApiOperation(value = "Post trait(s)", + notes = "Register new trait(s) in the data base") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Trait(s) saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postTrait(@ApiParam(value = DocumentationAnnotation.TRAIT_POST_DATA_DEFINITION) ArrayList traits, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + if (traits != null && !traits.isEmpty()) { + TraitDaoSesame traitDaoSesame = new TraitDaoSesame(); + if (context.getRemoteAddr() != null) { + traitDaoSesame.remoteUserAdress = context.getRemoteAddr(); + } + + traitDaoSesame.user = userSession.getUser(); + + POSTResultsReturn result = traitDaoSesame.checkAndInsert(traits); + + if (result.getHttpStatus().equals(Response.Status.CREATED)) { + //Code 201, traits insérés + postResponse = new ResponseFormPOST(result.statusList); + postResponse.getMetadata().setDatafiles(result.getCreatedResources()); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty traits(s) to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + @PUT + @ApiOperation(value = "Update trait") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Trait updated", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 404, message = "Trait not found"), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response putTrait( + @ApiParam(value = DocumentationAnnotation.TRAIT_POST_DATA_DEFINITION) ArrayList traits, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + if (traits != null && !traits.isEmpty()) { + TraitDaoSesame traitDaoSesame = new TraitDaoSesame(); + if (context.getRemoteAddr() != null) { + traitDaoSesame.remoteUserAdress = context.getRemoteAddr(); + } + + traitDaoSesame.user = userSession.getUser(); + + POSTResultsReturn result = traitDaoSesame.checkAndUpdate(traits); + + if (result.getHttpStatus().equals(Response.Status.OK)) { + //Code 200, traits modifiés + postResponse = new ResponseFormPOST(result.statusList); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty traits(s) to update")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormTrait getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the traits")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche de traits) + * @param traitDaoSesame + * @return la réponse pour l'utilisateur. Contient la liste des traits + * correspondant à la recherche + * SILEX:TODO + * on ne peut chercher que par uri et label. Il faudra ajouter d'autres critères + * \SILEX:TODO + */ + private Response getTraitsData(TraitDaoSesame traitDaoSesame) { + ArrayList traits; + ArrayList statusList = new ArrayList<>(); + ResponseFormTrait getResponse; + + traits = traitDaoSesame.allPaginate(); + + if (traits == null) { + getResponse = new ResponseFormTrait(0, 0, traits, true); + return noResultFound(getResponse, statusList); + } else if (!traits.isEmpty()) { + getResponse = new ResponseFormTrait(traitDaoSesame.getPageSize(), traitDaoSesame.getPage(), traits, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormTrait(0, 0, traits, true); + return noResultFound(getResponse, statusList); + } + } + + @GET + @ApiOperation(value = "Get all Traits corresponding to the searched params given", + notes = "Retrieve all traits authorized for the user corresponding to the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all traits", response = Trait.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getTraitsBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by URI", example = DocumentationAnnotation.EXAMPLE_TRAIT_URI) @QueryParam("uri") String uri, + @ApiParam(value = "Search by label", example = DocumentationAnnotation.EXAMPLE_TRAIT_LABEL) @QueryParam("label") String label + ) { + TraitDaoSesame traitDaoSesame = new TraitDaoSesame(); + + if (uri != null) { + traitDaoSesame.uri = uri; + } + if (label != null) { + traitDaoSesame.label = label; + } + + traitDaoSesame.user = userSession.getUser(); + traitDaoSesame.setPage(page); + traitDaoSesame.setPageSize(limit); + + return getTraitsData(traitDaoSesame); + } + + /** + * + * @param trait + * @param limit + * @param page + * @return le trait correspondant à l'uri donnée si elle existe + */ + @GET + @Path("{trait}") + @ApiOperation(value = "Get a trait", + notes = "Retrieve a trait. Need URL encoded trait URI (Unique resource identifier).") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve a trait.", response = Trait.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getTraitDetails( + @ApiParam(value = DocumentationAnnotation.TRAIT_URI_DEFINITION, required = true, example = DocumentationAnnotation.EXAMPLE_TRAIT_URI) @PathParam("trait") String trait, + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page) { + + if (trait == null) { + final Status status = new Status("Access error", StatusCodeMsg.ERR, "Empty trait URI"); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(status)).build(); + } + + TraitDaoSesame traitDaoSesame = new TraitDaoSesame(); + traitDaoSesame.uri = trait; + traitDaoSesame.setPageSize(limit); + traitDaoSesame.setPage(page); + traitDaoSesame.user = userSession.getUser(); + + return getTraitsData(traitDaoSesame); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/UnitResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/UnitResourceService.java new file mode 100644 index 000000000..5467d4ee7 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/UnitResourceService.java @@ -0,0 +1,280 @@ +//********************************************************************************************** +// UnitResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 18 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 18 2017 +// Subject: Represents the method data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.sesame.UnitDaoSesame; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.UnitDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormGET; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.brapi.form.ResponseFormUnit; +import phis2ws.service.view.model.phis.Unit; + +@Api("/units") +@Path("units") +public class UnitResourceService { + final static Logger LOGGER = LoggerFactory.getLogger(UnitResourceService.class); + + //Session utilisateur + @SessionInject + Session userSession; + + @POST + @ApiOperation(value = "Post unit(s)", + notes = "Register new unit(s) in the data base") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Unit(s) saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postUnit(@ApiParam(value = DocumentationAnnotation.UNIT_POST_DATA_DEFINITION) ArrayList units, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + if (units != null && !units.isEmpty()) { + UnitDaoSesame unitDaoSesame = new UnitDaoSesame(); + if (context.getRemoteAddr() != null) { + unitDaoSesame.remoteUserAdress = context.getRemoteAddr(); + } + + unitDaoSesame.user = userSession.getUser(); + + POSTResultsReturn result = unitDaoSesame.checkAndInsert(units); + + if (result.getHttpStatus().equals(Response.Status.CREATED)) { + //Code 201, unités insérés + postResponse = new ResponseFormPOST(result.statusList); + postResponse.getMetadata().setDatafiles(result.getCreatedResources()); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty unit(s) to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + @PUT + @ApiOperation(value = "Update unit") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Unit updated", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 404, message = "Unit not found"), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response putUnit( + @ApiParam(value = DocumentationAnnotation.UNIT_POST_DATA_DEFINITION) ArrayList units, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + if (units != null && !units.isEmpty()) { + UnitDaoSesame unitDaoSesame = new UnitDaoSesame(); + if (context.getRemoteAddr() != null) { + unitDaoSesame.remoteUserAdress = context.getRemoteAddr(); + } + + unitDaoSesame.user = userSession.getUser(); + + POSTResultsReturn result = unitDaoSesame.checkAndUpdate(units); + + if (result.getHttpStatus().equals(Response.Status.OK)) { + //Code 200, unités modifiées + postResponse = new ResponseFormPOST(result.statusList); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty unit(s) to update")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormUnit getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the units")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche d'unités) + * @param unitDaoSesame + * @return la réponse pour l'utilisateur. Contient la liste des unités + * correspondant à la recherche + * SILEX:TODO + * on ne peut chercher que par uri et label. Il faudra ajouter d'autres critères + * \SILEX:TODO + */ + private Response getUnitsData(UnitDaoSesame unitDaoSesame) { + ArrayList units; + ArrayList statusList = new ArrayList<>(); + ResponseFormUnit getResponse; + + units = unitDaoSesame.allPaginate(); + + if (units == null) { + getResponse = new ResponseFormUnit(0, 0, units, true); + return noResultFound(getResponse, statusList); + } else if (!units.isEmpty()) { + getResponse = new ResponseFormUnit(unitDaoSesame.getPageSize(), unitDaoSesame.getPage(), units, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormUnit(0, 0, units, true); + return noResultFound(getResponse, statusList); + } + } + + /** + * + * @param limit + * @param page + * @param uri + * @param label + * @return + */ + @GET + @ApiOperation(value = "Get all units corresponding to the searched params given", + notes = "Retrieve all units authorized for the user corresponding to the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all units", response = Unit.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getUnitsBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by URI", example = DocumentationAnnotation.EXAMPLE_UNIT_URI) @QueryParam("uri") String uri, + @ApiParam(value = "Search by label", example = DocumentationAnnotation.EXAMPLE_UNIT_LABEL) @QueryParam("label") String label + ) { + UnitDaoSesame unitDaoSesame = new UnitDaoSesame(); + + if (uri != null) { + unitDaoSesame.uri = uri; + } + if (label != null) { + unitDaoSesame.label = label; + } + + unitDaoSesame.user = userSession.getUser(); + unitDaoSesame.setPage(page); + unitDaoSesame.setPageSize(limit); + + return getUnitsData(unitDaoSesame); + } + /** + * + * @param unit + * @param limit + * @param page + * @return l'unité correspondant à l'uri donnée si elle existe + */ + @GET + @Path("{unit}") + @ApiOperation(value = "Get a unit", + notes = "Retrieve a unit. Need URL encoded unit URI (Unique resource identifier).") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve a unit.", response = Unit.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getUnitDetails( + @ApiParam(value = DocumentationAnnotation.UNIT_URI_DEFINITION, required = true, example = DocumentationAnnotation.EXAMPLE_UNIT_URI) @PathParam("unit") String unit, + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page) { + + if (unit == null) { + final Status status = new Status("Access error", StatusCodeMsg.ERR, "Empty unit URI"); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(status)).build(); + } + + UnitDaoSesame unitDaoSesame = new UnitDaoSesame(); + unitDaoSesame.uri = unit; + unitDaoSesame.setPageSize(limit); + unitDaoSesame.setPage(page); + unitDaoSesame.user = userSession.getUser(); + + return getUnitsData(unitDaoSesame); + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/UserResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/UserResourceService.java new file mode 100644 index 000000000..7d4136772 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/UserResourceService.java @@ -0,0 +1,332 @@ +//********************************************************************************************** +// UserResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: April, 2017 +// Subject: Represents the user data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.phis.UserDaoPhisBrapi; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.model.User; +import phis2ws.service.resources.dto.UserDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.utils.ResourcesUtils; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormGET; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.brapi.form.ResponseFormUser; + +@Api("/user") +@Path("users") +public class UserResourceService { + final static Logger LOGGER = LoggerFactory.getLogger(UserResourceService.class); + + //Session de l'utilisateur + @SessionInject + Session userSession; + + /** + * + * @param limit + * @param page + * @param email + * @param firstName + * @param familyName + * @param address + * @param phone + * @param affiliation + * @param orcid + * @param admin + * @param available + * @return liste des utilisateurs correspondant aux critères de recherche + * (ou tous les utilisateurs si pas de critères) + * Le retour (dans "data") est de la forme : + * [ + * { description du utilisateur1 }, + * { description du utilisateur2 }, + * ... + * ] + */ + @GET + @ApiOperation(value = "Get all users corresponding to the searched params given", + notes = "Retrieve all users authorized for the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all users", response = User.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getUserBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by email", example = DocumentationAnnotation.EXAMPLE_USER_EMAIL) @QueryParam("email") String email, + @ApiParam(value = "Search by first name", example = DocumentationAnnotation.EXAMPLE_USER_FIRST_NAME) @QueryParam("firstName") String firstName, + @ApiParam(value = "Search by family name", example = DocumentationAnnotation.EXAMPLE_USER_FAMILY_NAME) @QueryParam("familyName") String familyName, + @ApiParam(value = "Search by address", example = DocumentationAnnotation.EXAMPLE_USER_ADDRESS) @QueryParam("address") String address, + @ApiParam(value = "Search by phone", example = DocumentationAnnotation.EXAMPLE_USER_PHONE) @QueryParam("phone") String phone, + @ApiParam(value = "Search by affiliation", example = DocumentationAnnotation.EXAMPLE_USER_AFFILIATION) @QueryParam("affiliation") String affiliation, + @ApiParam(value = "Search by orcid", example = DocumentationAnnotation.EXAMPLE_USER_ORCID) @QueryParam("orcid") String orcid, + @ApiParam(value = "Search by admin", example = DocumentationAnnotation.EXAMPLE_USER_ADMIN) @QueryParam("admin") String admin, + @ApiParam(value = "Search by available", example = DocumentationAnnotation.EXAMPLE_USER_AVAILABLE) @QueryParam("available") String available) { + UserDaoPhisBrapi userDao = new UserDaoPhisBrapi(); + if (email != null) { + userDao.email = email; + } + if (firstName != null) { + userDao.firstName = firstName; + } + if (familyName != null) { + userDao.familyName = familyName; + } + if (userDao.address != null) { + userDao.address = address; + } + if (userDao.phone != null) { + userDao.phone = phone; + } + if (userDao.affiliation != null) { + userDao.affiliation = affiliation; + } + if (userDao.orcid != null) { + userDao.orcid = orcid; + } + if (admin != null) { + userDao.admin = ResourcesUtils.getStringBooleanValue(admin); + } + if (userDao.available != null) { + userDao.available = available; + } + + userDao.setPageSize(limit); + userDao.setPage(page); + + return getUsersData(userDao); + } + + /** + * + * @param userEmail + * @param limit + * @param page + * @return l'utilisateur correspondant à l'email s'il existe + */ + @GET + @Path("{userEmail}") + @ApiOperation(value = "Get a user", + notes = "Retrieve a user. Need user email") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve a user.", response = User.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getUserDetails( + @ApiParam(value = DocumentationAnnotation.USER_EMAIL_DEFINITION, required = true, example = DocumentationAnnotation.EXAMPLE_USER_EMAIL) @PathParam("userEmail") String userEmail, + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page) { + if (userEmail == null) { + final Status status = new Status("Access error", StatusCodeMsg.ERR, "Empty User email"); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(status)).build(); + } + + UserDaoPhisBrapi userDao = new UserDaoPhisBrapi(userEmail); + userDao.setPageSize(limit); + userDao.setPage(page); + + userDao.user = userSession.getUser(); + + return getUsersData(userDao); + } + + @POST + @ApiOperation(value = "Post a user", + notes = "Register a new user in the database") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "User saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postUser( + @ApiParam(value = DocumentationAnnotation.USER_POST_DATA_DEFINITION) ArrayList users, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + //Si dans les données envoyées il y a au moins un user + if (users != null && !users.isEmpty()) { + UserDaoPhisBrapi userDao = new UserDaoPhisBrapi(); + if (userDao.remoteUserAdress != null) { + userDao.remoteUserAdress = context.getRemoteAddr(); + } + + userDao.user = userSession.getUser(); + + //Vérification des users et insertion en BD + POSTResultsReturn result = userDao.checkAndInsertList(users); + + if (result.getHttpStatus().equals(Response.Status.CREATED)) { //201, projets insérés + postResponse = new ResponseFormPOST(result.statusList); + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty user(s) to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + @PUT + @ApiOperation(value = "Update users") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "User updated", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 404, message = "User not found"), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response putUser( + @ApiParam(value = DocumentationAnnotation.USER_POST_DATA_DEFINITION) ArrayList users, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + if (users != null && !users.isEmpty()) { + UserDaoPhisBrapi userDao = new UserDaoPhisBrapi(); + if (userDao.remoteUserAdress != null) { + userDao.remoteUserAdress = context.getRemoteAddr(); + } + userDao.user = userSession.getUser(); + + //Vérification des données et update de la BD + POSTResultsReturn result = userDao.checkAndUpdateList(users); + + if (result.getHttpStatus().equals(Response.Status.OK)) { //200 users modifiés + postResponse = new ResponseFormPOST(result.statusList); + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty user(s) to update")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormUser getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the users")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + private Response sqlError(ResponseFormUser getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("SQL error ", StatusCodeMsg.ERR, "can't fetch result")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(getResponse).build(); + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche de users) + * @param userDao UserDaoPhisBrapi + * @return la réponse pour l'utilisateur. + * Contient la liste des users correspondant à la recherce + */ + private Response getUsersData(UserDaoPhisBrapi userDao) { + ArrayList users = new ArrayList<>(); + ArrayList statusList = new ArrayList<>(); + ResponseFormUser getResponse; + Integer usersCount = userDao.count(); + + if (usersCount != null && usersCount == 0) { + getResponse = new ResponseFormUser((userDao.getPageSize()), userDao.getPage(), users, true); + return noResultFound(getResponse, statusList); + } else { + users = userDao.allPaginate(); + if (users == null) { + users = new ArrayList<>(); + getResponse = new ResponseFormUser(0, 0, users, true); + return sqlError(getResponse, statusList); + } else if (!users.isEmpty() && usersCount != null) { + getResponse = new ResponseFormUser(userDao.getPageSize(), userDao.getPage(), users, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormUser(0, 0, users, true); + return noResultFound(getResponse, statusList); + } + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/VariableResourceService.java b/phis2-ws/src/main/java/phis2ws/service/resources/VariableResourceService.java new file mode 100644 index 000000000..4d6639ea2 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/VariableResourceService.java @@ -0,0 +1,304 @@ +//********************************************************************************************** +// VariableResourceService.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 14 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 14 2017 +// Subject: Represents the variable data service +//*********************************************************************************************** +package phis2ws.service.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.Session; +import phis2ws.service.configuration.DefaultBrapiPaginationValues; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.dao.sesame.VariableDaoSesame; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.injection.SessionInject; +import phis2ws.service.resources.dto.VariableDTO; +import phis2ws.service.utils.POSTResultsReturn; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.AbstractResultForm; +import phis2ws.service.view.brapi.form.ResponseFormGET; +import phis2ws.service.view.brapi.form.ResponseFormPOST; +import phis2ws.service.view.brapi.form.ResponseFormVariable; +import phis2ws.service.view.model.phis.Variable; + +@Api("/variables") +@Path("variables") +public class VariableResourceService { + final static Logger LOGGER = LoggerFactory.getLogger(VariableResourceService.class); + + //Session de l'utilisateur + @SessionInject + Session userSession; + + /** + * + * @param variables la liste des variables à enregistrer + * @param context + * @return + */ + @POST + @ApiOperation(value = "Post variable(s)", + notes = "Register new variable(s) in the data base") + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Variable(s) saved", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response postVariable(@ApiParam(value = DocumentationAnnotation.VARIABLE_POST_DATA_DEFINITION) ArrayList variables, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + + //Si dans les données il y a au moins une variable + if (variables != null && !variables.isEmpty()) { + VariableDaoSesame variableDao = new VariableDaoSesame(); + if (context.getRemoteAddr() != null) { + variableDao.remoteUserAdress = context.getRemoteAddr(); + } + + variableDao.user = userSession.getUser(); + + //Vérification et insertion des variables + POSTResultsReturn result = variableDao.checkAndInsert(variables); + + if (result.getHttpStatus().equals(Response.Status.CREATED)) { + //Code 201, variables insérés + postResponse = new ResponseFormPOST(result.statusList); + postResponse.getMetadata().setDatafiles(result.getCreatedResources()); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty variable(s) to add")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + @PUT + @ApiOperation(value = "Update variable") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Variable updated", response = ResponseFormPOST.class), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 404, message = "Variable not found"), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_SEND_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response putVariable( + @ApiParam(value = DocumentationAnnotation.VARIABLE_POST_DATA_DEFINITION) ArrayList variables, + @Context HttpServletRequest context) { + AbstractResultForm postResponse = null; + if (variables != null && !variables.isEmpty()) { + VariableDaoSesame variableDaoSesame = new VariableDaoSesame(); + if (context.getRemoteAddr() != null) { + variableDaoSesame.remoteUserAdress = context.getRemoteAddr(); + } + + variableDaoSesame.user = userSession.getUser(); + + POSTResultsReturn result = variableDaoSesame.checkAndUpdate(variables); + + if (result.getHttpStatus().equals(Response.Status.OK)) { + //Code 200, traits modifiés + postResponse = new ResponseFormPOST(result.statusList); + } else if (result.getHttpStatus().equals(Response.Status.BAD_REQUEST) + || result.getHttpStatus().equals(Response.Status.OK) + || result.getHttpStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) { + postResponse = new ResponseFormPOST(result.statusList); + } + return Response.status(result.getHttpStatus()).entity(postResponse).build(); + } else { + postResponse = new ResponseFormPOST(new Status("Request error", StatusCodeMsg.ERR, "Empty variable(s) to update")); + return Response.status(Response.Status.BAD_REQUEST).entity(postResponse).build(); + } + } + + private Response noResultFound(ResponseFormVariable getResponse, ArrayList insertStatusList) { + insertStatusList.add(new Status("No results", StatusCodeMsg.INFO, "No results for the variables")); + getResponse.setStatus(insertStatusList); + return Response.status(Response.Status.NOT_FOUND).entity(getResponse).build(); + } + + /** + * Collecte les données issues d'une requête de l'utilisateur (recherche de traits) + * @param variableDaoSesame + * @return la réponse pour l'utilisateur. Contient la liste des traits + * correspondant à la recherche + * SILEX:TODO + * on ne peut chercher que par uri et label. Il faudra ajouter d'autres critères + * \SILEX:TODO + */ + private Response getVariablesData(VariableDaoSesame variableDaoSesame) { + ArrayList variables; + ArrayList statusList = new ArrayList<>(); + ResponseFormVariable getResponse; + + variables = variableDaoSesame.allPaginate(); + + if (variables == null) { + getResponse = new ResponseFormVariable(0, 0, variables, true); + return noResultFound(getResponse, statusList); + } else if (!variables.isEmpty()) { + getResponse = new ResponseFormVariable(variableDaoSesame.getPageSize(), variableDaoSesame.getPage(), variables, false); + if (getResponse.getResult().dataSize() == 0) { + return noResultFound(getResponse, statusList); + } else { + getResponse.setStatus(statusList); + return Response.status(Response.Status.OK).entity(getResponse).build(); + } + } else { + getResponse = new ResponseFormVariable(0, 0, variables, true); + return noResultFound(getResponse, statusList); + } + } + + /** + * + * @param limit + * @param page + * @param uri + * @param label + * @param trait + * @param method + * @param unit + * @return + */ + @GET + @ApiOperation(value = "Get all variables corresponding to the searched params given", + notes = "Retrieve all variables authorized for the user corresponding to the user corresponding to the searched params given") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve all variables", response = Variable.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getVariablesBySearch( + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page, + @ApiParam(value = "Search by URI", example = DocumentationAnnotation.EXAMPLE_VARIABLE_URI) @QueryParam("uri") String uri, + @ApiParam(value = "Search by label", example = DocumentationAnnotation.EXAMPLE_VARIABLE_LABEL) @QueryParam("label") String label, + @ApiParam(value = "Search by trait", example = DocumentationAnnotation.EXAMPLE_TRAIT_URI) @QueryParam("trait") String trait, + @ApiParam(value = "Search by method", example = DocumentationAnnotation.EXAMPLE_METHOD_URI) @QueryParam("method") String method, + @ApiParam(value = "Search by unit", example = DocumentationAnnotation.EXAMPLE_UNIT_URI) @QueryParam("unit") String unit + ) { + VariableDaoSesame variableDaoSesame = new VariableDaoSesame(); + + if (uri != null) { + variableDaoSesame.uri = uri; + } + if (label != null) { + variableDaoSesame.label = label; + } + if (trait != null) { + variableDaoSesame.trait = trait; + } + if (method != null) { + variableDaoSesame.method = method; + } + if (unit != null) { + variableDaoSesame.unit = unit; + } + + variableDaoSesame.user = userSession.getUser(); + variableDaoSesame.setPage(page); + variableDaoSesame.setPageSize(limit); + + return getVariablesData(variableDaoSesame); + } + + /** + * + * @param variable + * @param limit + * @param page + * @return la variable correspondant à l'uri donnée si elle existe + */ + @GET + @Path("{variable}") + @ApiOperation(value = "Get a variable", + notes = "Retrieve a variable. Need URL encoded variable URI (Unique resource identifier).") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Retrieve a variable.", response = Variable.class, responseContainer = "List"), + @ApiResponse(code = 400, message = DocumentationAnnotation.BAD_USER_INFORMATION), + @ApiResponse(code = 401, message = DocumentationAnnotation.USER_NOT_AUTHORIZED), + @ApiResponse(code = 500, message = DocumentationAnnotation.ERROR_FETCH_DATA) + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "Authorization", required = true, + dataType = "string", paramType = "header", + value = DocumentationAnnotation.ACCES_TOKEN, + example = GlobalWebserviceValues.AUTHENTICATION_SCHEME + " ") + }) + @Produces(MediaType.APPLICATION_JSON) + public Response getVariableDetail( + @ApiParam(value = DocumentationAnnotation.VARIABLE_URI_DEFINITION, required = true, example = DocumentationAnnotation.EXAMPLE_VARIABLE_URI) @PathParam("variable") String variable, + @ApiParam(value = DocumentationAnnotation.PAGE_SIZE) @QueryParam("pageSize") @DefaultValue(DefaultBrapiPaginationValues.PAGE_SIZE) int limit, + @ApiParam(value = DocumentationAnnotation.PAGE) @QueryParam("page") @DefaultValue(DefaultBrapiPaginationValues.PAGE) int page) { + + if (variable == null) { + final Status status = new Status("Access error", StatusCodeMsg.ERR, "Empty variable URI"); + return Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(status)).build(); + } + + VariableDaoSesame variableDaoSesame = new VariableDaoSesame(); + variableDaoSesame.uri = variable; + variableDaoSesame.setPageSize(limit); + variableDaoSesame.setPage(page); + variableDaoSesame.user = userSession.getUser(); + + return getVariablesData(variableDaoSesame); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/AgronomicalObjectDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/AgronomicalObjectDTO.java new file mode 100644 index 000000000..a01aba669 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/AgronomicalObjectDTO.java @@ -0,0 +1,109 @@ +//********************************************************************************************** +// AgronomicalObjectDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: august 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: July 18, 2017 - modifications of the saved data and save in triplestore +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by the user +//*********************************************************************************************** + +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; +import phis2ws.service.view.model.phis.AgronomicalObject; +import phis2ws.service.view.model.phis.Property; + +public class AgronomicalObjectDTO extends AbstractVerifiedClass { + + /** + * @param typeAgronomicalObject le type de l'objet agronomique (plot, fields, cultivated land...). On donne ici l'uri du concept + * @param geometry les coordonnées GPS (idéalement en WGS84) de l'objet agronomique + * @param experiment l'uri de l'essai concerné s'il y en a un + * @param year l'année utilisée dans l'uri de l'objet agronomique. Si le champ n'est pas renseigné, on prendra l'année actuelle. + * @param properties les propriétés associées à l'objet agronomique + */ + private String typeAgronomicalObject; + private String geometry; + private String experiment; + private String year; + private ArrayList properties; + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put(typeAgronomicalObject, Boolean.TRUE); + rules.put(geometry, Boolean.TRUE); + rules.put(experiment, Boolean.FALSE); + rules.put(year, Boolean.FALSE); + + return rules; + } + + @Override + public AgronomicalObject createObjectFromDTO() { + AgronomicalObject agronomicalObject = new AgronomicalObject(); + agronomicalObject.setTypeAgronomicalObject(typeAgronomicalObject); + agronomicalObject.setGeometry(geometry); + agronomicalObject.setUriExperiment(experiment); + + if (properties != null) { + for (Property property : properties) { + agronomicalObject.addProperty(property); + } + } + + return agronomicalObject; + } + + @ApiModelProperty(example = "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))") + public String getGeometry() { + return geometry; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/vocabulary/2017#Plot") + public String getTypeAgronomicalObject() { + return typeAgronomicalObject; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/diaphen/DIA2017-1") + public String getUriExperiment() { + return experiment; + } + + public void setUriExperiment(String uriConcernedItem) { + this.experiment = uriConcernedItem; + } + + public void setTypeAgronomicalObject(String typeAgronomicalObject) { + this.typeAgronomicalObject = typeAgronomicalObject; + } + + public void setGeometry(String geometry) { + this.geometry = geometry; + } + + @ApiModelProperty(example = "2017") + public String getYear() { + return year; + } + + public void setYear(String year) { + this.year = year; + } + + public ArrayList getProperties() { + return properties; + } + + public void setProperties(ArrayList properties) { + this.properties = properties; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/ConcernItemDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/ConcernItemDTO.java new file mode 100644 index 000000000..4ddb98c43 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/ConcernItemDTO.java @@ -0,0 +1,54 @@ +//********************************************************************************************** +// ConcernItemDTO.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: June, 2017 +// Subject: Represents the JSON submitted for the objects concerned by the annotation +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.HashMap; +import java.util.Map; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; + +public class ConcernItemDTO extends AbstractVerifiedClass{ + private String uri; + private String typeURI; + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put("uri", Boolean.TRUE); + rules.put("typeURI", Boolean.TRUE); + return rules; + } + + @Override + public Object createObjectFromDTO() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_EXPERIMENT_URI) + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_DOCUMENT_CONCERNED_TYPE_URI) + public String getTypeURI() { + return typeURI; + } + + public void setTypeURI(String typeURI) { + this.typeURI = typeURI; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/ConfigurationFilesMetadataDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/ConfigurationFilesMetadataDTO.java new file mode 100644 index 000000000..4e6e7e405 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/ConfigurationFilesMetadataDTO.java @@ -0,0 +1,132 @@ +//********************************************************************************************** +// ConfigurationFilesMetadataDTO.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: Represents the submitted JSON for the file metadata +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.HashMap; +import java.util.Map; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; + +@ApiModel +public class ConfigurationFilesMetadataDTO extends AbstractVerifiedClass { + private String provider; + private String clientPath; + private String plateform; + private String fromIP; + private String device; + private String filename; + private String serverFilename; + private String extension; + private String checksum; + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put("provider", Boolean.TRUE); + rules.put("clientPath", Boolean.FALSE); + rules.put("plateform", Boolean.TRUE); + rules.put("fromIP", Boolean.TRUE); + rules.put("device", Boolean.FALSE); + rules.put("filename", Boolean.TRUE); + rules.put("extension", Boolean.TRUE); + rules.put("checksum", Boolean.TRUE); + rules.put("serverFilename", Boolean.FALSE); + return rules; + } + + @ApiModelProperty(example = "test/dzdz/dzdz") + public String getClientPath() { + return clientPath; + } + + public void setClientPath(String clientPath) { + this.clientPath = clientPath; + } + + public String getServerFilename() { + return serverFilename; + } + + public void setServerFilename(String serverFilename) { + this.serverFilename = serverFilename; + } + + @ApiModelProperty(example = "106fa487baa1728083747de1c6df73e9") + public String getChecksum() { + return checksum; + } + + public void setChecksum(String checksum) { + this.checksum = checksum; + } + + + @ApiModelProperty(example = "m3p") + public String getPlateform() { + return plateform; + } + + public void setPlateform(String plateform) { + this.plateform = plateform; + } + @ApiModelProperty(example = "147.99.7.11") + public String getFromIP() { + return fromIP; + } + + public void setFromIP(String fromIP) { + this.fromIP = fromIP; + } + + @ApiModelProperty(example = "Tablet computer") + public String getDevice() { + return device; + } + + public void setDevice(String device) { + this.device = device; + } + + + @ApiModelProperty(example = "mistea") + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + @ApiModelProperty(example = "test numero 50005") + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + @ApiModelProperty(example = "jpg") + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + @Override + public Object createObjectFromDTO() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/DocumentMetadataDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/DocumentMetadataDTO.java new file mode 100644 index 000000000..212d97106 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/DocumentMetadataDTO.java @@ -0,0 +1,162 @@ +//********************************************************************************************** +// DocumentMetadataDTO.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 12 2017 (Ajout du status de documents) +// Subject: Represents the submitted JSON for the documents +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; + +public class DocumentMetadataDTO extends AbstractVerifiedClass { + private String uri; // /!\ ne sera pas utilisé pour le POST de métadonnées + private String documentType; + private String checksum; + private String creator; + private String language; //Il est recommandé que la valeur suive la norme RFC4646 + private String title; + private String creationDate; + private String extension; + private String comment; + private List concern; // Liste des éléments auxquels le doc est lié + private String status; // Status du document (linked / unlinked). Linked quand l'objet auquel il est + // lié existe réellement, unlinked quand il ne l'est pas encore + + private String serverFilePath; // ce champ n'est pas à fournir par le client. + // Sa valeur sera déterminée coté WS + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put("documentType", Boolean.TRUE); + rules.put("checksum", Boolean.FALSE); + rules.put("uri", Boolean.FALSE); + rules.put("creator", Boolean.FALSE); + rules.put("language", Boolean.FALSE); + rules.put("title", Boolean.FALSE); + rules.put("creationDate", Boolean.FALSE); + rules.put("extension", Boolean.FALSE); + rules.put("concern", Boolean.FALSE); + rules.put("comment", Boolean.FALSE); + rules.put("status", Boolean.TRUE); + return rules; + } + + @Override + public Object createObjectFromDTO() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/vocabulary/2015#ScientificDocument") + public String getDocumentType() { + return documentType; + } + + public void setDocumentType(String mediaType) { + this.documentType = mediaType; + } + + @ApiModelProperty(example = "106fa487baa1728083747de1c6df73e9") + public String getChecksum() { + return checksum; + } + + public void setChecksum(String checksum) { + this.checksum = checksum; + } + + @ApiModelProperty(example = "John Doe") + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + @ApiModelProperty(example = "fr") + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + @ApiModelProperty(example = "title") + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @ApiModelProperty(example = "2017-01-01") + public String getCreationDate() { + return creationDate; + } + + public void setCreationDate(String creationDate) { + this.creationDate = creationDate; + } + + public List getConcern() { + return concern; + } + + public void setConcern(List concern) { + this.concern = concern; + } + + @ApiModelProperty(example = "jpg") + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public String getServerFilePath() { + return serverFilePath; + } + + public void setServerFilePath(String serverFilePath) { + this.serverFilePath = serverFilePath; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + @ApiModelProperty(example = "linked") + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/ExperimentDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/ExperimentDTO.java new file mode 100644 index 000000000..b89820c38 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/ExperimentDTO.java @@ -0,0 +1,226 @@ +//********************************************************************************************** +// ExperimentDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: January 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 31 2017 : Passage de trial à experiment +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the client +// to save the database. +//*********************************************************************************************** + +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; +import phis2ws.service.view.model.phis.Contact; +import phis2ws.service.view.model.phis.Group; +import phis2ws.service.view.model.phis.Project; +import phis2ws.service.view.model.phis.Experiment; + +public class ExperimentDTO extends AbstractVerifiedClass { + + final static Logger LOGGER = LoggerFactory.getLogger(ExperimentDTO.class); + + private String uri; + private String startDate; + private String endDate; + private String field; + private String campaign; + private String place; + private String alias; + private String comment; + private String keywords; + private String objective; + private String cropSpecies; + private ArrayList projectsUris; + private ArrayList groupsUris; + private ArrayList contacts; + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put(uri, Boolean.TRUE); + rules.put(startDate, Boolean.TRUE); + rules.put(endDate, Boolean.TRUE); + rules.put(field, Boolean.FALSE); + rules.put(campaign, Boolean.FALSE); + rules.put(place, Boolean.FALSE); + rules.put(alias, Boolean.FALSE); + rules.put(comment, Boolean.FALSE); + rules.put(keywords, Boolean.FALSE); + rules.put(objective, Boolean.FALSE); + rules.put(cropSpecies, Boolean.FALSE); + + return rules; + } + + @Override + public Experiment createObjectFromDTO() { + Experiment experiment = new Experiment(uri); + experiment.setStartDate(startDate); + experiment.setEndDate(endDate); + experiment.setField(field); + experiment.setCampaign(campaign); + experiment.setPlace(place); + experiment.setAlias(alias); + experiment.setComment(comment); + experiment.setKeywords(keywords); + experiment.setObjective(objective); + experiment.setCropSpecies(cropSpecies); + + if (projectsUris != null) { + for (String projectURI : projectsUris) { + Project project = new Project(projectURI); + experiment.addProject(project); + } + } + + if (groupsUris != null) { + for (String groupURI : groupsUris) { + Group group = new Group(groupURI); + experiment.addGroup(group); + } + } + + if (contacts != null && !contacts.isEmpty()) { + for (Contact contact : contacts) { + experiment.addContact(contact); + } + } + + return experiment; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/diaphen/drops") + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + @ApiModelProperty(example = "2015-07-07") + public String getStartDate() { + return startDate; + } + + public void setStartDate(String startDate) { + this.startDate = startDate; + } + + @ApiModelProperty(example = "2015-08-07") + public String getEndDate() { + return endDate; + } + + public void setEndDate(String endDate) { + this.endDate = endDate; + } + + @ApiModelProperty(example = "field") + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + @ApiModelProperty(example = "campaign") + public String getCampaign() { + return campaign; + } + + public void setCampaign(String campaign) { + this.campaign = campaign; + } + + @ApiModelProperty(example = "place") + public String getPlace() { + return place; + } + + public void setPlace(String place) { + this.place = place; + } + + @ApiModelProperty(example = "alias") + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + @ApiModelProperty(example = "comment") + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + @ApiModelProperty(example = "keywords") + public String getKeywords() { + return keywords; + } + + public void setKeywords(String keywords) { + this.keywords = keywords; + } + + @ApiModelProperty(example = "objective") + public String getObjective() { + return objective; + } + + public void setObjective(String objective) { + this.objective = objective; + } + + public ArrayList getProjectsUris() { + return projectsUris; + } + + public void setProjectsUris(ArrayList projectsUris) { + this.projectsUris = projectsUris; + } + + public ArrayList getGroupsUris() { + return groupsUris; + } + + public void setGroupsUris(ArrayList groupsUris) { + this.groupsUris = groupsUris; + } + + @ApiModelProperty(example = "maize") + public String getCropSpecies() { + return cropSpecies; + } + + public void setCropSpecies(String cropSpecies) { + this.cropSpecies = cropSpecies; + } + + public ArrayList getContacts() { + return contacts; + } + + public void setContacts(ArrayList contacts) { + this.contacts = contacts; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/GroupDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/GroupDTO.java new file mode 100644 index 000000000..85a28e714 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/GroupDTO.java @@ -0,0 +1,110 @@ +//********************************************************************************************** +// GroupDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: April, 2017 +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the Client +// to save the database +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.model.User; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; +import phis2ws.service.view.model.phis.Group; + +public class GroupDTO extends AbstractVerifiedClass { + + final static Logger LOGGER = LoggerFactory.getLogger(GroupDTO.class); + + private String uri; + private String name; + private String level; + private String description; + + private ArrayList usersEmails = new ArrayList<>(); + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put(uri, Boolean.TRUE); + rules.put(name, Boolean.TRUE); + rules.put(level, Boolean.TRUE); + rules.put(description, Boolean.TRUE); + + return rules; + } + + @Override + public Group createObjectFromDTO() { + Group group = new Group(uri); + group.setName(name); + group.setLevel(level); + group.setDescription(description); + + if (usersEmails != null) { + for (String userEmail : usersEmails) { + User u = new User(userEmail); + group.addUser(u); + } + } + + return group; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_GROUP_URI) + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_GROUP_NAME) + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ApiModelProperty(example = "Admin") + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + @ApiModelProperty(example = "description of the gamma group") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public ArrayList getUsersEmails() { + return usersEmails; + } + + public void setUsersEmails(ArrayList usersEmails) { + this.usersEmails = usersEmails; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/InstanceDefinitionDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/InstanceDefinitionDTO.java new file mode 100644 index 000000000..4e30643a8 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/InstanceDefinitionDTO.java @@ -0,0 +1,95 @@ +//********************************************************************************************** +// InstanceDefinitonDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 16 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 16 2017 +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the client +// to save the database. +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; +import phis2ws.service.view.model.phis.InstanceDefinition; +import phis2ws.service.view.model.phis.OntologyReference; + +public class InstanceDefinitionDTO extends AbstractVerifiedClass { + //SILEX:info + //Pour l'instant, on ne prend qu'un label et un comment. + //Il faudra pouvoir en avoir plusieurs (ex. labels par langue). + //Il faudra donc modifier les req d'insertion/recherche/suppression/modification + //\SILEX:info + protected String uri; + protected String label; + protected String comment; + protected ArrayList ontologiesReferences = new ArrayList<>(); + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put(label, Boolean.TRUE); + rules.put(uri, Boolean.FALSE); + rules.put(comment, Boolean.FALSE); + + return rules; + } + + @Override + public InstanceDefinition createObjectFromDTO() { + InstanceDefinition instanceDefinition = new InstanceDefinition(); + instanceDefinition.setUri(uri); + instanceDefinition.setLabel(label); + instanceDefinition.setComment(comment); + + if (ontologiesReferences != null && !ontologiesReferences.isEmpty()) { + for (OntologyReference ontologyReference : ontologiesReferences) { + instanceDefinition.addOntologyReference(ontologyReference); + } + } + + return instanceDefinition; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/id/variables/v001") + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + @ApiModelProperty(example = "LAI") + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + @ApiModelProperty(example = "comment") + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public ArrayList getOntologiesReferences() { + return ontologiesReferences; + } + + public void setOntologiesReferences(ArrayList ontologiesReferences) { + this.ontologiesReferences = ontologiesReferences; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/LayerDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/LayerDTO.java new file mode 100644 index 000000000..11ea2f4e6 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/LayerDTO.java @@ -0,0 +1,91 @@ +//********************************************************************************************** +// LayerDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: August, 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: August, 16 2017 +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the client to +// create a geojson file corresponding to a layer +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; + +public class LayerDTO extends AbstractVerifiedClass { + final static Logger LOGGER = LoggerFactory.getLogger(LayerDTO.class); + + /** + * @param objectUri l'uri de l'objet pour lequel on veut une couche + * @param objectType type de l'objet (ex : http://www.phenome-fppn.fr/Vocabulary/2017/Experiment) + * @param depth true si on souhaite avoir tous les déscendants représentés dans + * la couche, + * false si on veut juste l'objet et ses enfants directs + * @param generateFile true si on génère le geojson, false si on ne génère pas le geojson + */ + private String objectUri; + private String objectType; + private String depth; + private String generateFile; + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put(objectUri, Boolean.TRUE); + rules.put(objectType, Boolean.TRUE); + rules.put(depth, Boolean.TRUE); + rules.put(generateFile, Boolean.FALSE); + + return rules; + } + + @Override + public Object createObjectFromDTO() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/diaphen/DIA2017-1") + public String getObjectUri() { + return objectUri; + } + + public void setObjectUri(String objectUri) { + this.objectUri = objectUri; + } + + @ApiModelProperty(example = "true") + public String getDepth() { + return depth; + } + + public void setDepth(String depth) { + this.depth = depth; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/vocabulary/2017#Experiment") + public String getObjectType() { + return objectType; + } + + public void setObjectType(String objectType) { + this.objectType = objectType; + } + + @ ApiModelProperty(example = "true") + public String getGenerateFile() { + return generateFile; + } + + public void setGenerateFile(String generateFile) { + this.generateFile = generateFile; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/LogoutDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/LogoutDTO.java new file mode 100644 index 000000000..b6bd2f0a8 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/LogoutDTO.java @@ -0,0 +1,35 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.HashMap; +import java.util.Map; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; + +/** + * Represente le JSON soumis pour les objets de type token + * + * @author A. CHARLEROY + */ +@ApiModel +public class LogoutDTO extends AbstractVerifiedClass { + + @ApiModelProperty(example = "2107aa78b05410a0dbb8f1d8b2d1b54b") + public String access_token; + + @Override + public Object createObjectFromDTO() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Map rules() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/MethodDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/MethodDTO.java new file mode 100644 index 000000000..fe2a0bead --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/MethodDTO.java @@ -0,0 +1,28 @@ +//********************************************************************************************** +// MethodDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 17 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 17 2017 +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the client +// to save the database +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.view.model.phis.Method; + +public class MethodDTO extends InstanceDefinitionDTO { + final static Logger LOGGER = LoggerFactory.getLogger(TraitDTO.class); + + public Method createObjectFromDTO() { + Method method = (Method) super.createObjectFromDTO(); + return method; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/PhenotypeDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/PhenotypeDTO.java new file mode 100644 index 000000000..292b09bc5 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/PhenotypeDTO.java @@ -0,0 +1,76 @@ +//********************************************************************************************** +// RawDataDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: September 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: September, 13 2017 +// Subject: XX +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; +import phis2ws.service.view.model.phis.Data; +import phis2ws.service.view.model.phis.Phenotype; +import phis2ws.service.view.model.phis.Provenance; + +public class PhenotypeDTO extends AbstractVerifiedClass { + + private String variableUri; + private Provenance provenance; + private ArrayList data; + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put(variableUri, Boolean.TRUE); + + //SILEX:todo + //Trouver une solution pour mettres les rules sur les objets complexes. + //\SILEX:todo + return rules; + } + + @Override + public Phenotype createObjectFromDTO() { + Phenotype phenotypes = new Phenotype(); + phenotypes.setVariableURI(variableUri); + phenotypes.setProvenance(new Provenance(provenance)); + for (Data d : data) { + phenotypes.addData(new Data(d)); + } + + return phenotypes; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/diaphen/id/variable/v00001") + public String getVariableUri() { + return variableUri; + } + + public void setVariableUri(String variableUri) { + this.variableUri = variableUri; + } + + public Provenance getProvenance() { + return provenance; + } + + public void setProvenance(Provenance provenance) { + this.provenance = provenance; + } + + public ArrayList getData() { + return data; + } + + public void setData(ArrayList data) { + this.data = data; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/ProjectDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/ProjectDTO.java new file mode 100644 index 000000000..0452dc1cb --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/ProjectDTO.java @@ -0,0 +1,215 @@ +//********************************************************************************************** +// ProjectDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: May, 2017 +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the Client +// to save the database +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; +import phis2ws.service.view.model.phis.Contact; +import phis2ws.service.view.model.phis.Project; + +public class ProjectDTO extends AbstractVerifiedClass { + + final static Logger logger = LoggerFactory.getLogger(ProjectDTO.class); + + private String uri; + private String name; + private String acronyme; + private String subprojectType; + private String financialSupport; + private String financialName; + private String dateStart; + private String dateEnd; + private String keywords; + private String description; + private String objective; + private String parentProject; + private String website; + private ArrayList contacts; + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put(uri, Boolean.TRUE); + rules.put(name, Boolean.TRUE); + rules.put(acronyme, Boolean.FALSE); + rules.put(subprojectType, Boolean.FALSE); + rules.put(financialSupport, Boolean.FALSE); + rules.put(financialName, Boolean.FALSE); + rules.put(dateStart, Boolean.TRUE); + rules.put(dateEnd, Boolean.FALSE); + rules.put(keywords, Boolean.FALSE); + rules.put(description, Boolean.FALSE); + rules.put(objective, Boolean.FALSE); + rules.put(parentProject, Boolean.FALSE); + rules.put(website, Boolean.FALSE); + + return rules; + } + + @Override + public Project createObjectFromDTO() { + Project project = new Project(uri); + project.setName(name); + project.setAcronyme(acronyme); + project.setSubprojectType(subprojectType); + project.setFinancialSupport(financialSupport); + project.setFinancialName(financialName); + project.setDateStart(dateStart); + project.setDateEnd(dateEnd); + project.setKeywords(keywords); + project.setDescription(description); + project.setObjective(objective); + project.setParentProject(parentProject); + project.setWebsite(website); + + if (contacts != null && !contacts.isEmpty()) { + for (Contact contact : contacts) { + project.addContact(contact); + } + } + + return project; + } + + @ApiModelProperty(example = "http://phenome-fppn.fr/phis_field/projectTest") + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + @ApiModelProperty(example = "projectTest") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ApiModelProperty(example = "P T") + public String getAcronyme() { + return acronyme; + } + + public void setAcronyme(String acronyme) { + this.acronyme = acronyme; + } + + @ApiModelProperty(example = "subproject type") + public String getSubprojectType() { + return subprojectType; + } + + public void setSubprojectType(String subprojectType) { + this.subprojectType = subprojectType; + } + + @ApiModelProperty(example = "financial support") + public String getFinancialSupport() { + return financialSupport; + } + + public void setFinancialSupport(String financialSupport) { + this.financialSupport = financialSupport; + } + + @ApiModelProperty(example = "financial name") + public String getFinancialName() { + return financialName; + } + + public void setFinancialName(String financialName) { + this.financialName = financialName; + } + + @ApiModelProperty(example = "2015-07-07") + public String getDateStart() { + return dateStart; + } + + public void setDateStart(String dateStart) { + this.dateStart = dateStart; + } + + @ApiModelProperty(example = "2016-07-07") + public String getDateEnd() { + return dateEnd; + } + + public void setDateEnd(String dateEnd) { + this.dateEnd = dateEnd; + } + + @ApiModelProperty(example = "keywords") + public String getKeywords() { + return keywords; + } + + public void setKeywords(String keywords) { + this.keywords = keywords; + } + + @ApiModelProperty(example = "description") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @ApiModelProperty(example = "objective") + public String getObjective() { + return objective; + } + + public void setObjective(String objective) { + this.objective = objective; + } + + @ApiModelProperty(example = "parent project") + public String getParentProject() { + return parentProject; + } + + public void setParentProject(String parentProject) { + this.parentProject = parentProject; + } + + @ApiModelProperty(example = "http://example.com") + public String getWebsite() { + return website; + } + + public void setWebsite(String website) { + this.website = website; + } + + public ArrayList getContacts() { + return contacts; + } + + public void setContacts(ArrayList contacts) { + this.contacts = contacts; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/TokenDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/TokenDTO.java new file mode 100644 index 000000000..0d360a9b3 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/TokenDTO.java @@ -0,0 +1,76 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.HashMap; +import java.util.Map; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; + +/** + * Represente le JSON soumis pour les objets de type token + * @author A. CHARLEROY + */ +public class TokenDTO extends AbstractVerifiedClass { + + private String grant_type; + private String username; + private String password; + private String client_id; + + @ApiModelProperty(example = "password") + public String getGrant_type() { + return grant_type; + } + + public void setGrant_type(String grant_type) { + this.grant_type = grant_type; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_EMAIL) + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_PASSWORD) + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + // @ApiModelProperty(example = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJndWVzdHBoaXNAc3VwYWdyby5pbnJhLmZyIiwiaXNzIjoiR25wSVMiLCJpYXQiOjE0NzYyNzY5MzgsImV4cCI6MTc5MTgwOTczOH0.Tm0fqmqSp1tna9g5govzgfPiRLaEu6OC5mvUNf1DKxa1nnAp5N1QUYwtTFdgOcUoWoO9nwj6Fe-4YbKpe7ECuNLqhm3-Pu42YI2359AkEorMxTfg4VUJfUVljgxSIakqrfCHt1qRImTpQegQiPsZque_BXBUxU3I7rScNODfzPs") + public String getClient_id() { + return client_id; + } + + public void setClient_id(String client_id) { + this.client_id = client_id; + } + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put("grant_type", Boolean.FALSE); + rules.put("username", Boolean.TRUE); + rules.put("password", Boolean.FALSE); + rules.put("client_id", Boolean.FALSE); + return rules; + } + + @Override + public Object createObjectFromDTO() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/TraitDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/TraitDTO.java new file mode 100644 index 000000000..e1f5421d3 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/TraitDTO.java @@ -0,0 +1,28 @@ +//********************************************************************************************** +// TraitDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 17 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 17 2017 +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the client +// to save the database +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.view.model.phis.Trait; + +public class TraitDTO extends InstanceDefinitionDTO { + final static Logger LOGGER = LoggerFactory.getLogger(TraitDTO.class); + + public Trait createObjectFromDTO() { + Trait trait = (Trait) super.createObjectFromDTO(); + return trait; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/UnitDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/UnitDTO.java new file mode 100644 index 000000000..4c6463c4d --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/UnitDTO.java @@ -0,0 +1,28 @@ +//********************************************************************************************** +// UnitDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 17 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 17 2017 +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the client +// to save the database +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.view.model.phis.Unit; + +public class UnitDTO extends InstanceDefinitionDTO { + final static Logger LOGGER = LoggerFactory.getLogger(UnitDTO.class); + + public Unit createObjectFromDTO() { + Unit trait = (Unit) super.createObjectFromDTO(); + return trait; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/UserDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/UserDTO.java new file mode 100644 index 000000000..bd5f687e7 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/UserDTO.java @@ -0,0 +1,169 @@ +//********************************************************************************************** +// UserDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: April, 2017 +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the Client +// to save the database +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.documentation.DocumentationAnnotation; +import phis2ws.service.resources.dto.manager.AbstractVerifiedClass; +import phis2ws.service.model.User; +import phis2ws.service.view.model.phis.Group; + +public class UserDTO extends AbstractVerifiedClass { + + final static Logger LOGGER = LoggerFactory.getLogger(UserDTO.class); + + private String email; + private String password; + private String firstName; + private String familyName; + private String address; + private String phone; + private String affiliation; + private String orcid; + private String admin; + private ArrayList groupsUris = new ArrayList<>(); + + @Override + public Map rules() { + Map rules = new HashMap<>(); + rules.put(email, Boolean.TRUE); + rules.put(password, Boolean.FALSE); + rules.put(firstName, Boolean.FALSE); + rules.put(familyName, Boolean.FALSE); + rules.put(address, Boolean.FALSE); + rules.put(phone, Boolean.FALSE); + rules.put(affiliation, Boolean.TRUE); + rules.put(orcid, Boolean.FALSE); + rules.put(admin, Boolean.FALSE); + + return rules; + } + + @Override + public User createObjectFromDTO() { + User user = new User(email); + user.setPassword(password); + user.setFirstName(firstName); + user.setFamilyName(familyName); + user.setAddress(address); + user.setPhone(phone); + user.setAffiliation(affiliation); + user.setOrcid(orcid); + user.setAdmin(admin); + + if (groupsUris != null) { + for (String groupURI : groupsUris) { + Group group = new Group(groupURI); + user.addGroup(group); + } + } + + return user; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_EMAIL) + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_PASSWORD) + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_FIRST_NAME) + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_FAMILY_NAME) + public String getFamilyName() { + return familyName; + } + + public void setFamilyName(String familyName) { + this.familyName = familyName; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_ADDRESS) + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_PHONE) + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_AFFILIATION) + public String getAffiliation() { + return affiliation; + } + + public void setAffiliation(String affiliation) { + this.affiliation = affiliation; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_ORCID) + public String getOrcid() { + return orcid; + } + + public void setOrcid(String orcid) { + this.orcid = orcid; + } + + @ApiModelProperty(example = DocumentationAnnotation.EXAMPLE_USER_ADMIN) + public String getAdmin() { + return admin; + } + + public void setAdmin(String admin) { + this.admin = admin; + } + + public ArrayList getGroupsUris() { + return groupsUris; + } + + public void setGroupsUris(ArrayList groupsUris) { + this.groupsUris = groupsUris; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/VariableDTO.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/VariableDTO.java new file mode 100644 index 000000000..44176a5d7 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/VariableDTO.java @@ -0,0 +1,83 @@ +//********************************************************************************************** +// VariableDTO.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 14 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 14 2017 +// Subject: A class which contains methods to automatically check the attributes +// of a class, from rules defined by user. +// Contains the list of the elements which might be send by the client +// to save the database +//*********************************************************************************************** +package phis2ws.service.resources.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.view.model.phis.Method; +import phis2ws.service.view.model.phis.Trait; +import phis2ws.service.view.model.phis.Unit; +import phis2ws.service.view.model.phis.Variable; + +public class VariableDTO extends InstanceDefinitionDTO { + final static Logger LOGGER = LoggerFactory.getLogger(VariableDTO.class); + + /** + * @param trait l'uri du trait (ex. http://www.phenome-fppn.fr/diaphen/id/trait/t001) + * @param method l'uri de la méthode (ex. http://www.phenome-fppn.fr/diaphen/id/method/m001) + * @param unit l'uri de l'unité (ex. http://www.phenome-fppn.fr/diaphen/id/unit/u001) + */ + private String trait; + private String method; + private String unit; + + @Override + public Map rules() { + Map rules = super.rules(); + rules.put(trait, Boolean.TRUE); + rules.put(method, Boolean.TRUE); + rules.put(unit, Boolean.TRUE); + return rules; + } + + @Override + public Variable createObjectFromDTO() { + Variable variable = (Variable) super.createObjectFromDTO(); + variable.setTrait(new Trait(trait)); + variable.setMethod(new Method(method)); + variable.setUnit(new Unit(unit)); + + return variable; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/diaphen/id/traits/t001") + public String getTrait() { + return trait; + } + + public void setTrait(String trait) { + this.trait = trait; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/diaphen/id/methods/m001") + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + @ApiModelProperty(example = "http://www.phenome-fppn.fr/diaphen/id/units/u001") + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/manager/AbstractVerifiedClass.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/manager/AbstractVerifiedClass.java new file mode 100644 index 000000000..f6d2d1b91 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/manager/AbstractVerifiedClass.java @@ -0,0 +1,98 @@ +//********************************************************************************************** +// AbstractVerifiedClass.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: may 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which contains methods to verify automatically class's attributes from rules defined by user +//*********************************************************************************************** +package phis2ws.service.resources.dto.manager; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** + * Réprésente un classe qui permet de définir des objets qui peuvent vérifier leur attributs + * @author Arnaud CHARLEROY + */ +public abstract class AbstractVerifiedClass implements VerifiedClassInterface { + /** + * Retourne une map contenant l'état dans la clé "state" et les champs manquant de l'objet + * @return Map + */ + @Override + public Map isOk() { + Map ok = new HashMap<>(); + Map rules = this.rules(); + + Field [] attributes = this.getClass().getDeclaredFields(); + + Boolean validationBool = Boolean.TRUE; + + try { + for (Field field : attributes) { + field.setAccessible(true); + Object fieldObject = field.get(this); + if (fieldObject instanceof List) { + List list = (List) ((List) fieldObject); + if(!list.isEmpty() + && Objects.equals(rules.get(field.getName()), Boolean.TRUE)){ + Map verifiedClassInstance = ((AbstractVerifiedClass) list.get(0)).isOk(); + if(verifiedClassInstance.get("state") == Boolean.FALSE){ + validationBool = Boolean.FALSE; + verifiedClassInstance.remove("state"); + ok.put(field.getName(), verifiedClassInstance); + } + } + + } else if (fieldObject instanceof AbstractVerifiedClass) { + Map verifiedClassInstance = ((AbstractVerifiedClass) fieldObject).isOk(); + if (verifiedClassInstance.get("state") == Boolean.FALSE) { + validationBool = Boolean.FALSE; + verifiedClassInstance.remove("state"); + ok.put(field.getName(), verifiedClassInstance); + } + } else { + if (Objects.equals(rules.get(field.getName()), Boolean.TRUE) && + (Objects.equals(fieldObject, null) || (fieldObject instanceof String && fieldObject == "")) ) { + validationBool = Boolean.FALSE; + ok.put(field.getName(), "empty"); + } + } + } + } catch(SecurityException | IllegalArgumentException | IllegalAccessException ex){ + ok.replace("state", Boolean.FALSE); + } + ok.put("state", validationBool); + return ok; + } + + /** + * Permet de transformer un objet en HashMap . + * Fonction pratique pour la transformation en JSON + * @return + */ + public Map toHashMap() { + Map objectHashMap = new LinkedHashMap<>(); + Field [] attributes = this.getClass().getDeclaredFields(); + try{ + for (Field field : attributes) { + field.setAccessible(true); + objectHashMap.put(field.getName(), (String) field.get(this)); + } + }catch(SecurityException | IllegalArgumentException | IllegalAccessException ex){ + objectHashMap.put("convertError", ex.getMessage()); + } + return objectHashMap; + } + + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/dto/manager/VerifiedClassInterface.java b/phis2-ws/src/main/java/phis2ws/service/resources/dto/manager/VerifiedClassInterface.java new file mode 100644 index 000000000..0170916ad --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/dto/manager/VerifiedClassInterface.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.resources.dto.manager; + +import java.util.Map; + + +public interface VerifiedClassInterface { + + public Map isOk(); + + public Map rules(); + + public T createObjectFromDTO(); +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/request/filters/AuthentificationRequestFilter.java b/phis2-ws/src/main/java/phis2ws/service/resources/request/filters/AuthentificationRequestFilter.java new file mode 100644 index 000000000..6304aeb8b --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/request/filters/AuthentificationRequestFilter.java @@ -0,0 +1,86 @@ +package phis2ws.service.resources.request.filters; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.ext.Provider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.authentication.TokenManager; +import phis2ws.service.configuration.GlobalWebserviceValues; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.ResponseFormGET; + +/** + * Permet de filtrer les envois au Web Service grâce au header des requêtes + * ainsi que d'autres paramètres + * + * @author Arnaud CHARLEROY + * @date 05/16 + * @update 10/16 brapi v1 + */ +@Provider +public class AuthentificationRequestFilter implements ContainerRequestFilter { + + final static Logger logger = LoggerFactory.getLogger(AuthentificationRequestFilter.class); + + /** + * Filtre le token de session + * + * @param requestContext + * @throws IOException + */ + @Override + public void filter(ContainerRequestContext requestContext) + throws IOException { + Response accessDenied = Response.status(Response.Status.UNAUTHORIZED) + .entity(new ResponseFormGET( + new Status("You cannot access this resource.", StatusCodeMsg.ERR, + "Invalid token"))) + .type(MediaType.APPLICATION_JSON).build(); + + final UriInfo uriInfo = requestContext.getUriInfo(); + final String resourcePath = uriInfo.getPath(); +// logger.debug(resourcePath); + // Swagger.json and token authorized + if (resourcePath != null && !resourcePath.contains("token") && !resourcePath.contains("swagger.json")) { + //Get request headers + final MultivaluedMap headers = requestContext.getHeaders(); + if (headers != null && !headers.containsKey(GlobalWebserviceValues.AUTHORIZATION_PROPERTY)) { + throw new WebApplicationException(accessDenied); + } + //Fetch authorization header +// logger.debug(headers.toString()); + String authorization = requestContext.getHeaderString(GlobalWebserviceValues.AUTHORIZATION_PROPERTY); +// logger.debug(authorization.toString()); + //If no authorization information present; block access + if (authorization == null || authorization.isEmpty()) { + throw new WebApplicationException(accessDenied); + } + + Pattern authorizationPattern = Pattern.compile(GlobalWebserviceValues.AUTHENTICATION_SCHEME + " .*"); + Matcher m = authorizationPattern.matcher(authorization); + + if (!m.matches()) { + throw new WebApplicationException(accessDenied); + } + + //Get session id + String userToken = authorization.replace("Bearer ", ""); +// logger.debug(authorization); +// logger.debug(userToken); +// logger.debug(Boolean.toString(TokenManager.Instance().checkAuthentification(userToken))); + if (!TokenManager.Instance().checkAuthentification(userToken)) { + throw new WebApplicationException(accessDenied); + } + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/request/filters/CORSResponseFilter.java b/phis2-ws/src/main/java/phis2ws/service/resources/request/filters/CORSResponseFilter.java new file mode 100644 index 000000000..65c58f4cc --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/request/filters/CORSResponseFilter.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.resources.request.filters; + +import java.io.IOException; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.ext.Provider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author A. CHARLEROY + */ +@Provider +public class CORSResponseFilter implements ContainerResponseFilter { + final static Logger logger = LoggerFactory.getLogger(CORSResponseFilter.class); + @Context UriInfo ui; + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + MultivaluedMap headers = responseContext.getHeaders(); + +// headers.add("Access-Control-Allow-Origin", "http://podcastpedia.org"); //allows CORS requests only coming from podcastpedia.org + headers.add("Access-Control-Allow-Origin", "*"); + headers.add("Access-Control-Allow-Headers", + "origin, content-type, accept, authorization"); + headers.add("Access-Control-Allow-Credentials", "true"); + headers.add("Access-Control-Allow-Methods", + "GET, POST, PUT, DELETE, OPTIONS, HEAD"); + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/resources/request/filters/PageSizeMaxRequestFilter.java b/phis2-ws/src/main/java/phis2ws/service/resources/request/filters/PageSizeMaxRequestFilter.java new file mode 100644 index 000000000..e05712b87 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/resources/request/filters/PageSizeMaxRequestFilter.java @@ -0,0 +1,62 @@ +package phis2ws.service.resources.request.filters; + +import java.io.IOException; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.ext.Provider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.documentation.StatusCodeMsg; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.form.ResponseFormGET; + +/** + * Permet de filtrer les envois au Web Service grâce au header des requêtes + * ainsi que d'autres paramètres + * + * @author Arnaud CHARLEROY + * @date 05/16 + * @update 07/16 + */ +@Provider +public class PageSizeMaxRequestFilter implements ContainerRequestFilter { + + final static Logger logger = LoggerFactory.getLogger(PageSizeMaxRequestFilter.class); + public final static int PAGE_SIZE_DEFAULT_LIMIT = 2097152; // 2 MB + + /** + * Filtre pour la taille des pages + * + * @param requestContext + * @throws IOException + */ + @Override + public void filter(ContainerRequestContext requestContext) + throws IOException { + UriBuilder b = requestContext.getUriInfo().getRequestUriBuilder(); + UriInfo uriInfo = requestContext.getUriInfo(); + MultivaluedMap queryParameters = uriInfo.getQueryParameters(); +// b. +// b.replaceQueryParam(name, values) + if (queryParameters.containsKey("pageSize")) { + final int currentPageSize = Integer.valueOf(queryParameters.getFirst("pageSize")); + final Integer contentLengthConfigValue = Integer.valueOf(PropertiesFileManager.getConfigFileProperty("service", "pageSizeMax")); + int pageSizeMaxValue = 0; + if (contentLengthConfigValue != null) { + pageSizeMaxValue = contentLengthConfigValue; + } else { + pageSizeMaxValue = PAGE_SIZE_DEFAULT_LIMIT; + } + if (currentPageSize > pageSizeMaxValue) { + final Status pageSizeError = new Status( "You are trying to retrieve to much result at one time.",StatusCodeMsg.ERR, "The current pageSize limit is : " + pageSizeMaxValue + " items"); + requestContext.abortWith(Response.status(Response.Status.BAD_REQUEST).entity(new ResponseFormGET(pageSizeError)).type(MediaType.APPLICATION_JSON).build()); + } + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/DocumentWaitingCheck.java b/phis2-ws/src/main/java/phis2ws/service/utils/DocumentWaitingCheck.java new file mode 100644 index 000000000..593028938 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/DocumentWaitingCheck.java @@ -0,0 +1,66 @@ +//********************************************************************************************** +// DocumentWaitingCheck.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: may 2016 +// Contact:arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: A class which permit to start a thread for documents sending +//*********************************************************************************************** +package phis2ws.service.utils; + +import java.util.concurrent.Callable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; +import phis2ws.service.resources.DocumentResourceService; + +public class DocumentWaitingCheck implements Callable { + final static Logger logger = LoggerFactory.getLogger(DocumentWaitingCheck.class); + final static String PROPS_FILE_NAME = "service"; + final static int DEFAUT_WAITING_FILE_TIME = 30; + final private String annotationsUri; + + public DocumentWaitingCheck(String annotationsUri) { + this.annotationsUri = annotationsUri; + } + + @Override + public Boolean call() { +// logger.debug("start"); + // Temps d'attente + int waitingFileTime = DEFAUT_WAITING_FILE_TIME; + try{ + waitingFileTime = Integer.valueOf(PropertiesFileManager.getConfigFileProperty(PROPS_FILE_NAME, "waitingFileTime")); + }catch(Exception e){ + logger.info("Can't parse waitingFileTime properties in " + PROPS_FILE_NAME + " properties file. Default value is "+ DEFAUT_WAITING_FILE_TIME +" seconds.", e); + } + + try { + Thread.sleep(waitingFileTime * 1000); + } catch (InterruptedException ex) { + logger.error(ex.getMessage(), ex); + } + // Si l'uri n'est pas présente dans la Map ou si sa valeur est à false +// (aucun envoi de fichier en cours), on la supprime. +// Sinon on ne fait rien + + + boolean find = DocumentResourceService.waitingAnnotFileCheck.containsKey(annotationsUri); + if(find){ + final Boolean running = DocumentResourceService.waitingAnnotFileCheck.get(annotationsUri); + if(!running){ + // Suppression tableau d'attente + DocumentResourceService.waitingAnnotFileCheck.remove(annotationsUri); + //Suppression de l'information + DocumentResourceService.waitingAnnotInformation.remove(annotationsUri); +// logger.debug("Remove : " + annotationsUri); + } +// logger.debug("sending : " + annotationsUri); + } +// logger.debug("thread Stopped : " + annotationsUri); + return null; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/FileUploader.java b/phis2-ws/src/main/java/phis2ws/service/utils/FileUploader.java new file mode 100644 index 000000000..026b59a95 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/FileUploader.java @@ -0,0 +1,172 @@ +//********************************************************************************************** +// FileUploader.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: may 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which permit to send a file to a distant server +//*********************************************************************************************** +package phis2ws.service.utils; + +import java.io.File; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpException; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import phis2ws.service.PropertiesFileManager; + +/** + * Classe qui étends la libraire JSch qui permet de réaliser des appels en SFTP en Java de façon siplifiée. + * @author Arnaud CHARLEROY + */ +public class FileUploader extends JSch{ + final static Logger logger = LoggerFactory.getLogger(FileUploader.class); + private static final String PROPERTY_FILE_NAME = "service"; + private String SFTPHost; + private String SFTPUser; + private String SFTPPass; + private String SFTPWorkingDirectory; + private Session session = null; + private Channel channel = null; + private ChannelSftp channelSftp = null; + + public FileUploader() { + // Paramtères de connexion + SFTPHost = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILE_NAME, "uploadFileServerIP"); + SFTPUser = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILE_NAME, "uploadFileServerUsername"); + SFTPPass = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILE_NAME, "uploadFileServerPassword"); + SFTPWorkingDirectory = PropertiesFileManager.getConfigFileProperty(PROPERTY_FILE_NAME, "uploadFileServerDirectory"); + + try { + // Connection + session = getSession(SFTPUser,SFTPHost); + session.setConfig("StrictHostKeyChecking", "no"); + session.setPassword(SFTPPass); + session.connect(); + channel = session.openChannel("sftp"); + channel.connect(); + channelSftp = (ChannelSftp) channel; + //Changement de dossier + channelSftp.cd(SFTPWorkingDirectory); + + } catch (SftpException ex) { + logger.error(ex.getMessage(), ex); + } catch (JSchException ex) { + logger.error(ex.getMessage(), ex); + } + } + + public boolean fileTransfer(File f, String filename) { + // Envoie d'un fichier au dossier prévu + FileInputStream fStream = null; + try { + fStream = new FileInputStream(f); + channelSftp.put(new FileInputStream(f), filename); + } catch (FileNotFoundException ex) { + logger.error(ex.getMessage(), ex); + return false; + } catch (SftpException | IOException ex) { + logger.error(ex.getMessage(), ex); + return false; + }finally{ + if(fStream != null){ + try { + fStream.close(); + } catch (IOException ex) { + logger.error("Error during file closing", ex); + } + } + } + + return true; + } + /** + * Fermeture des ressources + */ + public void closeConnection(){ + channelSftp.exit(); + channelSftp.disconnect(); + channel.disconnect(); + session.disconnect(); + } + + public String getSFTPHost() { + return SFTPHost; + } + + public void setSFTPHost(String SFTPHost) { + this.SFTPHost = SFTPHost; + } + + public String getSFTPUser() { + return SFTPUser; + } + + public void setSFTPUser(String SFTPUser) { + this.SFTPUser = SFTPUser; + } + + public String getSFTPPass() { + return SFTPPass; + } + + public void setSFTPPass(String SFTPPass) { + this.SFTPPass = SFTPPass; + } + + public String getSFTPWorkingDirectory() { + return SFTPWorkingDirectory; + } + + public void setSFTPWorkingDirectory(String SFTPWorkingDirectory) { + this.SFTPWorkingDirectory = SFTPWorkingDirectory; + } + + public Session getSession() { + return session; + } + + public void setSession(Session session) { + this.session = session; + } + + public Channel getChannel() { + return channel; + } + + public void setChannel(Channel channel) { + this.channel = channel; + } + + public ChannelSftp getChannelSftp() { + return channelSftp; + } + + public void setChannelSftp(ChannelSftp channelSftp) { + this.channelSftp = channelSftp; + } + + +} + +//public static void main(String[] args) { +// FileUploader jsch = new FileUploader(); +// File f = new File("/home/Arnaud CHARLEROY/Documents/1.jpg"); +// boolean fileTransfer = jsch.fileTransfer(f, "tranféréee.jpg"); +// System.err.println(fileTransfer); +// jsch.closeConnection(); +// } +// +//} + diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/JsonConverter.java b/phis2-ws/src/main/java/phis2ws/service/utils/JsonConverter.java new file mode 100644 index 000000000..eb06b5661 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/JsonConverter.java @@ -0,0 +1,86 @@ +package phis2ws.service.utils; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import javax.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JsonConverter - Classe possédant deux méthodes qui permettent la convertion + * d'objets en chaines de caractères au format JSON + * + * @version1.0 + * + * @author Samuël Chérimont + * @date 03/12/2015 + */ +@Singleton +public final class JsonConverter { + + /** + * Récupération des erreurs + */ + final static Logger logger = LoggerFactory.getLogger(JsonConverter.class); + + /** + * ConvertToPrettyJson() - Méthode de classe qui permet de convertir un + * objet quelconque en une chaine de caractère au format JSON qui sera + * lisible par un utilisateur + * + * @param o L'objet à convertir + * + * @return La chaine de caractère au format JSON + * @date 03/12/2015 + */ + public static String ConvertToPrettyJson(Object o) { + final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + final String jsonObject = gson.toJson(o); + return jsonObject; + } + + /** + * ConvertToJson() - Méthode de classe qui permet de onvertir un objet + * quelconque en une chaine de caractère au format JSON + * + * @param o l'objet à convertir + * + * @return La chaine de caractère au format JSON + * @date 03/12/2015 + * @update 06/2016 AC Récupération des exceptions de sérialization + */ + public static String ConvertToJson(Object o) { + final Gson gson = new GsonBuilder().create(); + String jsonObject = null; + try { + jsonObject = gson.toJson(o); + } catch (Exception e) { +// e.printStackTrace(); + logger.error(e.getMessage(), e); + + } + + return jsonObject; + } + + /** + * @author Arnaud CHARLEROY + * @param j Chaîne à convertir + * @param c Classe de l'objet dans lequel la chaîne doit être convertie + * @return Objet du converti du JSON + * + */ + public static Object ConvertFromJson(String j, Class c) { + final Gson gson = new GsonBuilder().create(); + Object jsonObject; + try { + jsonObject = c.newInstance(); + } catch (InstantiationException | IllegalAccessException ex) { + logger.error(ex.getMessage(), ex); + } + jsonObject = gson.fromJson(j, c); + + return jsonObject; + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/POSTResultsReturn.java b/phis2-ws/src/main/java/phis2ws/service/utils/POSTResultsReturn.java new file mode 100644 index 000000000..3c36e5b47 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/POSTResultsReturn.java @@ -0,0 +1,149 @@ +//********************************************************************************************** +// POSTResultsReturn.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: June 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which collect results, error, and http status from an insertion +//*********************************************************************************************** +package phis2ws.service.utils; + +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.core.Response; + +/** + * Représentes les résults d'un envoi en POST + * @author Arnaud CHARLEROY + */ +public class POSTResultsReturn { + private Boolean resultState; // Le résultat de insert et data (bon si les 2 snt ok et faux sinon) + private Boolean insertState; // Si l'insertion a été réussie + private Boolean dataState; //état des données (si elles sont correctes) + private Response.Status httpStatus; //status qui sera renvoyé au client avec l'insertion + public List statusList; //Si je veux rajouter d'autres status + public Boolean AlreadyExist; + public String errorMsg; + public List createdResources = new ArrayList<>(); + + public POSTResultsReturn() { + } + + public POSTResultsReturn(Boolean resultState) { + this.resultState = resultState; + } + + public POSTResultsReturn(Boolean resultState, Response.Status httpStatus, String errorMsg) { + this.resultState = resultState; + this.httpStatus = httpStatus; + this.errorMsg = errorMsg; + } + + public POSTResultsReturn(Boolean resultState, Boolean insertState, Boolean dataState) { + if(resultState){ + if(dataState){ + if(insertState == null){ // Inserted + httpStatus = Response.Status.ACCEPTED; + }else if(insertState){ // Inserted + httpStatus = Response.Status.CREATED; + }else{ // Failed to insert + httpStatus = Response.Status.INTERNAL_SERVER_ERROR; + } + }else{ // Wrong format or missing data + httpStatus = Response.Status.BAD_REQUEST; + } + }else{ + if(!dataState && ( insertState == null || insertState ) ){ + httpStatus = Response.Status.BAD_REQUEST; + }else{ + httpStatus = Response.Status.INTERNAL_SERVER_ERROR; + } + } + this.resultState = resultState; + this.insertState = insertState; + this.dataState = dataState; + } + + + public POSTResultsReturn(Boolean resultState, Boolean insertState, Boolean dataState, Response.Status httpStatus, List statusList, Boolean AlreadyExist, String errorMsg) { + this.resultState = resultState; + this.insertState = insertState; + this.dataState = dataState; + this.httpStatus = httpStatus; + this.statusList = statusList; + this.AlreadyExist = AlreadyExist; + this.errorMsg = errorMsg; + } + + public Boolean getResultState() { + return resultState; + } + + public void setResultState(Boolean resultState) { + + this.resultState = resultState; + } + + public Boolean getInsertState() { + return insertState; + } + + public void setInsertState(Boolean insertState) { + this.insertState = insertState; + } + + public Boolean getDataState() { + return dataState; + } + + public void setDataState(Boolean dataState) { + this.dataState = dataState; + } + + public Response.Status getHttpStatus() { + return httpStatus; + } + + public void setHttpStatus(Response.Status httpStatus) { + this.httpStatus = httpStatus; + } + + public List getStatusList() { + return statusList; + } + + public void setStatusList(List statusList) { + this.statusList = statusList; + } + + public Boolean getAlreadyExist() { + return AlreadyExist; + } + + public void setAlreadyExist(Boolean AlreadyExist) { + this.AlreadyExist = AlreadyExist; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public List getCreatedResources() { + return createdResources; + } + + public void setCreatedResources(List createdResources) { + this.createdResources = createdResources; + } + + public void addCreatedResource(String createdResource) { + this.createdResources.add(createdResource); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/ResourcesUtils.java b/phis2-ws/src/main/java/phis2ws/service/utils/ResourcesUtils.java new file mode 100644 index 000000000..08c136b54 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/ResourcesUtils.java @@ -0,0 +1,434 @@ +//********************************************************************************************** +// ResourcesUtils.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: may 2016 +// Contact:arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: May 31, 2017 +// Subject: A class which regroup all function which are not specific or can be usable in all the webservice +//*********************************************************************************************** +package phis2ws.service.utils; + +import java.sql.Timestamp; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.joda.time.format.DateTimeParser; +import phis2ws.service.configuration.DateFormats; +import phis2ws.service.configuration.GlobalWebserviceValues; + +/** + * List of functions which can be used in ressources + * + * @author Arnaud CHARLEROY + */ +public class ResourcesUtils { + + /** + * Permet de transformer un flux en chaine de caractère + * + * @param is + * @return + */ +// public static String convertStreamToString(java.io.InputStream is) { +// java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); +// return s.hasNext() ? s.next() : ""; +// } + /** + * + * @param original + * @return + */ + public static String capitalizeFirstLetter(String original) { + return (original == null || original.length() == 0) ? original : original.substring(0, 1).toUpperCase() + original.substring(1).toLowerCase(); + } + + public static List convertStringValuesSepared(String values, String pattern) { + List listValues; + try { + listValues = Arrays.asList(values.split(pattern)); + return listValues; + } catch (Exception e) { + listValues = new ArrayList<>(); + listValues.add(values); + + } + return null; + } + + /** + * extentY to plantHeight + * width to plantWidth + * @param mongoUserVariable + * @return + */ + public static String formatMongoImageAnalysisVariableForDB(String mongoUserVariable) { + String tmpMongoUserVariable = null; + String variableFormatMongo = ""; + // elcom special case + if (mongoUserVariable.contains("parallelBoudingBox")) { + tmpMongoUserVariable = mongoUserVariable.replaceFirst("parallelBoudingBox", ""); + variableFormatMongo = "parallelBoudingBox_" + tmpMongoUserVariable.toLowerCase(); + } else if (mongoUserVariable.contains("nonParallelBoudingBox")) { + tmpMongoUserVariable = mongoUserVariable.replaceFirst("nonParallelBoudingBox", ""); + variableFormatMongo = "nonParallelBoudingBox" + tmpMongoUserVariable.toLowerCase(); + } else { + for (int i = 0; i < mongoUserVariable.length(); i++) { + if (Character.isUpperCase(mongoUserVariable.charAt(i))) { + variableFormatMongo += "_"; + variableFormatMongo += Character.toLowerCase(mongoUserVariable.charAt(i)); + } else { + variableFormatMongo += mongoUserVariable.charAt(i); + } + } + } + return variableFormatMongo; + } + + /** + * Permet de récupérer des parsers présents dans un tableau et de retourner + * un ojet qui parse des dates. + * + * @return DateTimeParser + */ + private static DateTimeParser[] getParsers() { + DateTimeParser[] parsers = new DateTimeParser[DateFormats.AUTHORIZED_DATE_FORMATS.size()]; + int count = 0; + for (String format : DateFormats.AUTHORIZED_DATE_FORMATS) { + parsers[count] = DateTimeFormat.forPattern(format).getParser(); + count++; + } + return parsers; + } + + private static DateTimeParser[] getMongoParsers() { + DateTimeParser[] parsers = new DateTimeParser[DateFormats.DATETIME_MONGO_FORMAT.size()]; + int count = 0; + for (String format : DateFormats.DATETIME_MONGO_FORMAT) { + parsers[count] = DateTimeFormat.forPattern(format).getParser(); + count++; + } + return parsers; + } + + private static DateTimeParser[] getUserEnvironmentParsers() { + DateTimeParser[] parsers = new DateTimeParser[DateFormats.AUTHORIZED_USER_METEO_DATE_FORMATS.size()]; + int count = 0; + for (String format : DateFormats.AUTHORIZED_USER_METEO_DATE_FORMATS) { + parsers[count] = DateTimeFormat.forPattern(format).getParser(); + count++; + } + return parsers; + } + + private static DateTimeParser[] getUserTripleStoreParsers() { + DateTimeParser[] parsers = new DateTimeParser[DateFormats.AUTHORIZED_USER_SPARQL_DATE_FORMATS.size()]; + int count = 0; + for (String format : DateFormats.AUTHORIZED_USER_SPARQL_DATE_FORMATS) { + parsers[count] = DateTimeFormat.forPattern(format).getParser(); + count++; + } + return parsers; + } + + private static DateTimeParser[] getUserMongoParsers() { + DateTimeParser[] parsers = new DateTimeParser[DateFormats.AUTHORIZED_USER_MONGO_DATE_FORMATS.size()]; + int count = 0; + for (String format : DateFormats.AUTHORIZED_USER_MONGO_DATE_FORMATS) { + parsers[count] = DateTimeFormat.forPattern(format).getParser(); + count++; + } + return parsers; + } + + /** + * Vérifie si une date est valide et renvoie un booléen. + * + * @param date + * @return boolean + */ + public static boolean isDateIsValid(String date) { + + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getParsers()).toFormatter(); + try { + DateTime d = formatter.parseDateTime(date); + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + return true; + } + + public static boolean isDateIsValidYMD(String date) { + DateTimeParser parser = DateTimeFormat.forPattern("yyyy-MM-dd").getParser(); + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(parser).toFormatter(); + try { + DateTime d = formatter.parseDateTime(date); + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + return true; + } + + public static boolean isDateIsValidEnvironmentStartEndDate(String date) { + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getUserEnvironmentParsers()).toFormatter(); + try { + DateTime d = formatter.parseDateTime(date); + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + return true; + } + + public static boolean isDateIsValidTripleStoreUserDate(String date) { + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getUserTripleStoreParsers()).toFormatter(); + try { + DateTime d = formatter.parseDateTime(date); + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + return true; + } + + public static boolean isDateIsMongoUserDate(String date) { + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getUserMongoParsers()).toFormatter(); + try { + DateTime d = formatter.parseDateTime(date); + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + return true; + } + + public static String getValueOfAnURI(String uri) { + final String[] parts = uri.split("#"); + if ((uri.contains(">") && uri.contains("<"))) { + return parts[1].substring(0, parts[1].length() - 1); + } else { + return parts[1]; + } + } + + /** + * Transforme une chaîne de caractère en date + * + * @param date + * @return Date + */ + public static Date convertStringToJavaDate(String date) { + DateTime d = null; + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getParsers()).toFormatter(); + try { + d = formatter.parseDateTime(date); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + return d.toDate(); + } + + /** + * Transforme une chaîne de caractère en DateTime avec des parsers par + * défaut(librairie Joda) + * + * @see GlobalWebserviceValues + * @param date + * @return Date + */ + public static DateTime convertStringToJodaDateTime(String date) { + DateTime d = null; + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getParsers()).toFormatter(); + try { + d = formatter.parseDateTime(date); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + return d; + } + + /** + * Transforme une chaîne de caractère en DateTime avec des parsers par + * défaut(librairie Joda) + * + * @see GlobalWebserviceValues + * @param date + * @return Date + */ + public static DateTime convertStringToMongoJodaDateTime(String date) { + DateTime d = null; + final DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getMongoParsers()).toFormatter(); + try { + d = formatter.parseDateTime(date); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + return d; + } + + /** + * Vérifie la validité d'un timestamp + * + * @param inputString + * @return + */ +// public static Boolean isTimeStampValid(String inputString) +// { +// SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ssX"); +// try{ +// Date date = format.parse(inputString); +// GregorianCalendar cal = new GregorianCalendar(); +// cal.setTime(date); +// DateTime now = new DateTime(); +// return 1900 < cal.get(Calendar.YEAR) && cal.get(Calendar.YEAR) < 2500; +// +// } +// catch(ParseException e) +// { +// +// return false; +// } +// } + /** + * Converti un timestamp en date avec l'heure GMT Zone Paris + * + * @param ts + * @return + */ + public static DateTime convertPostgresSqlTimestampToDatetime(Timestamp ts) { + DateTime dt = new DateTime(ts, DateTimeZone.forID("Europe/paris")); + return dt; + } + + /** + * Transforme une chaîne de caractère en timestamp + * + * @param stringDate + * @return + * @throws ParseException + */ + public static Timestamp convertStringToTimeStamp(String stringDate) throws ParseException { +// final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ssX"; +// final SimpleDateFormat sdf = new SimpleDateFormat(TIME_FORMAT); +// Date utilDate = sdf.parse(stringDate); +// return new Timestamp(utilDate.getTime()); + final org.joda.time.format.DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ssZ"); + DateTime dt = formatter.parseDateTime(stringDate); + + return new Timestamp(dt.getMillis()); + + } + + /** + * Génère une chaine unique de 32 caractères + * + * @return + */ + public static String getUniqueID() { + return UUID.randomUUID().toString().replace("-", ""); + } + + /** + * Transforme une chaîne de caractère en DateTime avec un format entrée en + * paramètre (librairie Joda) + * + * @param stringDate + * @param pattern + * @return + */ + public static DateTime convertStringToDateTime(String stringDate, String pattern) { + final org.joda.time.format.DateTimeFormatter formatter = DateTimeFormat.forPattern(pattern); + try { + return formatter.withOffsetParsed().parseDateTime(stringDate); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + } + + public static DateTime convertUserEnvironementDateStringToDateTime(String stringDate) { + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getUserEnvironmentParsers()).toFormatter(); + try { + return formatter.withOffsetParsed().parseDateTime(stringDate); + } catch (Exception e) { + return null; + } + + } + + public static DateTime convertUserTripleStoreDateStringToDateTime(String stringDate) { + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getUserTripleStoreParsers()).toFormatter(); + try { + return formatter.withOffsetParsed().parseDateTime(stringDate); + } catch (Exception e) { + return null; + } + + } + + public static DateTime convertUserMongoDateStringToDateTime(String stringDate) { + DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, getUserMongoParsers()).toFormatter(); + try { + return formatter.withOffsetParsed().parseDateTime(stringDate); + } catch (Exception e) { + return null; + } + + } + +// /** +// * Thread safe +// * @see http://stackoverflow.com/questions/7346508/datatypefactory-usage-in-creating-xmlgregoriancalendar-hits-performance-badly +// */ +// final private static ThreadLocal datatypeFactoryHolder = new ThreadLocal() +// { +// @Override +// protected DatatypeFactory initialValue() +// { +// try +// { +// return DatatypeFactory.newInstance(); +// } catch (DatatypeConfigurationException e) +// { +// throw new IllegalStateException("failed to create " + DatatypeFactory.class.getSimpleName(), e); +// } +// } +// }; +// /** +// * Transforme un objet date pour qu'il soit utilisable par l'api sesame +// * @param date +// * @return +// * @see http://stackoverflow.com/questions/7346508/datatypefactory-usage-in-creating-xmlgregoriancalendar-hits-performance-badly +// */ +// public static XMLGregorianCalendar dateToXMLGregorianCalendar(Date date) +// { +// GregorianCalendar c = new GregorianCalendar(); +// c.setTime(date); +// return datatypeFactoryHolder.get().newXMLGregorianCalendar(c); +// } + + /** + * + * @param bool String + * @return la valeur booléenne du string (true pour t ou true, false pour f ou false) + */ + public static boolean getStringBooleanValue(String bool) { + return (bool.equalsIgnoreCase("true") || bool.equalsIgnoreCase("t")); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/sparql/SPARQLQueryBuilder.java b/phis2-ws/src/main/java/phis2ws/service/utils/sparql/SPARQLQueryBuilder.java new file mode 100644 index 000000000..293c776a8 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/sparql/SPARQLQueryBuilder.java @@ -0,0 +1,146 @@ +//********************************************************************************************** +// SPARQLQueryBuilder.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: may 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which permit to build a SPARQL query +//*********************************************************************************************** +package phis2ws.service.utils.sparql; + +/** + * Classe concrète qui permet de créer des requêtes de recherche en SPARQL + * + * @author Arnaud CHARLEROY + */ +public class SPARQLQueryBuilder extends SPARQLStringBuilder { + + private String where = null; + private Boolean count = null; + private Boolean distinct = null; + private String orderBy = null; + private Integer limit = null; + private Integer offset = null; + private String graph = null; + private String optional = null; + + public SPARQLQueryBuilder() { + super(); + } + + public void appendFrom(String from) { + this.from = from; + } + + public void appendOrderBy(String orderBy) { + this.orderBy = orderBy; + } + + public void appendLimit(Integer limit) { + this.limit = limit; + } + + public void appendOffset(Integer offset) { + this.offset = offset; + } + + public void appendCount(Boolean count) { + this.count = count; + } + + public void appendDistinct(Boolean distinct) { + this.distinct = distinct; + } + + public void appendGraph(String graph) { + this.graph = graph; + } + + public void appendOptional(String optional) { + this.optional = optional; + } + + /** + * Ajout du sélect + * + * @param values + */ + public void appendSelect(String values) { + if (values != null) { + this.select += values; + } + } + + @Override + public String toString() { + String queryResource = ""; + if (prefix != null) { + queryResource += prefix + "\n"; + } + if (count != null && count) { + if (select.length() == 0) { + queryResource += "SELECT ( COUNT ( DISTINCT * ) as ?count) "; + } else { + queryResource += "SELECT ( COUNT ( DISTINCT " + select + " ) as ?count) "; + } + } else if (this.distinct != null) { + if (select.length() == 0) { + queryResource += "SELECT DISTINCT * "; + } else { + queryResource += "SELECT DISTINCT " + select + " "; + } + } else if (select.length() == 0) { + queryResource += "SELECT * "; + } else { + queryResource += "SELECT " + select + " "; + } + if (from != null) { + queryResource += "FROM " + from + "\n"; + } + queryResource += "WHERE {\n"; + if (graph != null) { + queryResource += "GRAPH <" + graph + "> {"; + } + queryResource += body; + if (optional != null) { + queryResource += "\nOPTIONAL {" + optional + " } \n"; + } + if (!filter.isEmpty()) { + queryResource += "\nFILTER ( " + filter + " ) \n"; + } + if (graph != null) { + queryResource += "}"; + } + queryResource += "}"; + if (parameters.length() > 0) { + queryResource += "\n" + parameters; + } + + if (orderBy != null) { + queryResource += "\n" + "ORDER BY " + orderBy + " "; + } + if (limit != null) { + queryResource += "\n" + "LIMIT " + limit + " "; + } + if (offset != null) { + queryResource += "\n" + "OFFSET " + offset + " "; + } + return queryResource; + } + + @Override + public void clearBuilder() { + super.clearBuilder(); + count = null; + distinct = null; + limit = null; + offset = null; + orderBy = null; + where = null; + filter = ""; + optional = null; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/sparql/SPARQLStringBuilder.java b/phis2-ws/src/main/java/phis2ws/service/utils/sparql/SPARQLStringBuilder.java new file mode 100644 index 000000000..9c612b65a --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/sparql/SPARQLStringBuilder.java @@ -0,0 +1,293 @@ +//********************************************************************************************** +// SPARQLStringBuilder.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: may 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which provide method to manipulate SPARQL +//*********************************************************************************************** +package phis2ws.service.utils.sparql; + +import java.util.Iterator; +import java.util.List; + +/** + * Classe abstraite qui permet la création de constructeur qui facilite + * l'écriture de requête SPARQL à partir de chaîne de caractères + * + * @author Arnaud CHARLEROY + */ +public abstract class SPARQLStringBuilder { + + protected String prefix; + protected String select; + protected String from = null; + protected String body; // corps de la requête + protected String parameters; // textes additionnels + protected int blankNodeCounter = 0; + + protected String filter = ""; + Boolean sesameUriFormSubject = null; + Boolean sesameUriFormPredicat = null; + Boolean sesameUriFormObject = null; + + public SPARQLStringBuilder() { + prefix =""; + select = ""; + body = ""; + parameters = ""; + + } + + public void appendToBody(String queryPart) { + this.body += queryPart; + } + + /** + * Ajouts des préfix en amont + * + * @param localName + * @param prefixUri + */ + public void appendPrefix(String localName, String prefixUri) { + if (this.prefix.length() > 0) { + this.prefix += "\n" ; + } + this.prefix += "PREFIX " + localName + ":<" + prefixUri + "#>"; + } + + /** + * Paramètres supplémentaires (Exemple LIMIT) + * + * @param parameters + */ + public void appendParameters(String parameters) { + if (this.parameters != null && this.parameters.length() > 0) { + this.parameters += "\n"; + } + this.parameters += " " + parameters + " "; + } + + public void beginBodyOptional() { + this.body += "\n"+"OPTIONAL {"+"\n"; + } + + public void endBodyOptional() { + this.body += "\n"+"}"; + } + + public void forceSubjectUriForm() { + sesameUriFormSubject = true; + } + + public void normalSubjectUriForm() { + sesameUriFormSubject = null; + } + + public void stringifySubjectForm() { + sesameUriFormSubject = false; + } + + public void forcePredicatUriForm() { + sesameUriFormPredicat = true; + } + + public void normalPredicatUriForm() { + sesameUriFormPredicat = null; + } + + public void stringifyPredicatForm() { + sesameUriFormPredicat = false; + } + + public void forceObjectUriForm() { + sesameUriFormObject = true; + } + + public void normalObjectUriForm() { + sesameUriFormObject = null; + } + + public void stringifyObjectForm() { + sesameUriFormObject = false; + } + + public void appendFilter(String filter) { + this.filter += filter; + } + + /** + * Ajout du corps de la requête possibilité de mettre des semi colon + * + * @param subject + * @param predicate + * @param object + * @param semicolon + */ + public void appendTriplet(String subject, String predicate, String object, Boolean semicolon) { + if (body != null && body.length() > 0) { + body += "\n"; + } + + if (subject == null) { + body += " ?" + blankNodeCounter + " "; + blankNodeCounter++; + } else if (sesameUriFormSubject == null) { + if (subject.contains(":") && !subject.contains("http://")) { + body += " " + subject + " "; + } else if (subject.contains("\"")) { + body += " " + subject + " "; + } else if ((subject.contains("<") && subject.contains(">")) || subject.contains("?")) { + body += " " + subject + " "; + } else { + body += " <" + subject + "> "; + } + } else if (sesameUriFormSubject) { + body += " <" + subject + "> "; + } else { + body += " \"" + subject + "\" "; + } + + if (predicate == null) { + body += " ?" + blankNodeCounter + " "; + blankNodeCounter++; + } else if (sesameUriFormPredicat == null) { + if (predicate.contains(":") && !predicate.contains("http://")) { + body += " " + predicate + " "; + } else if (predicate.contains("\"")) { + body += " " + predicate + " "; + } else if ((predicate.contains(">") && predicate.contains("<")) || predicate.contains("?")) { + body += " " + predicate + " "; + } else { + body += " <" + predicate + "> "; + } + } else if (sesameUriFormPredicat) { + body += " <" + predicate + "> "; + } else { + body += " \"" + predicate + "\" "; + } + + if (object == null) { + body += " ?" + blankNodeCounter + " "; + blankNodeCounter++; + } else if (sesameUriFormObject == null) { + if (object.contains(":") && !object.contains("http://")) { + body += " " + object + " "; + } else if (object.contains("\"")) { + body += " " + object + " "; + } else if ((object.contains(">") && object.contains("<")) || object.contains("?")) { + body += " " + object + " "; + } else { + body += " <" + object + ">"; + } + } else if (sesameUriFormObject) { + body += " <" + object + "> "; + } else { + body += " \"" + object + "\" "; + } + + if (semicolon != null && semicolon) { + body += " ;"; + } else { + body += " . "; + } + } + + public void appendSimpleUnion(String subject, String predicate, List unions) { + + Iterator iteratorStringUnion = unions.iterator(); + while (iteratorStringUnion.hasNext()) { + String union = iteratorStringUnion.next(); + + if (body != null && body.length() > 0) { + body += "\n"; + } + if (unions.size() > 1) { + body += " { "; + } + + if (subject == null) { + body += " ?" + blankNodeCounter + " "; + blankNodeCounter++; + } else if (sesameUriFormSubject == null) { + if (subject.contains(":") && !subject.contains("http://")) { + body += " " + subject + " "; + } else if (subject.contains("\"")) { + body += " " + subject + " "; + } else if ((subject.contains("<") && subject.contains(">")) || subject.contains("?")) { + body += " " + subject + " "; + } else { + body += " <" + subject + "> "; + } + } else if (sesameUriFormSubject) { + body += " <" + subject + "> "; + } else { + body += " \"" + subject + "\" "; + } + + if (predicate == null) { + body += " ?" + blankNodeCounter + " "; + blankNodeCounter++; + } else if (sesameUriFormPredicat == null) { + if (predicate.contains(":") && !predicate.contains("http://")) { + body += " " + predicate + " "; + } else if (predicate.contains("\"")) { + body += " " + predicate + " "; + } else if ((predicate.contains(">") && predicate.contains("<")) || predicate.contains("?")) { + body += " " + predicate + " "; + } else { + body += " <" + predicate + "> "; + } + } else if (sesameUriFormPredicat) { + body += " <" + predicate + "> "; + } else { + body += " \"" + predicate + "\" "; + } + + if (union == null) { + body += " ?" + blankNodeCounter + " "; + blankNodeCounter++; + } else if (sesameUriFormObject == null) { + if (union.contains(":") && !union.contains("http://")) { + body += " " + union + " "; + } else if (union.contains("\"")) { + body += " " + union + " "; + } else if ((union.contains(">") && union.contains("<")) || union.contains("?")) { + body += " " + union + " "; + } else { + body += " <" + union + ">"; + } + } else if (sesameUriFormObject) { + body += " <" + union + "> "; + } else { + body += " \"" + union + "\" "; + } + if (unions.size() > 1) { + body += " }"; + if (iteratorStringUnion.hasNext()) { + body += " UNION "; + } + }else if(unions.size() == 1){ + body += " . "; + } + } + } + + /** + * + */ + protected void clearBuilder() { + blankNodeCounter = 0; + body = ""; + from = ""; + parameters = ""; + prefix = ""; + select = ""; + sesameUriFormObject = null; + sesameUriFormPredicat = null; + sesameUriFormSubject = null; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/sparql/SPARQLUpdateBuilder.java b/phis2-ws/src/main/java/phis2ws/service/utils/sparql/SPARQLUpdateBuilder.java new file mode 100644 index 000000000..ad06b7e87 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/sparql/SPARQLUpdateBuilder.java @@ -0,0 +1,122 @@ +//********************************************************************************************** +// SPARQLUpdateBuilder.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: may 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which permit to build SPARQL update query +//*********************************************************************************************** +package phis2ws.service.utils.sparql; + +/** + * Classe concrète qui permet de créer des requêtes d'insertion en SPARQL + * + * @author Arnaud CHARLEROY + */ +public class SPARQLUpdateBuilder extends SPARQLStringBuilder { + + private String whereBody; + private String add; + private String graph; + + public SPARQLUpdateBuilder() { + super(); + graph = ""; + whereBody = ""; + add = ""; + } + + public void appendGraphURI(String graph) { + if (graph != null) { + if (graph.contains("<") && graph.contains(">")) { + this.graph += "GRAPH " + graph + " { "; + } + this.graph += "GRAPH <" + graph + "> { "; + } + } + + + public void appendUpdateWhereTriplet(String subject, String predicate, String object, Boolean semicolon) { + if (whereBody.length() > 0) { + whereBody += "\n"; + } + if (subject == null) { + whereBody += " ?" + blankNodeCounter + " "; + blankNodeCounter++; + } else if (subject.contains(":") && !subject.contains("http://")) { + whereBody += " " + subject + " "; + }else if (subject.contains("\"")) { + whereBody += " " + subject + " "; + }else if ((subject.contains("<") && subject.contains(">")) || subject.contains("?")) { + whereBody += " " + subject + " "; + }else{ + whereBody += " <" + subject + "> "; + } + + if (predicate == null) { + whereBody += " ?" + blankNodeCounter + " "; + blankNodeCounter++; + } else if (predicate.contains(":") && !predicate.contains("http://")) { + whereBody += " " + predicate + " "; + }else if (predicate.contains("\"")) { + whereBody += " " + predicate + " "; + }else if ((predicate.contains(">") && predicate.contains("<")) || predicate.contains("?")) { + whereBody += " " + predicate + " "; + }else{ + whereBody += " <" + predicate + "> "; + } + + if (object == null) { + whereBody += " ?" + blankNodeCounter + " "; + blankNodeCounter++; + } else if (object.contains(":") && !object.contains("http://")) { + whereBody += " " + object + " "; + }else if (object.contains("\"")) { + whereBody += " " + object + " "; + }else if ((object.contains(">") && object.contains("<")) || object.contains("?")) { + whereBody += " " + object + " "; + }else{ + whereBody += " <" + object + ">"; + } + if (semicolon != null && semicolon) { + whereBody += " ;"; + }else{ + whereBody += " ."; + } + } + + @Override + public String toString() { + String addResource = ""; + if (prefix != null) { + addResource += prefix + "\n"; + } + addResource += "INSERT DATA {" + "\n"; + if (graph.length() > 0) { + addResource += graph + "\n"; + } + addResource += body + "\n"; + if (whereBody.length() > 0) { + addResource += " WHERE {"; + addResource += whereBody; + addResource += "}"; + } + if (graph.length() > 0) { + addResource += "}"; + } + addResource += "}"; + return addResource; + } + + @Override + public void clearBuilder() { + super.clearBuilder(); + add = ""; + graph = ""; + whereBody = ""; + + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/sql/JoinAttributes.java b/phis2-ws/src/main/java/phis2ws/service/utils/sql/JoinAttributes.java new file mode 100644 index 000000000..f0627a10b --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/sql/JoinAttributes.java @@ -0,0 +1,31 @@ +//********************************************************************************************** +// JoinAttributes.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: october 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which list usables JOIN in SQL +//*********************************************************************************************** +package phis2ws.service.utils.sql; + +import javax.inject.Singleton; + +/** + * Liste des jointures + * @author A. CHARLEROY + */ +@Singleton +public final class JoinAttributes { + public static String LEFTJOIN = "LEFT JOIN"; + public static String RIGHTJOIN = "RIGHT JOIN"; + public static String INNERJOIN = "INNER JOIN "; + public static String FULLJOIN = "FULL JOIN"; + public static String NATURALJOIN = "NATURAL JOIN"; + + public JoinAttributes() { + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/utils/sql/SQLQueryBuilder.java b/phis2-ws/src/main/java/phis2ws/service/utils/sql/SQLQueryBuilder.java new file mode 100644 index 000000000..41f5c8e6d --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/utils/sql/SQLQueryBuilder.java @@ -0,0 +1,356 @@ +//********************************************************************************************** +// SQLQueryBuilder.java +// +// Author(s): Arnaud CHARLEROY, Morgane Vidal +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: may 2016 +// Contact:arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: February, 2017 +// Subject: A class which permit to build a SQL query +//*********************************************************************************************** +package phis2ws.service.utils.sql; + +import java.util.Iterator; +import java.util.List; + +/** + * Classe concrète qui permet de créer des requêtes de recherche en SQL + * + * @author Arnaud CHARLEROY, Morgane Vidal + */ +public class SQLQueryBuilder { + + private boolean count = false; + private boolean distinct = false; + private String attributes = null; + public String from; + public String where; + public String join; + public String orderBy; + public String limit; +// public static JoinAttributes JoinAttributes = new JoinAttributes(; + + public SQLQueryBuilder() { + where = ""; + from = ""; + join = ""; + orderBy = ""; + limit = ""; + } + +// public JoinAttributes getJoinAttributes(){ +// return JoinAttributes; +// } + public void appendOrderBy(String valuesList, String order) { + if (valuesList != null) { + orderBy += "\n" + valuesList + "\n"; + if (order != null) { + orderBy += " " + order + " "; + } + } + } + + public void appendCount() { + count = true; + } + + public void appendDistinct() { + distinct = true; + } + + public void removeDistinct() { + distinct = true; + } + + public void removeCount() { + count = false; + } + + public void addAND() { + where += " AND "; + } + + public void addOR() { + where += " OR "; + } + + /** + * Ajout du sélect + * + * @param values + */ + public void appendSelect(String values) { + if (values != null) { + attributes = values; + } + } + + public void addISNULL(String attribute) { + if (where.length() > 0) { + this.where += " AND "; + } + where += attribute + " IS NULL "; + } + + public void appendJoin(String joinName, String table, String tableAlias, String constraint) { + if (table != null && constraint != null && joinName != null) { + join += "\n"; + if (tableAlias != null) { + join += joinName + " \"" + table + "\" AS " + tableAlias; + } else { + join += joinName + " \"" + table + "\""; + } + if (!joinName.equals(JoinAttributes.NATURALJOIN)) { + join += " ON " + constraint; + } + } + } + + /** + * Ajout du from + * + * @param table + * @param alias + */ + public void appendFrom(String table, String alias) { + if (table != null) { + if (from.length() > 0) { + from += ", \"" + table + "\" "; + } else { + from += " FROM \"" + table + "\" "; + } + if (alias != null) { + from += "AS " + alias + " "; + } + } + } + +public void appendORWhereConditions(String attribute, String value, String operator, String type, String tableAlias) { + if (attribute != null && value != null) { + if (where.length() > 0) { + this.where += " OR "; + } + if (tableAlias != null) { + this.where += tableAlias + "." + "\"" + attribute + "\""; + } else { + this.where += "\"" + attribute + "\""; + } + + if (operator != null) { + this.where += " " + operator + " "; + } else { + this.where += " = "; + } + this.where += "'" + value + "'"; + if (type != null) { + this.where += type; + } + } + } + + public void appendINConditions(String attribute, List valuesGroup, String tableAlias) { + if (valuesGroup != null) { + if (where.length() > 0) { + this.where += " AND "; + } + if (valuesGroup.size() > 1) { + if (tableAlias != null) { + this.where += tableAlias + "." + "\"" + attribute + "\" IN ("; + } else { + this.where += "\"" + attribute + "\" IN ("; + } + Iterator it = valuesGroup.iterator(); + while (it.hasNext()) { + this.where += "'" + it.next() + "'"; + if (it.hasNext()) { + this.where += ","; + } + } + this.where += " " + ") "; + } else if (valuesGroup.size() == 1) { + if (tableAlias != null) { + this.appendPrivateWhereConditions(attribute, valuesGroup.get(0), "=", null, tableAlias); + } else { + this.appendPrivateWhereConditions(attribute, valuesGroup.get(0), "=", null, null); + } + } + } + } + + public void appendWhereConditions(String attribute, String value, String operator, String type, String tableAlias) { + if (attribute != null && value != null) { + if (tableAlias != null) { + this.where += tableAlias + "." + "\"" + attribute + "\""; + } else { + this.where += "\"" + attribute + "\""; + } + + if (operator != null) { + this.where += " " + operator + " "; + } else { + this.where += " = "; + } + this.where += "'" + value + "'"; + if (type != null) { + this.where += type; + } + } + } + + public void appendANDWhereConditions(String attribute, String value, String operator, String type, String tableAlias) { + if (attribute != null && value != null) { + if (where.length() > 0) { + this.where += " AND "; + } + if (tableAlias != null) { + this.where += tableAlias + "." + "\"" + attribute + "\""; + } else { + this.where += "\"" + attribute + "\""; + } + + if (operator != null) { + this.where += " " + operator + " "; + } else { + this.where += " = "; + } + this.where += "'" + value + "'"; + if (type != null) { + this.where += type; + } + } + } + + /** + * @action ajoute la condition where (or) si la valeur à tester n'est pas nulle + * @param attribute le nom de l'attribut qui sera testé + * @param value la valeur de l'attribut à tester + * @param operator l'opérateur de comparaison + * @param type + * @param tableAlias + * @author Morgane Vidal, le 31 août 2017 + */ + public void appendORWhereConditionIfNeeded(String attribute, String value, String operator, String type, String tableAlias) { + if (value != null) { + appendORWhereConditions(attribute, value, operator, type, tableAlias); + } + } + /** + * @action ajoute la condition where (and) si la valeur à tester n'est pas nulle + * @param attribute le nom de l'attribut qui sera testé + * @param value la valeur de l'attribut à tester + * @param operator l'opérateur de comparaison + * @param type + * @param tableAlias + * @author Morgane Vidal, le 21 février 2017 + * + */ + public void appendANDWhereConditionIfNeeded(String attribute, String value, String operator, String type, String tableAlias) { + if (value != null) { + appendANDWhereConditions(attribute, value, operator, type, tableAlias); + } + } + + /** + * ajoute un limit à la requête + * @author Morgane Vidal, 21 février 2017 + * @param limit valeur de la limitation + */ + public void appendLimit(String limit) { + this.limit += limit; + } + + public void appendANDWhereTableConstraintConditions(String attribute, String secondAttribute, String operator, String tableAlias, String secondTableAlias) { + if (attribute != null && secondAttribute != null) { + if (where.length() > 0) { + this.where += " AND "; + } + if (tableAlias != null) { + this.where += tableAlias + "." + "\"" + attribute + "\""; + } else { + this.where += "\"" + attribute + "\""; + } + + if (operator != null) { + this.where += " " + operator + " "; + } else { + this.where += " = "; + } + if (secondTableAlias != null) { + this.where += secondTableAlias + "." + "\"" + secondAttribute + "\""; + } else { + this.where += "\"" + secondAttribute + "\""; + } + } + } + + private void appendPrivateWhereConditions(String attribute, String value, String operator, String type, String tableAlias) { + if (attribute != null && value != null) { + if (tableAlias != null) { + this.where += tableAlias + "." + "\"" + attribute + "\""; + } else { + this.where += "\"" + attribute + "\""; + } + + if (operator != null) { + this.where += " " + operator + " "; + } else { + this.where += " = "; + } + this.where += "'" + value + "'"; + if (type != null) { + this.where += type; + } + } + } + + @Override + public String toString() { + String query = ""; + if (attributes != null && attributes.length() > 0) { + if (count) { + if (distinct) { + query += "SELECT DISTINCT count(" + attributes + ") "; + } else { + query += "SELECT count(" + attributes + ") "; + } + } else if (distinct) { + query += "SELECT DISTINCT " + attributes + " "; + } else { + query += "SELECT " + attributes + " "; + } + } else if (count) { + if (distinct) { + query += "SELECT DISTINCT count(*) "; + } else { + query += "SELECT count(*) "; + } + + } else if (distinct) { + query += "SELECT DISTINCT * "; + } else { + query += "SELECT * "; + } + if (from != null) { + query += from; + } + if (join != null) { + query += join; + query += "\n"; + } + if (where != null && where.length() > 0) { + query += "WHERE " + where; + } + if (orderBy != null && orderBy.length() > 0) { + query += "\n"; + query += "ORDER BY "; + query += orderBy; + } + if (limit != null && limit.length() > 0) { + query += "\n"; + query += "LIMIT "; + query += limit; + } + return query; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/Metadata.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/Metadata.java new file mode 100644 index 000000000..56a1676c8 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/Metadata.java @@ -0,0 +1,142 @@ +//********************************************************************************************** +// Metadata.java +// +// Author(s): Samuël Chérimont, Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: december 2015 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which represente metadata in response form +//*********************************************************************************************** +package phis2ws.service.view.brapi; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; + +/** + * Metadata - Classe contenant les données de pagination ainsi que des + * informations de status pour chaque requête + * + * @version1.0 + * + * @author Samuël Chérimont + * @see Pagination, ResultForm + * @date 03/12/2015 + * @update 10/2016 AC mise à jour BRAPI datafiles (liens retournés à l'utilisateur) + */ + +@ApiModel +public class Metadata { + + private final Pagination pagination; + private List status; + private List datafiles; + + public void setDatafiles(List datafiles) { + this.datafiles = datafiles; + } + + public Metadata() { + pagination = null; + status = null; + datafiles = new ArrayList<>(); + } + + public Metadata(List statuslist) { + pagination = null; + status = statuslist; + datafiles = new ArrayList<>(); + } + + public Metadata(Status status) { + pagination = null; + List statusList = new ArrayList<>(); + statusList.add(status); + this.status = statusList; + datafiles = new ArrayList<>(); + } + + /** + * Metadata() - Constructeur de la classe Metadata Initialise les champs + * status et pagination, l'objet pagination est un objet vide si le résultat + * sur lequle porte la pagination n'est composé que d'un seul élément + * + * @param pageSize Nombre de résultat par page + * @param currentPage Page actuelle + * @param sizeList nombre d'éléments du résultat + * + * @see Pagination + * @date 03/12/2015 + * @note la liste status est toujours vide car il n'y a pas d'informations + * particulières concernant le status + */ + public Metadata(int pageSize, int currentPage, int sizeList) { + if (sizeList > 1) { + pagination = new Pagination(pageSize, currentPage, sizeList); + } else { + pagination = null; + } + status = null; + datafiles = new ArrayList<>(); + } + + public Metadata(int pageSize, int currentPage, int sizeList, List statuslist) { + if (sizeList > 1) { + pagination = new Pagination(pageSize, currentPage, sizeList); + } else { + pagination = null; + } + status = statuslist; + datafiles = new ArrayList<>(); + } + + public Metadata(int pageSize, int currentPage, int sizeList, Status status) { + if (sizeList > 1) { + pagination = new Pagination(pageSize, currentPage, sizeList); + } else { + pagination = null; + } + List statusList = new ArrayList<>(); + statusList.add(status); + this.status = statusList; + datafiles = new ArrayList<>(); + } + + /** + * getPagination() - Getteur classique Récupère le champ pagination qui est + * un Objet et le cast en Pagination + * + * @return la pagination sous forme d'objet Pagination + * @date 03/12/2015 + */ + @ApiModelProperty(position = 1, required = true) + public Pagination getPagination() { + return pagination; + } + + @ApiModelProperty(position = 2, required = true) + public List getStatus() { + return status; + } + + public void setStatus(List status) { + this.status = status; + } + + public void addStatus(Status status) { + if (this.status == null) { + this.status = new ArrayList<>(); + } + this.status.add(status); + } + + + @ApiModelProperty(position = 3, required = true) + public List getDatafiles() { + return datafiles; + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/Pagination.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/Pagination.java new file mode 100644 index 000000000..4e66afb63 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/Pagination.java @@ -0,0 +1,125 @@ +//********************************************************************************************** +// Pagination.java +// +// Author(s): Samuël Chérimont, Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: december 2015 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which represente pagination in response form +//*********************************************************************************************** +package phis2ws.service.view.brapi; + +/** + * Pagination - Cette classe donne des détails sur la pagination et ses champs + * sont utilisés pour permettre une pagination correcte des résultats + * + * @version1.0 + * + * @author Samuël Cherimont + * @date 03/12/2015 + */ + +public class Pagination { + + private Integer pageSize, currentPage, totalCount, totalPages; + + public Pagination() { + } + + + + /** + * Pagination() - Constructeur de la classe Pagination: Attribue une valeur + * à chaque champ d'un objet Pagination. + * + * @param pageSize nombre d'éléments par page + * @param currentPage page actuelle + * @param sizeList nombre d'éléments total + * + * @date 03/12/2015 + * @update 08/2016 + */ + public Pagination(Integer pageSize, Integer currentPage, Integer sizeList) { + if (pageSize <= 0) { + this.pageSize = 1; + } else { + this.pageSize = pageSize; + } + if (currentPage <= 0) { +// this.currentPage = 1; Modif AC * MAJ BRAPI (page 0 = 0 ) + this.currentPage = 0; + } else { + this.currentPage = currentPage; + } + /** + * Si le nombre d'éléments total est inférieur au nombre d'éléments par + * * page, attribuer la valeur du nombre d'éléments par page au nombre * + * d'éléments (norme) + * MAJ BRAPI Modif AC * MAJ BRAPI si (pageSize > sizeList ) pageSize = sizeList + */ +// if (sizeList < this.pageSize) { +// totalCount = this.pageSize; +// } else { + totalCount = sizeList; +// } + /** + * Pour avoir le nombre de pages totales il faut diviser le nombre de + * résutats par le nombre de résultats par page mais comme il s'agit de + * deux entiers, il peut y avoir un reste à cette division et si il y en + * a un alors il faut le prendre en compte et rajouter une page au + * nombre total de pages + */ +// System.err.println(totalCount); +// System.err.println(pageSize); + if (totalCount % this.pageSize == 0) { + totalPages = totalCount / this.pageSize; + } else { + totalPages = (totalCount / this.pageSize) + 1; + } +// System.err.println(totalPages); + } + + /** + * Getteur du champ pageSize correspondant au nombre de résultats par page + * + * @return la valeur de pageSize + * @date 03/12/2015 + */ + public Integer getPageSize() { + return pageSize; + } + + /** + * Getteur du champ currentPage correspondant à la page actuelle + * + * @return la valeur de currentPage + * @date 03/12/2015 + */ + public Integer getCurrentPage() { + return currentPage; + } + + /** + * Getteur du champ totalCount correspondant au nombre total de resultats + * + * @return la valeur de totalCount + * @date 03/12/2015 + */ + public Integer getTotalCount() { + return totalCount; + } + + /** + * Getteur du champ totalPages correspondant au nombre total de pages + * + * @return la valeur de totalPages + * @date 03/12/2015 + */ + public Integer getTotalPages() { + return totalPages; + } + + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/Status.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/Status.java new file mode 100644 index 000000000..6879b68cb --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/Status.java @@ -0,0 +1,38 @@ +//********************************************************************************************** +// Status.java +// +// Author(s): Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: august 2016 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: Define the status message which is returned to the user after a request +//*********************************************************************************************** +package phis2ws.service.view.brapi; + +/** + * Implémentation de la plant breeding api pour les status des messages de retour. + * Il est possible de mettre des codes Error ou autre + * @author Arnaud CHARLEROY + */ +public class Status { + + public String message = null; + public StatusException exception = null; + + public Status() { + } + + public Status(String message, StatusException exception) { + this.exception = exception; + this.message = message; + } + + public Status(String message, String code, String details) { + this.exception = new StatusException(code, details); + this.message = message; + } + +} + diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/StatusException.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/StatusException.java new file mode 100644 index 000000000..1c1cd291f --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/StatusException.java @@ -0,0 +1,28 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.view.brapi; + +/** + * + * @author charlero + */ +public class StatusException { + + public String type; + public String href; + public String details; + + public StatusException(String type, String details) { + this.type = type; + this.details = details; + } + + public StatusException(String type, String href, String details) { + this.type = type; + this.href = href; + this.details = details; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/AbstractResultForm.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/AbstractResultForm.java new file mode 100644 index 000000000..334315b55 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/AbstractResultForm.java @@ -0,0 +1,48 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.view.brapi.form; + +import java.util.List; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.Status; + +/** + * + * @author A. CHARLEROY + */ +public abstract class AbstractResultForm { + + protected Metadata metadata; + + public AbstractResultForm() { + metadata = new Metadata(); + } + + public AbstractResultForm(Status status) { + metadata = new Metadata(status); + } + + + public AbstractResultForm(List statusList) { + metadata = new Metadata(statusList); + } + + + public void setMetadata(Metadata metadata) { + this.metadata = metadata; + } + + + public Metadata getMetadata() { + return metadata; + } + + abstract int resultSize(); + + + + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormAgronomicalObject.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormAgronomicalObject.java new file mode 100644 index 000000000..e0ec760ac --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormAgronomicalObject.java @@ -0,0 +1,39 @@ +//********************************************************************************************** +// ResponseFormAgronomicalObject.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: august 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: January, 2017 +// Subject: Allows the formating of the result of the queries about +// agronomical objects +//*********************************************************************************************** + +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatAgronomicalObject; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.AgronomicalObject; + +public class ResponseFormAgronomicalObject extends ResultForm{ + + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param list liste des résultats + * @param paginate + */ + public ResponseFormAgronomicalObject(int pageSize, int currentPage, ArrayList list, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, list.size()); + if (list.size() > 1) { + result = new ResultatAgronomicalObject(list, metadata.getPagination(), paginate); + } else { + result = new ResultatAgronomicalObject(list); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormDocumentMetadata.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormDocumentMetadata.java new file mode 100644 index 000000000..b822b6b30 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormDocumentMetadata.java @@ -0,0 +1,36 @@ +//********************************************************************************************** +// ResponseFormDocumentMetadata.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: June 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: June, 2017 +// Subject: extends ResultForm. Adapted to the labelViews list informations used in the documents +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatDocumentMetadata; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.Document; + +public class ResponseFormDocumentMetadata extends ResultForm { + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param list liste des résultats + * @param paginate + */ + public ResponseFormDocumentMetadata(int pageSize, int currentPage, ArrayList list, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, list.size()); + if (list.size() > 1) { + result = new ResultatDocumentMetadata(list, metadata.getPagination(), paginate); + } else { + result = new ResultatDocumentMetadata(list); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormDocumentType.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormDocumentType.java new file mode 100644 index 000000000..47bc5af5d --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormDocumentType.java @@ -0,0 +1,66 @@ +//********************************************************************************************** +// ResponseFormDocumentType.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2016 +// Contact: arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: extends ResultForm. Adapted to the labelViews list informations used in the documents +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.Status; +import phis2ws.service.view.brapi.results.ResultatDocumentType; +import phis2ws.service.view.manager.ResultForm; + +public class ResponseFormDocumentType extends ResultForm { + public ResponseFormDocumentType(int pageSize, int currentPage, ArrayList list, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, list.size()); + if (list.size() > 1) { + result = new ResultatDocumentType(list, metadata.getPagination(), paginate); + } else { + result = new ResultatDocumentType(list); + } + + } + + public ResponseFormDocumentType(int pageSize, int currentPage, ArrayList list, boolean paginate, Integer totalCount) { + metadata = new Metadata(pageSize, currentPage, totalCount); + if (list.size() > 1) { + result = new ResultatDocumentType(list, metadata.getPagination(), paginate); + } else { + result = new ResultatDocumentType(list); + } + } + + public ResponseFormDocumentType(int pageSize, int currentPage, ArrayList list, boolean paginate, Status status) { + metadata = new Metadata(pageSize, currentPage, list.size(),status); + if (list.size() > 1) { + result = new ResultatDocumentType(list, metadata.getPagination(), paginate); + } else { + result = new ResultatDocumentType(list); + } + } + + public ResponseFormDocumentType(int pageSize, int currentPage, ArrayList list, boolean paginate, ArrayList status) { + metadata = new Metadata(pageSize, currentPage, list.size(),status); + if (list.size() > 1) { + result = new ResultatDocumentType(list, metadata.getPagination(), paginate); + } else { + result = new ResultatDocumentType(list); + } + } + + public ResponseFormDocumentType(int pageSize, int currentPage, ArrayList list, boolean paginate, Integer totalCount, ArrayList status) { + metadata = new Metadata(pageSize, currentPage, totalCount,status); + if (list.size() > 1) { + result = new ResultatDocumentType(list, metadata.getPagination(), paginate); + } else { + result = new ResultatDocumentType(list); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormExperiment.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormExperiment.java new file mode 100644 index 000000000..ecf33d226 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormExperiment.java @@ -0,0 +1,39 @@ +//********************************************************************************************** +// ResponseFormExperiment.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: august 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 31 2017 : Passage de trial à experiment +// Subject: Allows the formating of the results of the queries about experiments +//*********************************************************************************************** + +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatExperiment; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.Experiment; + + +public class ResponseFormExperiment extends ResultForm{ + + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param list liste des résultats + * @param paginate + */ + public ResponseFormExperiment(int pageSize, int currentPage, ArrayList list, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, list.size()); + if (list.size() > 1) { + result = new ResultatExperiment(list, metadata.getPagination(), paginate); + } else { + result = new ResultatExperiment(list); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormGET.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormGET.java new file mode 100644 index 000000000..cf746e288 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormGET.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.view.brapi.form; + +import java.util.List; +import phis2ws.service.view.brapi.Status; + +/** + * + * @author A. CHARLEROY + */ +public class ResponseFormGET extends AbstractResultForm{ + + public ResponseFormGET() { + super(); + } + + public ResponseFormGET(Status status) { + super(status); + } + + public ResponseFormGET(List statusList) { + super(statusList); + } + + @Override + public int resultSize() { + return 0; + } + + + + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormGroup.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormGroup.java new file mode 100644 index 000000000..87158227d --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormGroup.java @@ -0,0 +1,37 @@ +//********************************************************************************************** +// ResponseFormGroup.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: April, 2017 +// Subject: Allows the formating of the results of the queries about groups +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatGroup; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.Group; + +public class ResponseFormGroup extends ResultForm { + + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param groups liste des resultats + * @param paginate + */ + public ResponseFormGroup(int pageSize, int currentPage, ArrayList groups, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, groups.size()); + if (groups.size() > 1) { + result = new ResultatGroup(groups, metadata.getPagination(), paginate); + } else { + result = new ResultatGroup(groups); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormMethod.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormMethod.java new file mode 100644 index 000000000..25ab80eb2 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormMethod.java @@ -0,0 +1,36 @@ +//********************************************************************************************** +// ResponseFormMethod.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 23 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 23 2017 +// Subject: Allows the formating of the result of the queries about methods +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatMethod; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.Method; + +public class ResponseFormMethod extends ResultForm { + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param list liste des résultats + * @param paginate + */ + public ResponseFormMethod(int pageSize, int currentPage, ArrayList list, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, list.size()); + if (list.size() > 1) { + result = new ResultatMethod(list, metadata.getPagination(), paginate); + } else { + result = new ResultatMethod(list); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormPOST.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormPOST.java new file mode 100644 index 000000000..cf7e919f9 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormPOST.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.view.brapi.form; + +import java.util.List; +import phis2ws.service.view.brapi.Status; + +/** + * + * @author A. CHARLEROY + */ +public class ResponseFormPOST extends AbstractResultForm{ + + public ResponseFormPOST() { + super(); + } + + public ResponseFormPOST(Status status) { + super(status); + } + + public ResponseFormPOST(List statusList) { + super(statusList); + } + + @Override + public int resultSize() { + return 0; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormPhenotype.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormPhenotype.java new file mode 100644 index 000000000..110265884 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormPhenotype.java @@ -0,0 +1,36 @@ +//********************************************************************************************** +// ResponseFormPhenotype.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: October, 18 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 18 2017 +// Subject: Allows the formating of the result of the request about Phenotypes +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatPhenotype; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.Phenotype; + +public class ResponseFormPhenotype extends ResultForm { + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param list liste des résultats + * @param paginate + */ + public ResponseFormPhenotype(int pageSize, int currentPage, ArrayList list, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, list.size()); + if (list.size() > 1) { + result = new ResultatPhenotype(list, metadata.getPagination(), paginate); + } else { + result = new ResultatPhenotype(list); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormProject.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormProject.java new file mode 100644 index 000000000..48e7d7e79 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormProject.java @@ -0,0 +1,37 @@ +//********************************************************************************************** +// ResponseFormProject.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: Allows the formating of the results of the queries about projects +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatProject; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.Project; + +public class ResponseFormProject extends ResultForm { + + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param projects liste des résultats + * @param paginate + */ + public ResponseFormProject(int pageSize, int currentPage, ArrayList projects, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, projects.size()); + if (projects.size() > 1) { + result = new ResultatProject(projects, metadata.getPagination(), paginate); + } else { + result = new ResultatProject(projects); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormTrait.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormTrait.java new file mode 100644 index 000000000..378081798 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormTrait.java @@ -0,0 +1,36 @@ +//********************************************************************************************** +// ResponseFormTrait.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 23 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 23 2017 +// Subject: Allows the formating of the result of the queries about traits +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatTrait; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.Trait; + +public class ResponseFormTrait extends ResultForm { + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param list liste des résultats + * @param paginate + */ + public ResponseFormTrait(int pageSize, int currentPage, ArrayList list, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, list.size()); + if (list.size() > 1) { + result = new ResultatTrait(list, metadata.getPagination(), paginate); + } else { + result = new ResultatTrait(list); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormUnit.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormUnit.java new file mode 100644 index 000000000..658473a5b --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormUnit.java @@ -0,0 +1,36 @@ +//********************************************************************************************** +// ResponseFormUnit.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 23 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 23 2017 +// Subject: Allows the formating of the result of the queries about units +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatUnit; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.Unit; + +public class ResponseFormUnit extends ResultForm { + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param list liste des résultats + * @param paginate + */ + public ResponseFormUnit(int pageSize, int currentPage, ArrayList list, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, list.size()); + if (list.size() > 1) { + result = new ResultatUnit(list, metadata.getPagination(), paginate); + } else { + result = new ResultatUnit(list); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormUser.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormUser.java new file mode 100644 index 000000000..46a7d5144 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormUser.java @@ -0,0 +1,37 @@ +//********************************************************************************************** +// ResponseFormUser.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: April, 2017 +// Subject: Allows the formating of the results of the queries about groups +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.model.User; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatUser; +import phis2ws.service.view.manager.ResultForm; + +public class ResponseFormUser extends ResultForm { + + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param groups liste des resultats + * @param paginate + */ + public ResponseFormUser(int pageSize, int currentPage, ArrayList groups, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, groups.size()); + if (groups.size() > 1) { + result = new ResultatUser(groups, metadata.getPagination(), paginate); + } else { + result = new ResultatUser(groups); + } + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormVariable.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormVariable.java new file mode 100644 index 000000000..12a9c6f61 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseFormVariable.java @@ -0,0 +1,37 @@ +//********************************************************************************************** +// ResponseFormVariable.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 23 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 23 2017 +// Subject: Allows the formating of the result of the queries about variables +//*********************************************************************************************** +package phis2ws.service.view.brapi.form; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.results.ResultatVariable; +import phis2ws.service.view.manager.ResultForm; +import phis2ws.service.view.model.phis.Variable; + +public class ResponseFormVariable extends ResultForm { + /** + * Initialise les champs metadata et result + * @param pageSize nombre de résultats par page + * @param currentPage page demandée + * @param list liste des résultats + * @param paginate + */ + public ResponseFormVariable(int pageSize, int currentPage, ArrayList list, boolean paginate) { + metadata = new Metadata(pageSize, currentPage, list.size()); + if (list.size() > 1) { + result = new ResultatVariable(list, metadata.getPagination(), paginate); + } else { + result = new ResultatVariable(list); + } + } +} + diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseUnique.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseUnique.java new file mode 100644 index 000000000..90fc8ca07 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/ResponseUnique.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package phis2ws.service.view.brapi.form; + +import phis2ws.service.view.brapi.Metadata; + +/** + * + * @author charlero + */ +public class ResponseUnique { + Metadata metadata; + Object result; + + public ResponseUnique( Object result) { + this.metadata = new Metadata(); + this.result = result; + } + + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/TokenResponseStructure.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/TokenResponseStructure.java new file mode 100644 index 000000000..1023a3f01 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/form/TokenResponseStructure.java @@ -0,0 +1,42 @@ +package phis2ws.service.view.brapi.form; + +import phis2ws.service.view.brapi.Metadata; + +/** + * TokenResponseStructure - Prépare le résultat final pour qu'il soit conforme au modèle de + la Plant breeding API + * + * @version1.0 + * + * @author Samuël Chérimont + * @date 25/11/2015 + * @note session_token n'est pas conforme aux norme de nomination Java + */ +public class TokenResponseStructure { + + private final Metadata metadata; + private final String session_token; + + /** + * ResultForm() - Prepare l'objet métadata vide et associe l'identifiant de + * session à la variable session_token + * + * @param id L'identifiant de session + * + * @see result_output.Metadata + * @date 25/11/2015 + */ + public TokenResponseStructure(String id) { + this.metadata = new Metadata(0, 0, 1); + this.session_token = id; + } + + public Metadata getMetadata() { + return metadata; + } + + public String getSession_token() { + return session_token; + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatAgronomicalObject.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatAgronomicalObject.java new file mode 100644 index 000000000..a42114136 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatAgronomicalObject.java @@ -0,0 +1,41 @@ +//********************************************************************************************** +// ResultatAgronomicalObject.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: august 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: January, 2017 +// Subject: extend form Resultat adapted to the agronomical object list +//*********************************************************************************************** + +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.AgronomicalObject; + +public class ResultatAgronomicalObject extends Resultat{ + + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à un seul élément + * @param agronomicalObjectList + */ + public ResultatAgronomicalObject(ArrayList agronomicalObjectList) { + super(agronomicalObjectList); + } + + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à plusieurs éléments + * @param agronomicalObjectList + * @param pagination + * @param paginate + */ + public ResultatAgronomicalObject(ArrayList agronomicalObjectList, Pagination pagination, boolean paginate) { + super(agronomicalObjectList, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatDocumentMetadata.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatDocumentMetadata.java new file mode 100644 index 000000000..24d71b502 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatDocumentMetadata.java @@ -0,0 +1,40 @@ +//********************************************************************************************** +// ResultatDocumentMetadata.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: June 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: June, 2017 +// Subject: extended from Resultat adapted to the documents metadata list +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.Document; + +public class ResultatDocumentMetadata extends Resultat{ + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à un seul élément + * @param documentsList liste des documents contenant un seul élément + */ + public ResultatDocumentMetadata(ArrayList documentsList) { + super(documentsList); + } + + /** + * Contructeur qui appelle celui de la classe mère dans le cas d'une liste + * à plusieurs éléments + * @param documentsList liste des documents + * @param pagination Objet pagination permettant de trier la liste documentsList + * @param paginate + */ + public ResultatDocumentMetadata(ArrayList documentsList, Pagination pagination, + boolean paginate) { + super(documentsList, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatDocumentType.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatDocumentType.java new file mode 100644 index 000000000..67a080f51 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatDocumentType.java @@ -0,0 +1,26 @@ +//********************************************************************************************** +// ResultatDocumentType.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: October 2016 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: extends Resultat. Adapted to a labelView list +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; + +public class ResultatDocumentType extends Resultat { + public ResultatDocumentType(ArrayList list) { + super(list); + } + + public ResultatDocumentType(ArrayList list, Pagination pagination, boolean paginate) { + super(list, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatExperiment.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatExperiment.java new file mode 100644 index 000000000..3f66614b8 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatExperiment.java @@ -0,0 +1,43 @@ +//********************************************************************************************** +// ResultatExperiment.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: august 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: January, 2017 +// Subject: extended from Resultat adapted to the experiment list +//*********************************************************************************************** + +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.Experiment; + + +public class ResultatExperiment extends Resultat{ + + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à un seul élément + * @param experimentList liste des experimentations contenant un seul élément + */ + public ResultatExperiment(ArrayList experimentList) { + super(experimentList); + } + + /** + * Contructeur qui appelle celui de la classe mère dans le cas d'une liste + * à plusieurs éléments + * @param experimentList liste des expérimentations + * @param pagination Objet pagination permettant de trier la liste experimentList + * @param paginate + */ + public ResultatExperiment(ArrayList experimentList, Pagination pagination, + boolean paginate) { + super(experimentList, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatGroup.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatGroup.java new file mode 100644 index 000000000..a680373cd --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatGroup.java @@ -0,0 +1,39 @@ +//********************************************************************************************** +// ResultatGroup.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: April, 2017 +// Subject: extended from Resultat adapted to the group list +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.Group; + +public class ResultatGroup extends Resultat { + + /** + * appelle le constructeur de la classe mere, dans le cas d'une liste à un seul élément + * @param groups liste des groupes, contenant un seul élément + */ + public ResultatGroup(ArrayList groups) { + super(groups); + } + + /** + * appelle le constructeur parent dans le cas d'une liste à plusieurs éléments + * @param groups liste des groupes + * @param pagination objet pagination permettant de trier les groupes + * @param paginate + */ + public ResultatGroup(ArrayList groups, Pagination pagination, boolean paginate) { + super(groups, pagination, paginate); + } +} + diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatMethod.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatMethod.java new file mode 100644 index 000000000..6f2c3dbf0 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatMethod.java @@ -0,0 +1,39 @@ +//********************************************************************************************** +// ResultatMethod.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 23 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 23 2017 +// Subject: extend form Resultat adapted to the methods +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.Method; + +public class ResultatMethod extends Resultat { + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à un seul élément + * @param methods + */ + public ResultatMethod(ArrayList methods) { + super(methods); + } + + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à plusieurs éléments + * @param methods + * @param pagination + * @param paginate + */ + public ResultatMethod(ArrayList methods, Pagination pagination, boolean paginate) { + super(methods, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatPhenotype.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatPhenotype.java new file mode 100644 index 000000000..9baa66598 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatPhenotype.java @@ -0,0 +1,39 @@ +//********************************************************************************************** +// ResultatPhenotype.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: October, 18 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 18 2017 +// Subject: extend from Resultat, adapted to the phenotype object list +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.Phenotype; + +public class ResultatPhenotype extends Resultat { + /** + * Constructeur qui appelle celui de la classe mère dans le caqs d'une liste + * à un seul élément + * @param phenotypes + */ + public ResultatPhenotype(ArrayList phenotypes) { + super(phenotypes); + } + + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à plusieurs éléments + * @param phenotypes + * @param pagination + * @param paginate + */ + public ResultatPhenotype(ArrayList phenotypes, Pagination pagination, boolean paginate) { + super(phenotypes, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatProject.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatProject.java new file mode 100644 index 000000000..ccfac91b6 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatProject.java @@ -0,0 +1,38 @@ +//********************************************************************************************** +// ResultatProject.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: extended from Resultat adapted to the project list +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.Project; + +public class ResultatProject extends Resultat { + + /** + * appelle le constructeur de la classe mere, dans le cas d'une liste à un seul éléments + * @param projects liste des projets, contenant un seul élément + */ + public ResultatProject(ArrayList projects) { + super(projects); + } + + /** + * appelle le constructeur parent dans le cas d'une liste à plusieurs éléments + * @param projects liste des projets + * @param pagination objet pagination permettant de trier projects + * @param paginate + */ + public ResultatProject(ArrayList projects, Pagination pagination, boolean paginate) { + super(projects, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatTrait.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatTrait.java new file mode 100644 index 000000000..91b1f9d86 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatTrait.java @@ -0,0 +1,39 @@ +//********************************************************************************************** +// ResultatTrait.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 23 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 23 2017 +// Subject: extend form Resultat adapted to the traits +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.Trait; + +public class ResultatTrait extends Resultat { + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à un seul élément + * @param traits + */ + public ResultatTrait(ArrayList traits) { + super(traits); + } + + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à plusieurs éléments + * @param traits + * @param pagination + * @param paginate + */ + public ResultatTrait(ArrayList traits, Pagination pagination, boolean paginate) { + super(traits, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatUnit.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatUnit.java new file mode 100644 index 000000000..8f57d5bd9 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatUnit.java @@ -0,0 +1,39 @@ +//********************************************************************************************** +// ResultatUnit.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 23 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 23 2017 +// Subject: extend form Resultat adapted to the units +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.Unit; + +public class ResultatUnit extends Resultat{ + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à un seul élément + * @param units + */ + public ResultatUnit(ArrayList units) { + super(units); + } + + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à plusieurs éléments + * @param units + * @param pagination + * @param paginate + */ + public ResultatUnit(ArrayList units, Pagination pagination, boolean paginate) { + super(units, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatUser.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatUser.java new file mode 100644 index 000000000..746ee64a4 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatUser.java @@ -0,0 +1,37 @@ +//********************************************************************************************** +// ResultatUser.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: April, 2017 +// Subject: extended from Resultat adapted to the user list +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.model.User; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; + +public class ResultatUser extends Resultat { + /** + * appelle le constructeur de la classe mere, dans le cas d'une liste à un seul élément + * @param users liste des users, contenant un seul élément + */ + public ResultatUser(ArrayList users) { + super(users); + } + + /** + * appelle le constructeur parent dans le cas d'une liste à plusieurs éléments + * @param users liste des users + * @param pagination objet pagination permettant de trier les users + * @param paginate + */ + public ResultatUser(ArrayList users, Pagination pagination, boolean paginate) { + super(users, pagination, paginate); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatVariable.java b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatVariable.java new file mode 100644 index 000000000..9012b6be5 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/brapi/results/ResultatVariable.java @@ -0,0 +1,40 @@ +//********************************************************************************************** +// ResulatVariable.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: Novemnber, 23 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 23 2017 +// Subject: extend form Resultat adapted to the variables +//*********************************************************************************************** +package phis2ws.service.view.brapi.results; + +import java.util.ArrayList; +import phis2ws.service.view.brapi.Pagination; +import phis2ws.service.view.manager.Resultat; +import phis2ws.service.view.model.phis.Variable; + +public class ResultatVariable extends Resultat{ + + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à un seul élément + * @param variables + */ + public ResultatVariable(ArrayList variables) { + super(variables); + } + + /** + * Constructeur qui appelle celui de la classe mère dans le cas d'une liste + * à plusieurs éléments + * @param variables + * @param pagination + * @param paginate + */ + public ResultatVariable(ArrayList variables, Pagination pagination, boolean paginate) { + super(variables, pagination, paginate); + } +} \ No newline at end of file diff --git a/phis2-ws/src/main/java/phis2ws/service/view/manager/ResultForm.java b/phis2-ws/src/main/java/phis2ws/service/view/manager/ResultForm.java new file mode 100644 index 000000000..50f715983 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/manager/ResultForm.java @@ -0,0 +1,57 @@ +//********************************************************************************************** +// ResultForm.java +// +// Author(s): Samuël Chérimont, Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: december 2015 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which represente response form +//*********************************************************************************************** +package phis2ws.service.view.manager; + +import java.util.List; +import phis2ws.service.view.brapi.Metadata; +import phis2ws.service.view.brapi.Status; + +/** + * ResultForm - Classe abstraite qui sert de base aux ResultForm plus + * spécifiques + * + * @version1.0 + * + * @author Samuël Chérimont + * @param classe de spécification de la classe héritée + * @see Resultat + * @date 03/12/2015 + */ +public abstract class ResultForm { + + protected Metadata metadata; + protected Resultat result; + + /** + * resultSize() - Récupère le nombre d'éléments de la liste data qui est + un champ de result + * + * @return le nombre d'éléments de la liste + * @see Resultat + * @date 04/12/2015 + */ + public int resultSize() { + return result.dataSize(); + } + + public Metadata getMetadata() { + return metadata; + } + + public Resultat getResult() { + return result; + } + + public void setStatus(List status){ + metadata.setStatus(status); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/manager/Resultat.java b/phis2-ws/src/main/java/phis2ws/service/view/manager/Resultat.java new file mode 100644 index 000000000..f558f4250 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/manager/Resultat.java @@ -0,0 +1,126 @@ +//********************************************************************************************** +// Resultat.java +// +// Author(s): Samuël Chérimont, Arnaud CHARLEROY +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2016 +// Creation date: december 2015 +// Contact:arnaud.charleroy@supagro.inra.fr, anne.tireau@supagro.inra.fr, pascal.neveu@supagro.inra.fr +// Last modification date: October, 2016 +// Subject: A class which represente result part in response form +//*********************************************************************************************** +package phis2ws.service.view.manager; + +import phis2ws.service.view.brapi.Pagination; +import java.util.ArrayList; + +/** + * Resultat - Classe abstraite offrant un modèle pour la présentation des + * résultats sous forme d'un objet qui sera conforme aux standarts de la web api + * au format JSON + * + * @version1.0 + * + * @author Samuël Chérimont + * @param Classe des objets conetnus dans la liste data + * + * @see Pagination + * @date 14/12/2015 + */ +public abstract class Resultat { + + ArrayList data; + + public Resultat() { + data = new ArrayList<>(); + } + + + + /** + * Resultat() - Constructeur appelé dans le cas ou un seul résultat est + * demandé (Pagination non nécessaire) + * + * @param list Liste contenant un seul objet ou zero + * @date 14/12/2015 + */ + public Resultat(ArrayList list) { + this.data = new ArrayList(); + if(list.size() > 0){ + data.add(list.get(0)); + } + + } + + /** + * Resultat() - Constructeur qui prend en compte la pagination + * + * @param list Liste contenant plusieurs éléments + * @param pagination Objet Pagination permettant de trier la liste list + * @param paginate + * + * @see copyList() + * @date 14/12/2015 + */ +// @SuppressWarnings("OverridableMethodCallInConstructor") + public Resultat(ArrayList list, Pagination pagination, boolean paginate) { + if(paginate){ + this.data = list; + }else{ + this.data = copyList(list, pagination); + } + + } + + + /** + * copyList() - Trie une liste d'éléments selon la page demandée et le + * nombre de résultats par page + * + * @param list liste des éléments non triés + * @param pagination Objet contenant les données de pagination nécessaires + * pour le tri de la liste list + * @return la liste des éléments paginée + * @date 14/12/2015 + * @Update AC 08/16 première page = page 0 BRAPI + */ + final ArrayList copyList(ArrayList list, Pagination pagination) { + ArrayList finalList = new ArrayList(); + if (pagination.getCurrentPage() > pagination.getTotalPages()) { + return finalList; + } +// int i = (pagination.getCurrentPage() - 1) * pagination.getPageSize(); +// Modification BRAPI page 0 au lieu de page 1 dans metadata + int i = (pagination.getCurrentPage()) * pagination.getPageSize(); + int tmp = i; + while (i < list.size() && (i - tmp) < pagination.getPageSize()) { + finalList.add(list.get(i)); + i++; + } + return finalList; + }; + + /** + * dataSize() - Recupère le nombre d'objets du champ data + * + * @return Le nombre d'objets de la liste data ou 0 si la liste n'a pas été + * initialisée + * @date 14/12/2015 + */ + public int dataSize() { + if (data != null) { + return data.size(); + } else { + return 0; + } + } + + public ArrayList getData() { + return data; + } + + public void setData(ArrayList data) { + this.data = data; + } + +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/AgronomicalObject.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/AgronomicalObject.java new file mode 100644 index 000000000..2544ba748 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/AgronomicalObject.java @@ -0,0 +1,86 @@ +//********************************************************************************************** +// AgronomicalObject.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: august 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: January, 2017 +// Subject: Represents the agronomical object +//*********************************************************************************************** + +package phis2ws.service.view.model.phis; + +import java.util.ArrayList; + +public class AgronomicalObject { + private String uri; + private String typeAgronomicalObject; + private String geometry; + private String experiment; + + //SILEX:INFO + //Pour l'instant je l'ai mis en attribut pour aller plus vite et avancer le reste (dans le get ao) + //Il faudra modifier le getAO (search) en récupérant toutes les propriétés, alias inclu + private String alias; + //\SILEX:INFO + + private ArrayList properties = new ArrayList<>(); + + public AgronomicalObject(String uri) { + this.uri = uri; + } + + public AgronomicalObject() { + + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getTypeAgronomicalObject() { + return typeAgronomicalObject; + } + + public void setTypeAgronomicalObject(String typeAgronomicalObject) { + this.typeAgronomicalObject = typeAgronomicalObject; + } + + public String getGeometry() { + return geometry; + } + + public void setGeometry(String geometry) { + this.geometry = geometry; + } + + public String getUriExperiment() { + return experiment; + } + + public void setUriExperiment(String uriExperiment) { + this.experiment = uriExperiment; + } + + public ArrayList getProperties() { + return properties; + } + + public void addProperty(Property property) { + properties.add(property); + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Contact.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Contact.java new file mode 100644 index 000000000..24f9a859c --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Contact.java @@ -0,0 +1,56 @@ +//********************************************************************************************** +// Contact.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: Represents the contact view (used in Experiment and Project) +//*********************************************************************************************** + +package phis2ws.service.view.model.phis; + +public class Contact { + private String email; + private String firstName; + private String familyName; + private String type; + + public Contact() { + + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getFamilyName() { + return familyName; + } + + public void setFamilyName(String familyName) { + this.familyName = familyName; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Data.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Data.java new file mode 100644 index 000000000..ef5ea72ce --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Data.java @@ -0,0 +1,61 @@ +//********************************************************************************************** +// Data.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: September 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: September, 14 2017 +// Subject: Represents the data view +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +public class Data { + private String agronomicalObject; + private String date; + private String value; + private String variable; //Used only for the get phenotypes + + public Data() { + + } + + public Data(Data data) { + agronomicalObject = data.getAgronomicalObject(); + date = data.getDate(); + value = data.getValue(); + } + + public String getAgronomicalObject() { + return agronomicalObject; + } + + public void setAgronomicalObject(String agronomicalObject) { + this.agronomicalObject = agronomicalObject; + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getVariable() { + return variable; + } + + public void setVariable(String variable) { + this.variable = variable; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Document.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Document.java new file mode 100644 index 000000000..94fa60049 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Document.java @@ -0,0 +1,128 @@ +//********************************************************************************************** +// Document.java +// +// Author(s): Arnaud CHARLEROY, Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: arnaud.charleroy@inra.fr, morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: Represents the Document +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +import java.util.ArrayList; +import phis2ws.service.resources.dto.ConcernItemDTO; + +public class Document { + private String uri; + private String documentType; + private String creator; + private String language; + private String title; + private String creationDate; + private String format; + private String comment; + private ArrayList concernedItems = new ArrayList<>(); + private String status; + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getDocumentType() { + return documentType; + } + + public void setDocumentType(String documentType) { + this.documentType = documentType; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getCreationDate() { + return creationDate; + } + + public void setCreationDate(String creationDate) { + this.creationDate = creationDate; + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public ArrayList getConcernedItems() { + return concernedItems; + } + + public void setConcernedItems(ArrayList concernedItems) { + this.concernedItems = concernedItems; + } + + public void addConcernedItem(ConcernItemDTO concernedItem) { + this.concernedItems.add(concernedItem); + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + /** + * Compare les deux documents (compare les attributs un à un) + * @param document Document à comparer à this + * @return true s'ils sont égaux, false sinon + */ + public boolean equals(Document document) { + return this.uri.equals(document.uri) + && this.documentType.equals(document.documentType) + && this.creator.equals(document.creator) + && this.language.equals(document.language) + && this.title.equals(document.title) + && this.creationDate.equals(document.creationDate) + && this.format.equals(document.format) + && this.status.equals(document.status); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Experiment.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Experiment.java new file mode 100644 index 000000000..33834d534 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Experiment.java @@ -0,0 +1,176 @@ +//********************************************************************************************** +// Experiment.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: January 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: February, 2017 +// Subject: Represents the experiment +//*********************************************************************************************** + +package phis2ws.service.view.model.phis; + +import java.util.ArrayList; + +public class Experiment { + private String uri; + private String startDate; + private String endDate; + private String field; + private String campaign; + private String place; + private String alias; + private String comment; + private String keywords; + private String objective; + private String cropSpecies; + + private ArrayList projects = new ArrayList<>(); + private ArrayList groups = new ArrayList<>(); + private ArrayList contacts = new ArrayList<>(); + + public Experiment() { + + } + + public Experiment(String uri) { + this.uri = uri; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getStartDate() { + return startDate; + } + + public void setStartDate(String startDate) { + this.startDate = startDate; + } + + public String getEndDate() { + return endDate; + } + + public void setEndDate(String endDate) { + this.endDate = endDate; + } + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + public String getCampaign() { + return campaign; + } + + public void setCampaign(String campaign) { + this.campaign = campaign; + } + + public String getPlace() { + return place; + } + + public void setPlace(String place) { + this.place = place; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public String getKeywords() { + return keywords; + } + + public void setKeywords(String keywords) { + this.keywords = keywords; + } + + public String getObjective() { + return objective; + } + + public void setObjective(String objective) { + this.objective = objective; + } + + public String getCropSpecies() { + return cropSpecies; + } + + public void setCropSpecies(String cropSpecies) { + this.cropSpecies = cropSpecies; + } + + public ArrayList getContacts() { + return contacts; + } + + public void addContact(Contact contact) { + this.contacts.add(contact); + } + + public ArrayList getProjects() { + return this.projects; + } + + public void addProject(Project project) { + this.projects.add(project); + } + + public ArrayList getGroups() { + return this.groups; + } + + public void addGroup(Group group) { + this.groups.add(group); + } + + public void setGroupList(ArrayList groups) { + this.groups = groups; + } + + /** + * Compare les deux experimentations (compare les attributs un à un) + * @param experiment Experiment à comparer à this + * @return true s'ils sont égaux, false sinon + */ + public boolean equals(Experiment experiment) { + return this.uri.equals(experiment.uri) + && this.startDate.equals(experiment.startDate) + && this.endDate.equals(experiment.endDate) + && this.field.equals(experiment.field) + && this.campaign.equals(experiment.campaign) + && this.place.equals(experiment.place) + && this.alias.equals(experiment.alias) + && this.comment.equals(experiment.comment) + && this.keywords.equals(experiment.keywords) + && this.objective.equals(experiment.objective) + && this.cropSpecies.equals(experiment.cropSpecies); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Group.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Group.java new file mode 100644 index 000000000..6b9411cc5 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Group.java @@ -0,0 +1,79 @@ +//********************************************************************************************** +// Group.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: April 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: May, 2017 +// Subject: Represents the group +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +import java.util.ArrayList; +import phis2ws.service.model.User; + +public class Group { + private String uri; + private String name; + private String level; + private String description; + private ArrayList users = new ArrayList<>(); + + public Group() { + + } + + public Group(String uri) { + this.uri = uri; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public boolean equals(Group group) { + return this.uri.equals(group.uri); + } + + public ArrayList getUsers() { + return this.users; + } + + public void addUser(User u) { + this.users.add(u); + } + + public void setUserList(ArrayList users) { + this.users = users; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/InstanceDefinition.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/InstanceDefinition.java new file mode 100644 index 000000000..39fa41f83 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/InstanceDefinition.java @@ -0,0 +1,73 @@ +//********************************************************************************************** +// InstanceDefinition.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 15 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 15 2017 +// Subject: Represents the instance definiton view +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +import java.util.ArrayList; + +public class InstanceDefinition { + + /** + * @attribute uri l'uri de l'instance (ex. http://www.phenome-fppn.fr/diaphen/id/variable/v001) + * @attribute label le label de la variable (ex. surface foliaire) + * @attribute comment un commentaire sur l'instance + * @attribute ontologiesReferences des références vers des ontologies externes (ex. une référence skos vers la crop onto) + */ + protected String uri; + protected String label; + protected String comment; + + ArrayList ontologiesReferences = new ArrayList<>(); + + public InstanceDefinition() { + + } + + public InstanceDefinition(String uri) { + this.uri = uri; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public ArrayList getOntologiesReferences() { + return ontologiesReferences; + } + + public void setOntologiesReferences(ArrayList ontologiesReferences) { + this.ontologiesReferences = ontologiesReferences; + } + + public void addOntologyReference(OntologyReference ontologyReference) { + ontologiesReferences.add(ontologyReference); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Layer.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Layer.java new file mode 100644 index 000000000..216b177f6 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Layer.java @@ -0,0 +1,71 @@ +//********************************************************************************************** +// Layer.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: August 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: August, 28 2017 +// Subject: Represents the layer +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +import java.util.HashMap; + +public class Layer { + + /** + * @param objectURI URI de l'objet auquel la couche correspond + * @param objectType type de l'objet (ex : http://www.phenome-fppn.fr/Vocabulary/2017/Experiment) + * @param depth true si la couche a tous les descendants de objectURI + * false si on elle n'a que les enfants directs + * @param filePath le chemin du fichier geoJSON correspondant à la couche + * @param children la liste des descendants de objectURI. Clé : URI, valeur : type + */ + private String objectURI; + private String objectType; + private String depth; + private String filePath; + private HashMap children; + + public String getObjectURI() { + return objectURI; + } + + public void setObjectURI(String objectURI) { + this.objectURI = objectURI; + } + + public String getObjectType() { + return objectType; + } + + public void setObjectType(String objectType) { + this.objectType = objectType; + } + + public String getDepth() { + return depth; + } + + public void setDepth(String depth) { + this.depth = depth; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public void addChildren(String key, String value) { + this.children.put(key, value); + } + + public HashMap getChildren() { + return this.children; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Method.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Method.java new file mode 100644 index 000000000..cb701551e --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Method.java @@ -0,0 +1,21 @@ +//********************************************************************************************** +// Method.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 17 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 17 2017 +// Subject: Represents the instance of definition view +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +public class Method extends InstanceDefinition { + public Method() { + + } + public Method(String uri) { + super(uri); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/OntologyReference.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/OntologyReference.java new file mode 100644 index 000000000..1823a1edb --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/OntologyReference.java @@ -0,0 +1,56 @@ +//********************************************************************************************** +// OntologyReference.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 15 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 15 2017 +// Subject: Represents the reference to an ontology view +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +import io.swagger.annotations.ApiModelProperty; + +public class OntologyReference { + /** + * @param property ex. skos:exactMatch + * @param object ex. http://www.cropontology.org/rdf/CO_715:0000139 + * @param seeAlso ex. http://www.cropontology.org/ontology/CO_715/ + */ + private String property; + private String object; + private String seeAlso; + + public OntologyReference() { + + } + + @ApiModelProperty(example = "http://www.w3.org/2004/02/skos/core#closeMatch") + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + + @ApiModelProperty(example = "http://www.cropontology.org/rdf/CO_715:0000139") + public String getObject() { + return object; + } + + public void setObject(String object) { + this.object = object; + } + + @ApiModelProperty(example = "http://www.cropontology.org/ontology/CO_715/") + public String getSeeAlso() { + return seeAlso; + } + + public void setSeeAlso(String seeAlso) { + this.seeAlso = seeAlso; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Phenotype.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Phenotype.java new file mode 100644 index 000000000..a929b43d0 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Phenotype.java @@ -0,0 +1,61 @@ +//********************************************************************************************** +// Phenotype.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: September 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: September, 14 2017 +// Subject: XX +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +import java.util.ArrayList; + +public class Phenotype { + private String variableURI; + private String experiment; + private Provenance provenance; + private ArrayList data = new ArrayList<>(); + + public Phenotype() { + + } + + public String getVariableURI() { + return variableURI; + } + + public void setVariableURI(String variableURI) { + this.variableURI = variableURI; + } + + public Provenance getProvenance() { + return provenance; + } + + public void setProvenance(Provenance provenance) { + this.provenance = provenance; + } + + public ArrayList getData() { + return data; + } + + public void setData(ArrayList data) { + this.data = data; + } + + public void addData(Data d) { + data.add(d); + } + + public String getExperiment() { + return experiment; + } + + public void setExperiment(String experiment) { + this.experiment = experiment; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Project.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Project.java new file mode 100644 index 000000000..df932b293 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Project.java @@ -0,0 +1,173 @@ +//********************************************************************************************** +// Project.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: March 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: March, 2017 +// Subject: Represents the project +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +import java.util.ArrayList; + +public class Project { + private String uri; + private String name; + private String acronyme; + private String subprojectType; + private String financialSupport; + private String financialName; + private String dateStart; + private String dateEnd; + private String keywords; + private String description; + private String objective; + private String parentProject; + private String website; + + private ArrayList contacts = new ArrayList<>(); + + public Project() { + + } + + public Project(String uri) { + this.uri = uri; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAcronyme() { + return acronyme; + } + + public void setAcronyme(String acronyme) { + this.acronyme = acronyme; + } + + public String getSubprojectType() { + return subprojectType; + } + + public void setSubprojectType(String subprojectType) { + this.subprojectType = subprojectType; + } + + public String getFinancialSupport() { + return financialSupport; + } + + public void setFinancialSupport(String financialSupport) { + this.financialSupport = financialSupport; + } + + public String getFinancialName() { + return financialName; + } + + public void setFinancialName(String financialName) { + this.financialName = financialName; + } + + public String getDateStart() { + return dateStart; + } + + public void setDateStart(String dateStart) { + this.dateStart = dateStart; + } + + public String getDateEnd() { + return dateEnd; + } + + public void setDateEnd(String dateEnd) { + this.dateEnd = dateEnd; + } + + public String getKeywords() { + return keywords; + } + + public void setKeywords(String keywords) { + this.keywords = keywords; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getObjective() { + return objective; + } + + public void setObjective(String objective) { + this.objective = objective; + } + + public String getParentProject() { + return parentProject; + } + + public void setParentProject(String parentProject) { + this.parentProject = parentProject; + } + + public String getWebsite() { + return website; + } + + public void setWebsite(String website) { + this.website = website; + } + + public ArrayList getContacts() { + return contacts; + } + + public void addContact(Contact contact) { + this.contacts.add(contact); + } + + /** + * Compare les deux projets (compare les attributs un à un) + * @param project Project à comparer à this + * @return true s'ils sont les mêmes, false sinon + */ + public boolean equals(Project project) { + return this.uri.equals(project.uri) + && this.name.equals(project.name) + && this.acronyme.equals(project.acronyme) + && this.subprojectType.equals(project.subprojectType) + && this.financialSupport.equals(project.financialSupport) + && this.financialName.equals(project.financialName) + && this.dateStart.equals(project.dateStart) + && this.dateEnd.equals(project.dateEnd) + && this.keywords.equals(project.keywords) + && this.description.equals(project.description) + && this.objective.equals(project.objective) + && this.parentProject.equals(project.parentProject) + && this.website.equals(project.website); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Property.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Property.java new file mode 100644 index 000000000..f7a472dcb --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Property.java @@ -0,0 +1,53 @@ +//********************************************************************************************** +// Property.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: September 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: September, 5 2017 +// Subject: Represents the view of Properties (used in Agronomical Object). +// Corresponds to data which will be saved in the triplestore +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +public class Property { + + /** + * @param typeProperty type de la propriété (ex. http://www.phenome-fppn.fr/vocabulary/2017#Variety) + * null si c'est non typé (String)) + * @param relation nom de la relation (ex. http://www.phenome-fppn.fr/vocabulary/2017#fromVariety) + * @param value valeur (ex. plot alias) + */ + private String typeProperty; + private String relation; + private String value; + + public Property() { + } + + public String getTypeProperty() { + return typeProperty; + } + + public void setTypeProperty(String typeProperty) { + this.typeProperty = typeProperty; + } + + public String getRelation() { + return relation; + } + + public void setRelation(String relation) { + this.relation = relation; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Provenance.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Provenance.java new file mode 100644 index 000000000..203be409f --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Provenance.java @@ -0,0 +1,73 @@ +//********************************************************************************************** +// Provenance.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: September 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 4 2017 (ajout de l'attribut wasGeneratedBy) +// Subject: Represents the data provenance view +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +import java.util.ArrayList; + +public class Provenance { + private String uri; + private String creationDate; + private WasGeneratedBy wasGeneratedBy; + private ArrayList documentsUris = new ArrayList<>(); + + public Provenance() { + + } + + public Provenance(Provenance prov) { + uri = prov.getUri(); + creationDate = prov.getCreationDate(); + wasGeneratedBy = prov.getWasGeneratedBy(); + + if (prov.getDocumentsUris() != null) { + for (String docUri : prov.getDocumentsUris()) { + documentsUris.add(docUri); + } + } + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getCreationDate() { + return creationDate; + } + + public void setCreationDate(String creationDate) { + this.creationDate = creationDate; + } + + public WasGeneratedBy getWasGeneratedBy() { + return wasGeneratedBy; + } + + public void setWasGeneratedBy(WasGeneratedBy wasGeneratedBy) { + this.wasGeneratedBy = wasGeneratedBy; + } + + public ArrayList getDocumentsUris() { + return documentsUris; + } + + public void setDocumentsUris(ArrayList documentsUris) { + this.documentsUris = documentsUris; + } + + public void addDocumentUri(String documentUri) { + this.documentsUris.add(uri); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Trait.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Trait.java new file mode 100644 index 000000000..9a675446a --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Trait.java @@ -0,0 +1,21 @@ +//********************************************************************************************** +// Trait.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 17 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 17 2017 +// Subject: Represents the instance of definition view +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +public class Trait extends InstanceDefinition { + public Trait() { + + } + public Trait(String uri) { + super(uri); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Unit.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Unit.java new file mode 100644 index 000000000..3d4f01d02 --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Unit.java @@ -0,0 +1,22 @@ +//********************************************************************************************** +// Unit.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 18 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 18 2017 +// Subject: Represents the instance of definition view +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +public class Unit extends InstanceDefinition { + public Unit() { + + } + + public Unit(String uri) { + super(uri); + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Variable.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Variable.java new file mode 100644 index 000000000..99277d52f --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/Variable.java @@ -0,0 +1,55 @@ +//********************************************************************************************** +// Variable.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: November, 16 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: November, 16 2017 +// Subject: Represents the instance of definition view +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +public class Variable extends InstanceDefinition { + + /** + * @attribute trait le trait + * @attribute method la méthode + * @attribute unit l'unité + */ + protected Trait trait; + protected Method method; + protected Unit unit; + + public Variable() { + } + + public Variable(String uri) { + super(uri); + } + + public Trait getTrait() { + return trait; + } + + public void setTrait(Trait trait) { + this.trait = trait; + } + + public Method getMethod() { + return method; + } + + public void setMethod(Method method) { + this.method = method; + } + + public Unit getUnit() { + return unit; + } + + public void setUnit(Unit unit) { + this.unit = unit; + } +} diff --git a/phis2-ws/src/main/java/phis2ws/service/view/model/phis/WasGeneratedBy.java b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/WasGeneratedBy.java new file mode 100644 index 000000000..fa43703aa --- /dev/null +++ b/phis2-ws/src/main/java/phis2ws/service/view/model/phis/WasGeneratedBy.java @@ -0,0 +1,37 @@ +//********************************************************************************************** +// WasGeneratedBy.java +// +// Author(s): Morgane VIDAL +// PHIS-SILEX version 1.0 +// Copyright © - INRA - 2017 +// Creation date: October 2017 +// Contact: morgane.vidal@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +// Last modification date: October, 4 2017 +// Subject: Represents the data wasGeneratedBy view +//*********************************************************************************************** +package phis2ws.service.view.model.phis; + +public class WasGeneratedBy { + private String wasGeneratedBy; + private String wasGeneratedByDescription; + + public WasGeneratedBy() { + + } + + public String getWasGeneratedBy() { + return wasGeneratedBy; + } + + public void setWasGeneratedBy(String wasGeneratedBy) { + this.wasGeneratedBy = wasGeneratedBy; + } + + public String getWasGeneratedByDescription() { + return wasGeneratedByDescription; + } + + public void setWasGeneratedByDescription(String wasGeneratedByDescription) { + this.wasGeneratedByDescription = wasGeneratedByDescription; + } +} diff --git a/phis2-ws/src/main/resources/logback.xml b/phis2-ws/src/main/resources/logback.xml new file mode 100644 index 000000000..7a9d80852 --- /dev/null +++ b/phis2-ws/src/main/resources/logback.xml @@ -0,0 +1,138 @@ + + + + + + + + ${logPattern} + + + + + + INFO + ACCEPT + DENY + + ${logDirectory}/phenomeApiInfo.log + + 30 + ${logDirectory}/phenomeApiInfo-%d{yyyy-MM-dd}.log.zip + + + ${logPattern} + + + + + + + DEBUG + ACCEPT + DENY + + ${logDirectory}/phenomeApiDebug.log + + 30 + ${logDirectory}/phenomeApiDebug-%d{yyyy-MM-dd}.log.zip + + + ${logPattern} + + + + + + + ERROR + ACCEPT + DENY + + ${logDirectory}/phenomeApiError.log + + + 30 + ${logDirectory}/phenomeApiError-%d{yyyy-MM-dd}.log.zip + + + ${logPattern} + + + + + + + + return (logger.contains("DaoPhisBrapi") || logger.contains("DaoEnvironment") || logger.contains("SQLDAO")); + + DENY + ACCEPT + + + TRACE + ACCEPT + DENY + + ${logDirectory}/phenomeApiDB.log + + ${logDirectory}/phenomeApiDB-%i.log.zip + 1 + 10 + + + 200MB + + + ${logPattern} + + + + + + + + return logger.contains("DaoSesame"); + + DENY + ACCEPT + + + TRACE + ACCEPT + DENY + + ${logDirectory}/phenomeApiSPARQL.log + + ${logDirectory}/phenomeApiSPARQL-%i.log.zip + 1 + 10 + + + 200MB + + + ${logPattern} + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/phis2-ws/src/main/resources/mongodb_nosql_config.properties b/phis2-ws/src/main/resources/mongodb_nosql_config.properties new file mode 100644 index 000000000..1f3fa00e8 --- /dev/null +++ b/phis2-ws/src/main/resources/mongodb_nosql_config.properties @@ -0,0 +1,13 @@ +############################ +## Mongo Configuration +############################ +driver=org.mongodb.driver +url=mongodb://127.0.0.1:27017 +db=dbname + +############################ +## Collection +############################ +documents=documents +provenance=provenance +data=data \ No newline at end of file diff --git a/phis2-ws/src/main/resources/phis_sql_config.properties b/phis2-ws/src/main/resources/phis_sql_config.properties new file mode 100644 index 000000000..1c83fc935 --- /dev/null +++ b/phis2-ws/src/main/resources/phis_sql_config.properties @@ -0,0 +1,32 @@ +driver=org.postgresql.Driver +url=jdbc:postgresql://127.0.0.1:5432/database +username=username +password=password + +############################ +## Optional Properties manager configuration +## see https://tomcat.apache.org/tomcat-8.0-doc/jdbc-pool.html +## see getSQLPoolDataSourceProperties() in PropertiesFileManager.class +############################ +testWhileIdle=true +testOnBorrow=true +testOnReturn=false +validationQuery=SELECT 1 +validationInterval=30000 +timeBetweenEvictionRunsMillis=10000 +# Max connexion to the database +maxActive=3 +# Instead of multiple closing and opening connection, connexions are keeped open +minIdle=1 +maxIdle=3 +maxWait=20000 +initialSize=1 +# Time before disconnect +suspectTimeout=60 +removeAbandonedTimeout=60 +removeAbandoned=true +logAbandoned=true +minEvictableIdleTimeMillis=30000 +jmxEnabled=true +maxAge=3000 +jdbcInterceptors=org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer \ No newline at end of file diff --git a/phis2-ws/src/main/resources/service.properties b/phis2-ws/src/main/resources/service.properties new file mode 100644 index 000000000..9223035c8 --- /dev/null +++ b/phis2-ws/src/main/resources/service.properties @@ -0,0 +1,78 @@ +################################################################################ +#Configuration des filtres sur les requ\u00eates +################################################################################ +#Taille maximale pour l'envoi de donn\u00e9es par le service environment +#2MB=2097152 octets +#4MB=4194304 octets +environmentPostDataSize=4194304 +#Limit de taille des \u00e9l\u00e9ments pr\u00e9sents sur une page +pageSizeMax=150000 +mongoPageSizeMax=1000000 +################################################################################ +###LOGS +################################################################################ +#Chemin de creation du dossier des fichiers de log +#Le r\u00e9pertoire est directement cr\u00e9e lors du d\u00e9marrage du WS +#@see InitDestroyContextListener +logDirectory=/home/tomcat/phis2ws/logs + +#Format du message de Log sauvegard\u00e9 +##DEV +logPattern=%d{dd MMM yyyy;HH:mm:ss.SSS} Class %C line %L %M [%thread] %-5level %logger{36} - %msg%n \u200b +##PROD +#logPattern=%d{dd MMM yyyy;HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + +################################################################################ +###AUTHENTIFICATION +################################################################################ +# Gnpis publicKeyFileName +gnpisPublicKeyFileName=GnpIS-JWT-public-key +#1200000 secondes par d\u00e9faut sessionTime=1200000 +#Modification pour avoir le temps en seconde +sessionTime=1200000 + +################################################################################ +###CHEMIN D'ACCES AU WEB SERVICE +################################################################################ +host=127.0.0.1:8084 +basePath=/phis2ws/rest + +################################################################################ +###DOCUMENTATION DU WEB SERVICE +################################################################################ +webAppHost=127.0.0.1 +webAppPort=8084 + +#Nom de l'API +webAppApiDocsName=phis2ws + + +################################################################################ +##Chemin de base pour les ressources dans la documentation +################################################################################ +webAppApiBasePath=/phis2ws/resources + +################################################################################ +#Envoi de fichier +################################################################################ +#Temps d'attente pour l'envoi de fichier +#waitingFileTime=50, 50 sec +waitingFileTime=50000 +uploadFileServerPort=22 +# Adresse de sauvegarde des donn\u00e9es +uploadFileServerIP=127.0.0.1 +uploadFileServerUsername=username +uploadFileServerPassword=password +uploadFileServerDirectory=/home/phis2ws/documents/instance +layerFileServerDirectory=/var/www/html/layers +layerFileServerAddress=http://127.0.0.1/layers + +################################################################################ +#Configuration pour la presentation d'informations +################################################################################ +documentsServerWebBasePath=http://web.supagro.inra.fr/phis + +################################################################################ +#URIs +################################################################################ +baseURI=http://www.phenome-fppn.fr/platform/ diff --git a/phis2-ws/src/main/resources/sesame_rdf_config.properties b/phis2-ws/src/main/resources/sesame_rdf_config.properties new file mode 100644 index 000000000..9bf5c39ee --- /dev/null +++ b/phis2-ws/src/main/resources/sesame_rdf_config.properties @@ -0,0 +1,3 @@ +sesameServer=http://127.0.0.1:8084/rdf4j-server/ +platform=platform +repositoryID=repository diff --git a/phis2-ws/src/main/webapp/META-INF/context.xml b/phis2-ws/src/main/webapp/META-INF/context.xml new file mode 100644 index 000000000..019f63e14 --- /dev/null +++ b/phis2-ws/src/main/webapp/META-INF/context.xml @@ -0,0 +1,2 @@ + + diff --git a/phis2-ws/src/main/webapp/WEB-INF/web.xml b/phis2-ws/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..6058a3670 --- /dev/null +++ b/phis2-ws/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,12 @@ + + + + + + 30 + + + diff --git a/phis2-ws/src/main/webapp/api-docs/css/print.css b/phis2-ws/src/main/webapp/api-docs/css/print.css new file mode 100644 index 000000000..d4c1e7045 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/css/print.css @@ -0,0 +1,1362 @@ +/* Original style from softwaremaniacs.org (c) Ivan Sagalaev */ +.swagger-section pre code { + display: block; + padding: 0.5em; + background: #F0F0F0; +} +.swagger-section pre code, +.swagger-section pre .subst, +.swagger-section pre .tag .title, +.swagger-section pre .lisp .title, +.swagger-section pre .clojure .built_in, +.swagger-section pre .nginx .title { + color: black; +} +.swagger-section pre .string, +.swagger-section pre .title, +.swagger-section pre .constant, +.swagger-section pre .parent, +.swagger-section pre .tag .value, +.swagger-section pre .rules .value, +.swagger-section pre .rules .value .number, +.swagger-section pre .preprocessor, +.swagger-section pre .ruby .symbol, +.swagger-section pre .ruby .symbol .string, +.swagger-section pre .aggregate, +.swagger-section pre .template_tag, +.swagger-section pre .django .variable, +.swagger-section pre .smalltalk .class, +.swagger-section pre .addition, +.swagger-section pre .flow, +.swagger-section pre .stream, +.swagger-section pre .bash .variable, +.swagger-section pre .apache .tag, +.swagger-section pre .apache .cbracket, +.swagger-section pre .tex .command, +.swagger-section pre .tex .special, +.swagger-section pre .erlang_repl .function_or_atom, +.swagger-section pre .markdown .header { + color: #800; +} +.swagger-section pre .comment, +.swagger-section pre .annotation, +.swagger-section pre .template_comment, +.swagger-section pre .diff .header, +.swagger-section pre .chunk, +.swagger-section pre .markdown .blockquote { + color: #888; +} +.swagger-section pre .number, +.swagger-section pre .date, +.swagger-section pre .regexp, +.swagger-section pre .literal, +.swagger-section pre .smalltalk .symbol, +.swagger-section pre .smalltalk .char, +.swagger-section pre .go .constant, +.swagger-section pre .change, +.swagger-section pre .markdown .bullet, +.swagger-section pre .markdown .link_url { + color: #080; +} +.swagger-section pre .label, +.swagger-section pre .javadoc, +.swagger-section pre .ruby .string, +.swagger-section pre .decorator, +.swagger-section pre .filter .argument, +.swagger-section pre .localvars, +.swagger-section pre .array, +.swagger-section pre .attr_selector, +.swagger-section pre .important, +.swagger-section pre .pseudo, +.swagger-section pre .pi, +.swagger-section pre .doctype, +.swagger-section pre .deletion, +.swagger-section pre .envvar, +.swagger-section pre .shebang, +.swagger-section pre .apache .sqbracket, +.swagger-section pre .nginx .built_in, +.swagger-section pre .tex .formula, +.swagger-section pre .erlang_repl .reserved, +.swagger-section pre .prompt, +.swagger-section pre .markdown .link_label, +.swagger-section pre .vhdl .attribute, +.swagger-section pre .clojure .attribute, +.swagger-section pre .coffeescript .property { + color: #88F; +} +.swagger-section pre .keyword, +.swagger-section pre .id, +.swagger-section pre .phpdoc, +.swagger-section pre .title, +.swagger-section pre .built_in, +.swagger-section pre .aggregate, +.swagger-section pre .css .tag, +.swagger-section pre .javadoctag, +.swagger-section pre .phpdoc, +.swagger-section pre .yardoctag, +.swagger-section pre .smalltalk .class, +.swagger-section pre .winutils, +.swagger-section pre .bash .variable, +.swagger-section pre .apache .tag, +.swagger-section pre .go .typename, +.swagger-section pre .tex .command, +.swagger-section pre .markdown .strong, +.swagger-section pre .request, +.swagger-section pre .status { + font-weight: bold; +} +.swagger-section pre .markdown .emphasis { + font-style: italic; +} +.swagger-section pre .nginx .built_in { + font-weight: normal; +} +.swagger-section pre .coffeescript .javascript, +.swagger-section pre .javascript .xml, +.swagger-section pre .tex .formula, +.swagger-section pre .xml .javascript, +.swagger-section pre .xml .vbscript, +.swagger-section pre .xml .css, +.swagger-section pre .xml .cdata { + opacity: 0.5; +} +.swagger-section .hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #F0F0F0; +} +.swagger-section .hljs, +.swagger-section .hljs-subst { + color: #444; +} +.swagger-section .hljs-keyword, +.swagger-section .hljs-attribute, +.swagger-section .hljs-selector-tag, +.swagger-section .hljs-meta-keyword, +.swagger-section .hljs-doctag, +.swagger-section .hljs-name { + font-weight: bold; +} +.swagger-section .hljs-built_in, +.swagger-section .hljs-literal, +.swagger-section .hljs-bullet, +.swagger-section .hljs-code, +.swagger-section .hljs-addition { + color: #1F811F; +} +.swagger-section .hljs-regexp, +.swagger-section .hljs-symbol, +.swagger-section .hljs-variable, +.swagger-section .hljs-template-variable, +.swagger-section .hljs-link, +.swagger-section .hljs-selector-attr, +.swagger-section .hljs-selector-pseudo { + color: #BC6060; +} +.swagger-section .hljs-type, +.swagger-section .hljs-string, +.swagger-section .hljs-number, +.swagger-section .hljs-selector-id, +.swagger-section .hljs-selector-class, +.swagger-section .hljs-quote, +.swagger-section .hljs-template-tag, +.swagger-section .hljs-deletion { + color: #880000; +} +.swagger-section .hljs-title, +.swagger-section .hljs-section { + color: #880000; + font-weight: bold; +} +.swagger-section .hljs-comment { + color: #888888; +} +.swagger-section .hljs-meta { + color: #2B6EA1; +} +.swagger-section .hljs-emphasis { + font-style: italic; +} +.swagger-section .hljs-strong { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap { + line-height: 1; + font-family: "Droid Sans", sans-serif; + min-width: 760px; + max-width: 960px; + margin-left: auto; + margin-right: auto; + /* JSONEditor specific styling */ +} +.swagger-section .swagger-ui-wrap b, +.swagger-section .swagger-ui-wrap strong { + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap q, +.swagger-section .swagger-ui-wrap blockquote { + quotes: none; +} +.swagger-section .swagger-ui-wrap p { + line-height: 1.4em; + padding: 0 0 10px; + color: #333333; +} +.swagger-section .swagger-ui-wrap q:before, +.swagger-section .swagger-ui-wrap q:after, +.swagger-section .swagger-ui-wrap blockquote:before, +.swagger-section .swagger-ui-wrap blockquote:after { + content: none; +} +.swagger-section .swagger-ui-wrap .heading_with_menu h1, +.swagger-section .swagger-ui-wrap .heading_with_menu h2, +.swagger-section .swagger-ui-wrap .heading_with_menu h3, +.swagger-section .swagger-ui-wrap .heading_with_menu h4, +.swagger-section .swagger-ui-wrap .heading_with_menu h5, +.swagger-section .swagger-ui-wrap .heading_with_menu h6 { + display: block; + clear: none; + float: left; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + width: 60%; +} +.swagger-section .swagger-ui-wrap table { + border-collapse: collapse; + border-spacing: 0; +} +.swagger-section .swagger-ui-wrap table thead tr th { + padding: 5px; + font-size: 0.9em; + color: #666666; + border-bottom: 1px solid #999999; +} +.swagger-section .swagger-ui-wrap table tbody tr:last-child td { + border-bottom: none; +} +.swagger-section .swagger-ui-wrap table tbody tr.offset { + background-color: #f0f0f0; +} +.swagger-section .swagger-ui-wrap table tbody tr td { + padding: 6px; + font-size: 0.9em; + border-bottom: 1px solid #cccccc; + vertical-align: top; + line-height: 1.3em; +} +.swagger-section .swagger-ui-wrap ol { + margin: 0px 0 10px; + padding: 0 0 0 18px; + list-style-type: decimal; +} +.swagger-section .swagger-ui-wrap ol li { + padding: 5px 0px; + font-size: 0.9em; + color: #333333; +} +.swagger-section .swagger-ui-wrap ol, +.swagger-section .swagger-ui-wrap ul { + list-style: none; +} +.swagger-section .swagger-ui-wrap h1 a, +.swagger-section .swagger-ui-wrap h2 a, +.swagger-section .swagger-ui-wrap h3 a, +.swagger-section .swagger-ui-wrap h4 a, +.swagger-section .swagger-ui-wrap h5 a, +.swagger-section .swagger-ui-wrap h6 a { + text-decoration: none; +} +.swagger-section .swagger-ui-wrap h1 a:hover, +.swagger-section .swagger-ui-wrap h2 a:hover, +.swagger-section .swagger-ui-wrap h3 a:hover, +.swagger-section .swagger-ui-wrap h4 a:hover, +.swagger-section .swagger-ui-wrap h5 a:hover, +.swagger-section .swagger-ui-wrap h6 a:hover { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap h1 span.divider, +.swagger-section .swagger-ui-wrap h2 span.divider, +.swagger-section .swagger-ui-wrap h3 span.divider, +.swagger-section .swagger-ui-wrap h4 span.divider, +.swagger-section .swagger-ui-wrap h5 span.divider, +.swagger-section .swagger-ui-wrap h6 span.divider { + color: #aaaaaa; +} +.swagger-section .swagger-ui-wrap a { + color: #547f00; +} +.swagger-section .swagger-ui-wrap a img { + border: none; +} +.swagger-section .swagger-ui-wrap article, +.swagger-section .swagger-ui-wrap aside, +.swagger-section .swagger-ui-wrap details, +.swagger-section .swagger-ui-wrap figcaption, +.swagger-section .swagger-ui-wrap figure, +.swagger-section .swagger-ui-wrap footer, +.swagger-section .swagger-ui-wrap header, +.swagger-section .swagger-ui-wrap hgroup, +.swagger-section .swagger-ui-wrap menu, +.swagger-section .swagger-ui-wrap nav, +.swagger-section .swagger-ui-wrap section, +.swagger-section .swagger-ui-wrap summary { + display: block; +} +.swagger-section .swagger-ui-wrap pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #fcf6db; + border: 1px solid #e5e0c6; + padding: 10px; +} +.swagger-section .swagger-ui-wrap pre code { + line-height: 1.6em; + background: none; +} +.swagger-section .swagger-ui-wrap .content > .content-type > div > label { + clear: both; + display: block; + color: #0F6AB4; + font-size: 1.1em; + margin: 0; + padding: 15px 0 5px; +} +.swagger-section .swagger-ui-wrap .content pre { + font-size: 12px; + margin-top: 5px; + padding: 5px; +} +.swagger-section .swagger-ui-wrap .icon-btn { + cursor: pointer; +} +.swagger-section .swagger-ui-wrap .info_title { + padding-bottom: 10px; + font-weight: bold; + font-size: 25px; +} +.swagger-section .swagger-ui-wrap .footer { + margin-top: 20px; +} +.swagger-section .swagger-ui-wrap p.big, +.swagger-section .swagger-ui-wrap div.big p { + font-size: 1em; + margin-bottom: 10px; +} +.swagger-section .swagger-ui-wrap form.fullwidth ol li.string input, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.url input, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.text textarea, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.numeric input { + width: 500px !important; +} +.swagger-section .swagger-ui-wrap .info_license { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_tos { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .message-fail { + color: #cc0000; +} +.swagger-section .swagger-ui-wrap .info_url { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_email { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_name { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_description { + padding-bottom: 10px; + font-size: 15px; +} +.swagger-section .swagger-ui-wrap .markdown ol li, +.swagger-section .swagger-ui-wrap .markdown ul li { + padding: 3px 0px; + line-height: 1.4em; + color: #333333; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input { + display: block; + padding: 4px; + width: auto; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input.title, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input.title, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input.title { + font-size: 1.3em; +} +.swagger-section .swagger-ui-wrap table.fullwidth { + width: 100%; +} +.swagger-section .swagger-ui-wrap .model-signature { + font-family: "Droid Sans", sans-serif; + font-size: 1em; + line-height: 1.5em; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav a { + text-decoration: none; + color: #AAA; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav a:hover { + text-decoration: underline; + color: black; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav .selected { + color: black; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap .model-signature .propType { + color: #5555aa; +} +.swagger-section .swagger-ui-wrap .model-signature pre:hover { + background-color: #ffffdd; +} +.swagger-section .swagger-ui-wrap .model-signature pre { + font-size: .85em; + line-height: 1.2em; + overflow: auto; + max-height: 200px; + cursor: pointer; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav { + display: block; + min-width: 230px; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li:last-child { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li { + float: left; + margin: 0 5px 5px 0; + padding: 2px 5px 2px 0; + border-right: 1px solid #ddd; +} +.swagger-section .swagger-ui-wrap .model-signature .propOpt { + color: #555; +} +.swagger-section .swagger-ui-wrap .model-signature .snippet small { + font-size: 0.75em; +} +.swagger-section .swagger-ui-wrap .model-signature .propOptKey { + font-style: italic; +} +.swagger-section .swagger-ui-wrap .model-signature .description .strong { + font-weight: bold; + color: #000; + font-size: .9em; +} +.swagger-section .swagger-ui-wrap .model-signature .description div { + font-size: 0.9em; + line-height: 1.5em; + margin-left: 1em; +} +.swagger-section .swagger-ui-wrap .model-signature .description .stronger { + font-weight: bold; + color: #000; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper { + border-spacing: 0; + position: absolute; + background-color: #ffffff; + border: 1px solid #bbbbbb; + display: none; + font-size: 11px; + max-width: 400px; + line-height: 30px; + color: black; + padding: 5px; + margin-left: 10px; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th { + text-align: center; + background-color: #eeeeee; + border: 1px solid #bbbbbb; + font-size: 11px; + color: #666666; + font-weight: bold; + padding: 5px; + line-height: 15px; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:first-child, +.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:last-child { + display: inline; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:not(:first-child):before { + display: block; + content: ''; +} +.swagger-section .swagger-ui-wrap .model-signature .description span:last-of-type.propDesc.markdown > p:only-child { + margin-right: -3px; +} +.swagger-section .swagger-ui-wrap .model-signature .propName { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-container { + clear: both; +} +.swagger-section .swagger-ui-wrap .body-textarea { + width: 300px; + height: 100px; + border: 1px solid #aaa; +} +.swagger-section .swagger-ui-wrap .markdown p code, +.swagger-section .swagger-ui-wrap .markdown li code { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #f0f0f0; + color: black; + padding: 1px 3px; +} +.swagger-section .swagger-ui-wrap .required { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .editor_holder { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap .editor_holder label { + font-weight: normal!important; + /* JSONEditor uses bold by default for all labels, we revert that back to normal to not give the impression that by default fields are required */ +} +.swagger-section .swagger-ui-wrap .editor_holder label.required { + font-weight: bold!important; +} +.swagger-section .swagger-ui-wrap input.parameter { + width: 300px; + border: 1px solid #aaa; +} +.swagger-section .swagger-ui-wrap h1 { + color: black; + font-size: 1.5em; + line-height: 1.3em; + padding: 10px 0 10px 0; + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .heading_with_menu { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap .heading_with_menu ul { + display: block; + clear: none; + float: right; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + margin-top: 10px; +} +.swagger-section .swagger-ui-wrap h2 { + color: black; + font-size: 1.3em; + padding: 10px 0 10px 0; +} +.swagger-section .swagger-ui-wrap h2 a { + color: black; +} +.swagger-section .swagger-ui-wrap h2 span.sub { + font-size: 0.7em; + color: #999999; + font-style: italic; +} +.swagger-section .swagger-ui-wrap h2 span.sub a { + color: #777777; +} +.swagger-section .swagger-ui-wrap span.weak { + color: #666666; +} +.swagger-section .swagger-ui-wrap .message-success { + color: #89BF04; +} +.swagger-section .swagger-ui-wrap caption, +.swagger-section .swagger-ui-wrap th, +.swagger-section .swagger-ui-wrap td { + text-align: left; + font-weight: normal; + vertical-align: middle; +} +.swagger-section .swagger-ui-wrap .code { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.text textarea { + font-family: "Droid Sans", sans-serif; + height: 250px; + padding: 4px; + display: block; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.select select { + display: block; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean label { + display: block; + float: left; + clear: none; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean input { + display: block; + float: left; + clear: none; + margin: 0 5px 0 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.required label { + color: black; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label { + display: block; + clear: both; + width: auto; + padding: 0 0 3px; + color: #666666; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label abbr { + padding-left: 3px; + color: #888888; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li p.inline-hints { + margin-left: 0; + font-style: italic; + font-size: 0.9em; + margin: 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.buttons { + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap span.blank, +.swagger-section .swagger-ui-wrap span.empty { + color: #888888; + font-style: italic; +} +.swagger-section .swagger-ui-wrap .markdown h3 { + color: #547f00; +} +.swagger-section .swagger-ui-wrap .markdown h4 { + color: #666666; +} +.swagger-section .swagger-ui-wrap .markdown pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #fcf6db; + border: 1px solid #e5e0c6; + padding: 10px; + margin: 0 0 10px 0; +} +.swagger-section .swagger-ui-wrap .markdown pre code { + line-height: 1.6em; + overflow: auto; +} +.swagger-section .swagger-ui-wrap div.gist { + margin: 20px 0 25px 0 !important; +} +.swagger-section .swagger-ui-wrap ul#resources { + font-family: "Droid Sans", sans-serif; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource { + border-bottom: 1px solid #dddddd; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading h2 a, +.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading h2 a { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading ul.options li a, +.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading ul.options li a { + color: #555555; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:last-child { + border-bottom: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading { + border: 1px solid transparent; + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options { + overflow: hidden; + padding: 0; + display: block; + clear: none; + float: right; + margin: 14px 10px 0 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li { + float: left; + clear: none; + margin: 0; + padding: 2px 10px; + border-right: 1px solid #dddddd; + color: #666666; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a { + color: #aaaaaa; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover { + text-decoration: underline; + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:active, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a.active { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.last { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 { + color: #999999; + padding-left: 0; + display: block; + clear: none; + float: left; + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a { + color: #999999; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation { + float: none; + clear: both; + overflow: hidden; + display: block; + margin: 0 0 10px; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading { + float: none; + clear: both; + overflow: hidden; + display: block; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 { + display: block; + clear: none; + float: left; + width: auto; + margin: 0; + padding: 0; + line-height: 1.1em; + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path { + padding-left: 10px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a { + color: black; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a.toggleOperation.deprecated { + text-decoration: line-through; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.http_method a { + text-transform: uppercase; + text-decoration: none; + color: white; + display: inline-block; + width: 50px; + font-size: 0.7em; + text-align: center; + padding: 7px 0 4px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + -o-border-radius: 2px; + -ms-border-radius: 2px; + -khtml-border-radius: 2px; + border-radius: 2px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span { + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options { + overflow: hidden; + padding: 0; + display: block; + clear: none; + float: right; + margin: 6px 10px 0 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li { + float: left; + clear: none; + margin: 0; + padding: 2px 10px; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a { + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content { + border-top: none; + padding: 10px; + -moz-border-radius-bottomleft: 6px; + -webkit-border-bottom-left-radius: 6px; + -o-border-bottom-left-radius: 6px; + -ms-border-bottom-left-radius: 6px; + -khtml-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -moz-border-radius-bottomright: 6px; + -webkit-border-bottom-right-radius: 6px; + -o-border-bottom-right-radius: 6px; + -ms-border-bottom-right-radius: 6px; + -khtml-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + margin: 0 0 20px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content h4 { + font-size: 1.1em; + margin: 0; + padding: 15px 0 5px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header a { + padding: 4px 0 0 10px; + display: inline-block; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit { + display: block; + clear: none; + float: left; + padding: 6px 8px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber { + background-image: url('../images/throbber.gif'); + width: 128px; + height: 16px; + display: block; + clear: none; + float: right; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error { + outline: 2px solid black; + outline-color: #cc0000; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form select[name='parameterContentType'] { + max-width: 300px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + padding: 10px; + font-size: 0.9em; + max-height: 400px; + overflow-y: auto; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading { + background-color: #f9f2e9; + border: 1px solid #f0e0ca; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a { + background-color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #f0e0ca; + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a { + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content { + background-color: #faf5ee; + border: 1px solid #f0e0ca; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 { + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content div.sandbox_header a { + color: #dcb67f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading { + background-color: #fcffcd; + border: 1px solid black; + border-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading h3 span.http_method a { + text-transform: uppercase; + background-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #ffd20f; + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li a { + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content { + background-color: #fcffcd; + border: 1px solid black; + border-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content h4 { + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content div.sandbox_header a { + color: #6fc992; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading { + background-color: #f5e8e8; + border: 1px solid #e8c6c7; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a { + text-transform: uppercase; + background-color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #e8c6c7; + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a { + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { + background-color: #f7eded; + border: 1px solid #e8c6c7; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 { + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content div.sandbox_header a { + color: #c8787a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading { + background-color: #e7f6ec; + border: 1px solid #c3e8d1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a { + background-color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3e8d1; + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a { + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content { + background-color: #ebf7f0; + border: 1px solid #c3e8d1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 { + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a { + color: #6fc992; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading { + background-color: #FCE9E3; + border: 1px solid #F5D5C3; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a { + background-color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #f0cecb; + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a { + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content { + background-color: #faf0ef; + border: 1px solid #f0cecb; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 { + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content div.sandbox_header a { + color: #dcb67f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading { + background-color: #e7f0f7; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a { + background-color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3d9ec; + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content div.sandbox_header a { + color: #6fa5d2; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading { + background-color: #e7f0f7; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a { + background-color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3d9ec; + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content div.sandbox_header a { + color: #6fa5d2; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { + border-top: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li.last { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:hover, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:active, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a.active { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap p#colophon { + margin: 0 15px 40px 15px; + padding: 10px 0; + font-size: 0.8em; + border-top: 1px solid #dddddd; + font-family: "Droid Sans", sans-serif; + color: #999999; + font-style: italic; +} +.swagger-section .swagger-ui-wrap p#colophon a { + text-decoration: none; + color: #547f00; +} +.swagger-section .swagger-ui-wrap h3 { + color: black; + font-size: 1.1em; + padding: 10px 0 10px 0; +} +.swagger-section .swagger-ui-wrap .markdown ol, +.swagger-section .swagger-ui-wrap .markdown ul { + font-family: "Droid Sans", sans-serif; + margin: 5px 0 10px; + padding: 0 0 0 18px; + list-style-type: disc; +} +.swagger-section .swagger-ui-wrap form.form_box { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; + padding: 10px; +} +.swagger-section .swagger-ui-wrap form.form_box label { + color: #0f6ab4 !important; +} +.swagger-section .swagger-ui-wrap form.form_box input[type=submit] { + display: block; + padding: 10px; +} +.swagger-section .swagger-ui-wrap form.form_box p.weak { + font-size: 0.8em; +} +.swagger-section .swagger-ui-wrap form.form_box p { + font-size: 0.9em; + padding: 0 0 15px; + color: #7e7b6d; +} +.swagger-section .swagger-ui-wrap form.form_box p a { + color: #646257; +} +.swagger-section .swagger-ui-wrap form.form_box p strong { + color: black; +} +.swagger-section .swagger-ui-wrap .operation-status td.markdown > p:last-child { + padding-bottom: 0; +} +.swagger-section .title { + font-style: bold; +} +.swagger-section .secondary_form { + display: none; +} +.swagger-section .main_image { + display: block; + margin-left: auto; + margin-right: auto; +} +.swagger-section .oauth_body { + margin-left: 100px; + margin-right: 100px; +} +.swagger-section .oauth_submit { + text-align: center; + display: inline-block; +} +.swagger-section .authorize-wrapper { + margin: 15px 0 10px; +} +.swagger-section .authorize-wrapper_operation { + float: right; +} +.swagger-section .authorize__btn:hover { + text-decoration: underline; + cursor: pointer; +} +.swagger-section .authorize__btn_operation:hover .authorize-scopes { + display: block; +} +.swagger-section .authorize-scopes { + position: absolute; + margin-top: 20px; + background: #FFF; + border: 1px solid #ccc; + border-radius: 5px; + display: none; + font-size: 13px; + max-width: 300px; + line-height: 30px; + color: black; + padding: 5px; +} +.swagger-section .authorize-scopes .authorize__scope { + text-decoration: none; +} +.swagger-section .authorize__btn_operation { + height: 18px; + vertical-align: middle; + display: inline-block; + background: url(../images/explorer_icons.png) no-repeat; +} +.swagger-section .authorize__btn_operation_login { + background-position: 0 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .authorize__btn_operation_logout { + background-position: -30px 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section #auth_container { + color: #fff; + display: inline-block; + border: none; + padding: 5px; + width: 87px; + height: 13px; +} +.swagger-section #auth_container .authorize__btn { + color: #fff; +} +.swagger-section .auth_container { + padding: 0 0 10px; + margin-bottom: 5px; + border-bottom: solid 1px #CCC; + font-size: 0.9em; +} +.swagger-section .auth_container .auth__title { + color: #547f00; + font-size: 1.2em; +} +.swagger-section .auth_container .basic_auth__label { + display: inline-block; + width: 60px; +} +.swagger-section .auth_container .auth__description { + color: #999999; + margin-bottom: 5px; +} +.swagger-section .auth_container .auth__button { + margin-top: 10px; + height: 30px; +} +.swagger-section .auth_container .key_auth__field { + margin: 5px 0; +} +.swagger-section .auth_container .key_auth__label { + display: inline-block; + width: 60px; +} +.swagger-section .api-popup-dialog { + position: absolute; + display: none; +} +.swagger-section .api-popup-dialog-wrapper { + z-index: 1000; + width: 500px; + background: #FFF; + padding: 20px; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 13px; + color: #777; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +.swagger-section .api-popup-dialog-shadow { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.2; + background-color: gray; + z-index: 900; +} +.swagger-section .api-popup-dialog .api-popup-title { + font-size: 24px; + padding: 10px 0; +} +.swagger-section .api-popup-dialog .api-popup-title { + font-size: 24px; + padding: 10px 0; +} +.swagger-section .api-popup-dialog .error-msg { + padding-left: 5px; + padding-bottom: 5px; +} +.swagger-section .api-popup-dialog .api-popup-content { + max-height: 500px; + overflow-y: auto; +} +.swagger-section .api-popup-dialog .api-popup-authbtn { + height: 30px; +} +.swagger-section .api-popup-dialog .api-popup-cancel { + height: 30px; +} +.swagger-section .api-popup-scopes { + padding: 10px 20px; +} +.swagger-section .api-popup-scopes li { + padding: 5px 0; + line-height: 20px; +} +.swagger-section .api-popup-scopes li input { + position: relative; + top: 2px; +} +.swagger-section .api-popup-scopes .api-scope-desc { + padding-left: 20px; + font-style: italic; +} +.swagger-section .api-popup-actions { + padding-top: 10px; +} +#header { + display: none; +} +.swagger-section .swagger-ui-wrap .model-signature pre { + max-height: none; +} +.swagger-section .swagger-ui-wrap .body-textarea { + width: 100px; +} +.swagger-section .swagger-ui-wrap input.parameter { + width: 100px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options { + display: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints { + display: block !important; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content { + display: block !important; +} diff --git a/phis2-ws/src/main/webapp/api-docs/css/reset.css b/phis2-ws/src/main/webapp/api-docs/css/reset.css new file mode 100644 index 000000000..b2b078943 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/css/reset.css @@ -0,0 +1,125 @@ +/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} +body { + line-height: 1; +} +ol, +ul { + list-style: none; +} +blockquote, +q { + quotes: none; +} +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/phis2-ws/src/main/webapp/api-docs/css/screen.css b/phis2-ws/src/main/webapp/api-docs/css/screen.css new file mode 100644 index 000000000..9d680e2d9 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/css/screen.css @@ -0,0 +1,1489 @@ +/* Original style from softwaremaniacs.org (c) Ivan Sagalaev */ +.swagger-section pre code { + display: block; + padding: 0.5em; + background: #F0F0F0; +} +.swagger-section pre code, +.swagger-section pre .subst, +.swagger-section pre .tag .title, +.swagger-section pre .lisp .title, +.swagger-section pre .clojure .built_in, +.swagger-section pre .nginx .title { + color: black; +} +.swagger-section pre .string, +.swagger-section pre .title, +.swagger-section pre .constant, +.swagger-section pre .parent, +.swagger-section pre .tag .value, +.swagger-section pre .rules .value, +.swagger-section pre .rules .value .number, +.swagger-section pre .preprocessor, +.swagger-section pre .ruby .symbol, +.swagger-section pre .ruby .symbol .string, +.swagger-section pre .aggregate, +.swagger-section pre .template_tag, +.swagger-section pre .django .variable, +.swagger-section pre .smalltalk .class, +.swagger-section pre .addition, +.swagger-section pre .flow, +.swagger-section pre .stream, +.swagger-section pre .bash .variable, +.swagger-section pre .apache .tag, +.swagger-section pre .apache .cbracket, +.swagger-section pre .tex .command, +.swagger-section pre .tex .special, +.swagger-section pre .erlang_repl .function_or_atom, +.swagger-section pre .markdown .header { + color: #800; +} +.swagger-section pre .comment, +.swagger-section pre .annotation, +.swagger-section pre .template_comment, +.swagger-section pre .diff .header, +.swagger-section pre .chunk, +.swagger-section pre .markdown .blockquote { + color: #888; +} +.swagger-section pre .number, +.swagger-section pre .date, +.swagger-section pre .regexp, +.swagger-section pre .literal, +.swagger-section pre .smalltalk .symbol, +.swagger-section pre .smalltalk .char, +.swagger-section pre .go .constant, +.swagger-section pre .change, +.swagger-section pre .markdown .bullet, +.swagger-section pre .markdown .link_url { + color: #080; +} +.swagger-section pre .label, +.swagger-section pre .javadoc, +.swagger-section pre .ruby .string, +.swagger-section pre .decorator, +.swagger-section pre .filter .argument, +.swagger-section pre .localvars, +.swagger-section pre .array, +.swagger-section pre .attr_selector, +.swagger-section pre .important, +.swagger-section pre .pseudo, +.swagger-section pre .pi, +.swagger-section pre .doctype, +.swagger-section pre .deletion, +.swagger-section pre .envvar, +.swagger-section pre .shebang, +.swagger-section pre .apache .sqbracket, +.swagger-section pre .nginx .built_in, +.swagger-section pre .tex .formula, +.swagger-section pre .erlang_repl .reserved, +.swagger-section pre .prompt, +.swagger-section pre .markdown .link_label, +.swagger-section pre .vhdl .attribute, +.swagger-section pre .clojure .attribute, +.swagger-section pre .coffeescript .property { + color: #88F; +} +.swagger-section pre .keyword, +.swagger-section pre .id, +.swagger-section pre .phpdoc, +.swagger-section pre .title, +.swagger-section pre .built_in, +.swagger-section pre .aggregate, +.swagger-section pre .css .tag, +.swagger-section pre .javadoctag, +.swagger-section pre .phpdoc, +.swagger-section pre .yardoctag, +.swagger-section pre .smalltalk .class, +.swagger-section pre .winutils, +.swagger-section pre .bash .variable, +.swagger-section pre .apache .tag, +.swagger-section pre .go .typename, +.swagger-section pre .tex .command, +.swagger-section pre .markdown .strong, +.swagger-section pre .request, +.swagger-section pre .status { + font-weight: bold; +} +.swagger-section pre .markdown .emphasis { + font-style: italic; +} +.swagger-section pre .nginx .built_in { + font-weight: normal; +} +.swagger-section pre .coffeescript .javascript, +.swagger-section pre .javascript .xml, +.swagger-section pre .tex .formula, +.swagger-section pre .xml .javascript, +.swagger-section pre .xml .vbscript, +.swagger-section pre .xml .css, +.swagger-section pre .xml .cdata { + opacity: 0.5; +} +.swagger-section .hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #F0F0F0; +} +.swagger-section .hljs, +.swagger-section .hljs-subst { + color: #444; +} +.swagger-section .hljs-keyword, +.swagger-section .hljs-attribute, +.swagger-section .hljs-selector-tag, +.swagger-section .hljs-meta-keyword, +.swagger-section .hljs-doctag, +.swagger-section .hljs-name { + font-weight: bold; +} +.swagger-section .hljs-built_in, +.swagger-section .hljs-literal, +.swagger-section .hljs-bullet, +.swagger-section .hljs-code, +.swagger-section .hljs-addition { + color: #1F811F; +} +.swagger-section .hljs-regexp, +.swagger-section .hljs-symbol, +.swagger-section .hljs-variable, +.swagger-section .hljs-template-variable, +.swagger-section .hljs-link, +.swagger-section .hljs-selector-attr, +.swagger-section .hljs-selector-pseudo { + color: #BC6060; +} +.swagger-section .hljs-type, +.swagger-section .hljs-string, +.swagger-section .hljs-number, +.swagger-section .hljs-selector-id, +.swagger-section .hljs-selector-class, +.swagger-section .hljs-quote, +.swagger-section .hljs-template-tag, +.swagger-section .hljs-deletion { + color: #880000; +} +.swagger-section .hljs-title, +.swagger-section .hljs-section { + color: #880000; + font-weight: bold; +} +.swagger-section .hljs-comment { + color: #888888; +} +.swagger-section .hljs-meta { + color: #2B6EA1; +} +.swagger-section .hljs-emphasis { + font-style: italic; +} +.swagger-section .hljs-strong { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap { + line-height: 1; + font-family: "Droid Sans", sans-serif; + min-width: 760px; + max-width: 960px; + margin-left: auto; + margin-right: auto; + /* JSONEditor specific styling */ +} +.swagger-section .swagger-ui-wrap b, +.swagger-section .swagger-ui-wrap strong { + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap q, +.swagger-section .swagger-ui-wrap blockquote { + quotes: none; +} +.swagger-section .swagger-ui-wrap p { + line-height: 1.4em; + padding: 0 0 10px; + color: #333333; +} +.swagger-section .swagger-ui-wrap q:before, +.swagger-section .swagger-ui-wrap q:after, +.swagger-section .swagger-ui-wrap blockquote:before, +.swagger-section .swagger-ui-wrap blockquote:after { + content: none; +} +.swagger-section .swagger-ui-wrap .heading_with_menu h1, +.swagger-section .swagger-ui-wrap .heading_with_menu h2, +.swagger-section .swagger-ui-wrap .heading_with_menu h3, +.swagger-section .swagger-ui-wrap .heading_with_menu h4, +.swagger-section .swagger-ui-wrap .heading_with_menu h5, +.swagger-section .swagger-ui-wrap .heading_with_menu h6 { + display: block; + clear: none; + float: left; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + width: 60%; +} +.swagger-section .swagger-ui-wrap table { + border-collapse: collapse; + border-spacing: 0; +} +.swagger-section .swagger-ui-wrap table thead tr th { + padding: 5px; + font-size: 0.9em; + color: #666666; + border-bottom: 1px solid #999999; +} +.swagger-section .swagger-ui-wrap table tbody tr:last-child td { + border-bottom: none; +} +.swagger-section .swagger-ui-wrap table tbody tr.offset { + background-color: #f0f0f0; +} +.swagger-section .swagger-ui-wrap table tbody tr td { + padding: 6px; + font-size: 0.9em; + border-bottom: 1px solid #cccccc; + vertical-align: top; + line-height: 1.3em; +} +.swagger-section .swagger-ui-wrap ol { + margin: 0px 0 10px; + padding: 0 0 0 18px; + list-style-type: decimal; +} +.swagger-section .swagger-ui-wrap ol li { + padding: 5px 0px; + font-size: 0.9em; + color: #333333; +} +.swagger-section .swagger-ui-wrap ol, +.swagger-section .swagger-ui-wrap ul { + list-style: none; +} +.swagger-section .swagger-ui-wrap h1 a, +.swagger-section .swagger-ui-wrap h2 a, +.swagger-section .swagger-ui-wrap h3 a, +.swagger-section .swagger-ui-wrap h4 a, +.swagger-section .swagger-ui-wrap h5 a, +.swagger-section .swagger-ui-wrap h6 a { + text-decoration: none; +} +.swagger-section .swagger-ui-wrap h1 a:hover, +.swagger-section .swagger-ui-wrap h2 a:hover, +.swagger-section .swagger-ui-wrap h3 a:hover, +.swagger-section .swagger-ui-wrap h4 a:hover, +.swagger-section .swagger-ui-wrap h5 a:hover, +.swagger-section .swagger-ui-wrap h6 a:hover { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap h1 span.divider, +.swagger-section .swagger-ui-wrap h2 span.divider, +.swagger-section .swagger-ui-wrap h3 span.divider, +.swagger-section .swagger-ui-wrap h4 span.divider, +.swagger-section .swagger-ui-wrap h5 span.divider, +.swagger-section .swagger-ui-wrap h6 span.divider { + color: #aaaaaa; +} +.swagger-section .swagger-ui-wrap a { + color: #547f00; +} +.swagger-section .swagger-ui-wrap a img { + border: none; +} +.swagger-section .swagger-ui-wrap article, +.swagger-section .swagger-ui-wrap aside, +.swagger-section .swagger-ui-wrap details, +.swagger-section .swagger-ui-wrap figcaption, +.swagger-section .swagger-ui-wrap figure, +.swagger-section .swagger-ui-wrap footer, +.swagger-section .swagger-ui-wrap header, +.swagger-section .swagger-ui-wrap hgroup, +.swagger-section .swagger-ui-wrap menu, +.swagger-section .swagger-ui-wrap nav, +.swagger-section .swagger-ui-wrap section, +.swagger-section .swagger-ui-wrap summary { + display: block; +} +.swagger-section .swagger-ui-wrap pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #fcf6db; + border: 1px solid #e5e0c6; + padding: 10px; +} +.swagger-section .swagger-ui-wrap pre code { + line-height: 1.6em; + background: none; +} +.swagger-section .swagger-ui-wrap .content > .content-type > div > label { + clear: both; + display: block; + color: #0F6AB4; + font-size: 1.1em; + margin: 0; + padding: 15px 0 5px; +} +.swagger-section .swagger-ui-wrap .content pre { + font-size: 12px; + margin-top: 5px; + padding: 5px; +} +.swagger-section .swagger-ui-wrap .icon-btn { + cursor: pointer; +} +.swagger-section .swagger-ui-wrap .info_title { + padding-bottom: 10px; + font-weight: bold; + font-size: 25px; +} +.swagger-section .swagger-ui-wrap .footer { + margin-top: 20px; +} +.swagger-section .swagger-ui-wrap p.big, +.swagger-section .swagger-ui-wrap div.big p { + font-size: 1em; + margin-bottom: 10px; +} +.swagger-section .swagger-ui-wrap form.fullwidth ol li.string input, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.url input, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.text textarea, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.numeric input { + width: 500px !important; +} +.swagger-section .swagger-ui-wrap .info_license { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_tos { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .message-fail { + color: #cc0000; +} +.swagger-section .swagger-ui-wrap .info_url { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_email { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_name { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_description { + padding-bottom: 10px; + font-size: 15px; +} +.swagger-section .swagger-ui-wrap .markdown ol li, +.swagger-section .swagger-ui-wrap .markdown ul li { + padding: 3px 0px; + line-height: 1.4em; + color: #333333; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input { + display: block; + padding: 4px; + width: auto; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input.title, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input.title, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input.title { + font-size: 1.3em; +} +.swagger-section .swagger-ui-wrap table.fullwidth { + width: 100%; +} +.swagger-section .swagger-ui-wrap .model-signature { + font-family: "Droid Sans", sans-serif; + font-size: 1em; + line-height: 1.5em; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav a { + text-decoration: none; + color: #AAA; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav a:hover { + text-decoration: underline; + color: black; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav .selected { + color: black; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap .model-signature .propType { + color: #5555aa; +} +.swagger-section .swagger-ui-wrap .model-signature pre:hover { + background-color: #ffffdd; +} +.swagger-section .swagger-ui-wrap .model-signature pre { + font-size: .85em; + line-height: 1.2em; + overflow: auto; + max-height: 200px; + cursor: pointer; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav { + display: block; + min-width: 230px; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li:last-child { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li { + float: left; + margin: 0 5px 5px 0; + padding: 2px 5px 2px 0; + border-right: 1px solid #ddd; +} +.swagger-section .swagger-ui-wrap .model-signature .propOpt { + color: #555; +} +.swagger-section .swagger-ui-wrap .model-signature .snippet small { + font-size: 0.75em; +} +.swagger-section .swagger-ui-wrap .model-signature .propOptKey { + font-style: italic; +} +.swagger-section .swagger-ui-wrap .model-signature .description .strong { + font-weight: bold; + color: #000; + font-size: .9em; +} +.swagger-section .swagger-ui-wrap .model-signature .description div { + font-size: 0.9em; + line-height: 1.5em; + margin-left: 1em; +} +.swagger-section .swagger-ui-wrap .model-signature .description .stronger { + font-weight: bold; + color: #000; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper { + border-spacing: 0; + position: absolute; + background-color: #ffffff; + border: 1px solid #bbbbbb; + display: none; + font-size: 11px; + max-width: 400px; + line-height: 30px; + color: black; + padding: 5px; + margin-left: 10px; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th { + text-align: center; + background-color: #eeeeee; + border: 1px solid #bbbbbb; + font-size: 11px; + color: #666666; + font-weight: bold; + padding: 5px; + line-height: 15px; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:first-child, +.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:last-child { + display: inline; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:not(:first-child):before { + display: block; + content: ''; +} +.swagger-section .swagger-ui-wrap .model-signature .description span:last-of-type.propDesc.markdown > p:only-child { + margin-right: -3px; +} +.swagger-section .swagger-ui-wrap .model-signature .propName { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-container { + clear: both; +} +.swagger-section .swagger-ui-wrap .body-textarea { + width: 300px; + height: 100px; + border: 1px solid #aaa; +} +.swagger-section .swagger-ui-wrap .markdown p code, +.swagger-section .swagger-ui-wrap .markdown li code { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #f0f0f0; + color: black; + padding: 1px 3px; +} +.swagger-section .swagger-ui-wrap .required { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .editor_holder { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap .editor_holder label { + font-weight: normal!important; + /* JSONEditor uses bold by default for all labels, we revert that back to normal to not give the impression that by default fields are required */ +} +.swagger-section .swagger-ui-wrap .editor_holder label.required { + font-weight: bold!important; +} +.swagger-section .swagger-ui-wrap input.parameter { + width: 300px; + border: 1px solid #aaa; +} +.swagger-section .swagger-ui-wrap h1 { + color: black; + font-size: 1.5em; + line-height: 1.3em; + padding: 10px 0 10px 0; + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .heading_with_menu { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap .heading_with_menu ul { + display: block; + clear: none; + float: right; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + margin-top: 10px; +} +.swagger-section .swagger-ui-wrap h2 { + color: black; + font-size: 1.3em; + padding: 10px 0 10px 0; +} +.swagger-section .swagger-ui-wrap h2 a { + color: black; +} +.swagger-section .swagger-ui-wrap h2 span.sub { + font-size: 0.7em; + color: #999999; + font-style: italic; +} +.swagger-section .swagger-ui-wrap h2 span.sub a { + color: #777777; +} +.swagger-section .swagger-ui-wrap span.weak { + color: #666666; +} +.swagger-section .swagger-ui-wrap .message-success { + color: #89BF04; +} +.swagger-section .swagger-ui-wrap caption, +.swagger-section .swagger-ui-wrap th, +.swagger-section .swagger-ui-wrap td { + text-align: left; + font-weight: normal; + vertical-align: middle; +} +.swagger-section .swagger-ui-wrap .code { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.text textarea { + font-family: "Droid Sans", sans-serif; + height: 250px; + padding: 4px; + display: block; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.select select { + display: block; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean label { + display: block; + float: left; + clear: none; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean input { + display: block; + float: left; + clear: none; + margin: 0 5px 0 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.required label { + color: black; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label { + display: block; + clear: both; + width: auto; + padding: 0 0 3px; + color: #666666; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label abbr { + padding-left: 3px; + color: #888888; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li p.inline-hints { + margin-left: 0; + font-style: italic; + font-size: 0.9em; + margin: 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.buttons { + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap span.blank, +.swagger-section .swagger-ui-wrap span.empty { + color: #888888; + font-style: italic; +} +.swagger-section .swagger-ui-wrap .markdown h3 { + color: #547f00; +} +.swagger-section .swagger-ui-wrap .markdown h4 { + color: #666666; +} +.swagger-section .swagger-ui-wrap .markdown pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #fcf6db; + border: 1px solid #e5e0c6; + padding: 10px; + margin: 0 0 10px 0; +} +.swagger-section .swagger-ui-wrap .markdown pre code { + line-height: 1.6em; + overflow: auto; +} +.swagger-section .swagger-ui-wrap div.gist { + margin: 20px 0 25px 0 !important; +} +.swagger-section .swagger-ui-wrap ul#resources { + font-family: "Droid Sans", sans-serif; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource { + border-bottom: 1px solid #dddddd; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading h2 a, +.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading h2 a { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading ul.options li a, +.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading ul.options li a { + color: #555555; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:last-child { + border-bottom: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading { + border: 1px solid transparent; + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options { + overflow: hidden; + padding: 0; + display: block; + clear: none; + float: right; + margin: 14px 10px 0 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li { + float: left; + clear: none; + margin: 0; + padding: 2px 10px; + border-right: 1px solid #dddddd; + color: #666666; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a { + color: #aaaaaa; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover { + text-decoration: underline; + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:active, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a.active { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.last { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 { + color: #999999; + padding-left: 0; + display: block; + clear: none; + float: left; + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a { + color: #999999; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation { + float: none; + clear: both; + overflow: hidden; + display: block; + margin: 0 0 10px; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading { + float: none; + clear: both; + overflow: hidden; + display: block; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 { + display: block; + clear: none; + float: left; + width: auto; + margin: 0; + padding: 0; + line-height: 1.1em; + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path { + padding-left: 10px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a { + color: black; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a.toggleOperation.deprecated { + text-decoration: line-through; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.http_method a { + text-transform: uppercase; + text-decoration: none; + color: white; + display: inline-block; + width: 50px; + font-size: 0.7em; + text-align: center; + padding: 7px 0 4px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + -o-border-radius: 2px; + -ms-border-radius: 2px; + -khtml-border-radius: 2px; + border-radius: 2px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span { + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options { + overflow: hidden; + padding: 0; + display: block; + clear: none; + float: right; + margin: 6px 10px 0 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li { + float: left; + clear: none; + margin: 0; + padding: 2px 10px; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a { + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content { + border-top: none; + padding: 10px; + -moz-border-radius-bottomleft: 6px; + -webkit-border-bottom-left-radius: 6px; + -o-border-bottom-left-radius: 6px; + -ms-border-bottom-left-radius: 6px; + -khtml-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -moz-border-radius-bottomright: 6px; + -webkit-border-bottom-right-radius: 6px; + -o-border-bottom-right-radius: 6px; + -ms-border-bottom-right-radius: 6px; + -khtml-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + margin: 0 0 20px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content h4 { + font-size: 1.1em; + margin: 0; + padding: 15px 0 5px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header a { + padding: 4px 0 0 10px; + display: inline-block; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit { + display: block; + clear: none; + float: left; + padding: 6px 8px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber { + background-image: url('../images/throbber.gif'); + width: 128px; + height: 16px; + display: block; + clear: none; + float: right; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error { + outline: 2px solid black; + outline-color: #cc0000; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form select[name='parameterContentType'] { + max-width: 300px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + padding: 10px; + font-size: 0.9em; + max-height: 400px; + overflow-y: auto; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading { + background-color: #f9f2e9; + border: 1px solid #f0e0ca; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a { + background-color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #f0e0ca; + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a { + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content { + background-color: #faf5ee; + border: 1px solid #f0e0ca; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 { + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content div.sandbox_header a { + color: #dcb67f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading { + background-color: #fcffcd; + border: 1px solid black; + border-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading h3 span.http_method a { + text-transform: uppercase; + background-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #ffd20f; + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li a { + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content { + background-color: #fcffcd; + border: 1px solid black; + border-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content h4 { + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content div.sandbox_header a { + color: #6fc992; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading { + background-color: #f5e8e8; + border: 1px solid #e8c6c7; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a { + text-transform: uppercase; + background-color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #e8c6c7; + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a { + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { + background-color: #f7eded; + border: 1px solid #e8c6c7; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 { + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content div.sandbox_header a { + color: #c8787a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading { + background-color: #e7f6ec; + border: 1px solid #c3e8d1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a { + background-color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3e8d1; + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a { + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content { + background-color: #ebf7f0; + border: 1px solid #c3e8d1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 { + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a { + color: #6fc992; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading { + background-color: #FCE9E3; + border: 1px solid #F5D5C3; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a { + background-color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #f0cecb; + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a { + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content { + background-color: #faf0ef; + border: 1px solid #f0cecb; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 { + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content div.sandbox_header a { + color: #dcb67f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading { + background-color: #e7f0f7; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a { + background-color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3d9ec; + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content div.sandbox_header a { + color: #6fa5d2; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading { + background-color: #e7f0f7; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a { + background-color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3d9ec; + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content div.sandbox_header a { + color: #6fa5d2; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { + border-top: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li.last { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:hover, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:active, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a.active { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap p#colophon { + margin: 0 15px 40px 15px; + padding: 10px 0; + font-size: 0.8em; + border-top: 1px solid #dddddd; + font-family: "Droid Sans", sans-serif; + color: #999999; + font-style: italic; +} +.swagger-section .swagger-ui-wrap p#colophon a { + text-decoration: none; + color: #547f00; +} +.swagger-section .swagger-ui-wrap h3 { + color: black; + font-size: 1.1em; + padding: 10px 0 10px 0; +} +.swagger-section .swagger-ui-wrap .markdown ol, +.swagger-section .swagger-ui-wrap .markdown ul { + font-family: "Droid Sans", sans-serif; + margin: 5px 0 10px; + padding: 0 0 0 18px; + list-style-type: disc; +} +.swagger-section .swagger-ui-wrap form.form_box { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; + padding: 10px; +} +.swagger-section .swagger-ui-wrap form.form_box label { + color: #0f6ab4 !important; +} +.swagger-section .swagger-ui-wrap form.form_box input[type=submit] { + display: block; + padding: 10px; +} +.swagger-section .swagger-ui-wrap form.form_box p.weak { + font-size: 0.8em; +} +.swagger-section .swagger-ui-wrap form.form_box p { + font-size: 0.9em; + padding: 0 0 15px; + color: #7e7b6d; +} +.swagger-section .swagger-ui-wrap form.form_box p a { + color: #646257; +} +.swagger-section .swagger-ui-wrap form.form_box p strong { + color: black; +} +.swagger-section .swagger-ui-wrap .operation-status td.markdown > p:last-child { + padding-bottom: 0; +} +.swagger-section .title { + font-style: bold; +} +.swagger-section .secondary_form { + display: none; +} +.swagger-section .main_image { + display: block; + margin-left: auto; + margin-right: auto; +} +.swagger-section .oauth_body { + margin-left: 100px; + margin-right: 100px; +} +.swagger-section .oauth_submit { + text-align: center; + display: inline-block; +} +.swagger-section .authorize-wrapper { + margin: 15px 0 10px; +} +.swagger-section .authorize-wrapper_operation { + float: right; +} +.swagger-section .authorize__btn:hover { + text-decoration: underline; + cursor: pointer; +} +.swagger-section .authorize__btn_operation:hover .authorize-scopes { + display: block; +} +.swagger-section .authorize-scopes { + position: absolute; + margin-top: 20px; + background: #FFF; + border: 1px solid #ccc; + border-radius: 5px; + display: none; + font-size: 13px; + max-width: 300px; + line-height: 30px; + color: black; + padding: 5px; +} +.swagger-section .authorize-scopes .authorize__scope { + text-decoration: none; +} +.swagger-section .authorize__btn_operation { + height: 18px; + vertical-align: middle; + display: inline-block; + background: url(../images/explorer_icons.png) no-repeat; +} +.swagger-section .authorize__btn_operation_login { + background-position: 0 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .authorize__btn_operation_logout { + background-position: -30px 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section #auth_container { + color: #fff; + display: inline-block; + border: none; + padding: 5px; + width: 87px; + height: 13px; +} +.swagger-section #auth_container .authorize__btn { + color: #fff; +} +.swagger-section .auth_container { + padding: 0 0 10px; + margin-bottom: 5px; + border-bottom: solid 1px #CCC; + font-size: 0.9em; +} +.swagger-section .auth_container .auth__title { + color: #547f00; + font-size: 1.2em; +} +.swagger-section .auth_container .basic_auth__label { + display: inline-block; + width: 60px; +} +.swagger-section .auth_container .auth__description { + color: #999999; + margin-bottom: 5px; +} +.swagger-section .auth_container .auth__button { + margin-top: 10px; + height: 30px; +} +.swagger-section .auth_container .key_auth__field { + margin: 5px 0; +} +.swagger-section .auth_container .key_auth__label { + display: inline-block; + width: 60px; +} +.swagger-section .api-popup-dialog { + position: absolute; + display: none; +} +.swagger-section .api-popup-dialog-wrapper { + z-index: 1000; + width: 500px; + background: #FFF; + padding: 20px; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 13px; + color: #777; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +.swagger-section .api-popup-dialog-shadow { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.2; + background-color: gray; + z-index: 900; +} +.swagger-section .api-popup-dialog .api-popup-title { + font-size: 24px; + padding: 10px 0; +} +.swagger-section .api-popup-dialog .api-popup-title { + font-size: 24px; + padding: 10px 0; +} +.swagger-section .api-popup-dialog .error-msg { + padding-left: 5px; + padding-bottom: 5px; +} +.swagger-section .api-popup-dialog .api-popup-content { + max-height: 500px; + overflow-y: auto; +} +.swagger-section .api-popup-dialog .api-popup-authbtn { + height: 30px; +} +.swagger-section .api-popup-dialog .api-popup-cancel { + height: 30px; +} +.swagger-section .api-popup-scopes { + padding: 10px 20px; +} +.swagger-section .api-popup-scopes li { + padding: 5px 0; + line-height: 20px; +} +.swagger-section .api-popup-scopes li input { + position: relative; + top: 2px; +} +.swagger-section .api-popup-scopes .api-scope-desc { + padding-left: 20px; + font-style: italic; +} +.swagger-section .api-popup-actions { + padding-top: 10px; +} +.swagger-section .access { + float: right; +} +.swagger-section .auth { + float: right; +} +.swagger-section .api-ic { + height: 18px; + vertical-align: middle; + display: inline-block; + background: url(../images/explorer_icons.png) no-repeat; +} +.swagger-section .api-ic .api_information_panel { + position: relative; + margin-top: 20px; + margin-left: -5px; + background: #FFF; + border: 1px solid #ccc; + border-radius: 5px; + display: none; + font-size: 13px; + max-width: 300px; + line-height: 30px; + color: black; + padding: 5px; +} +.swagger-section .api-ic .api_information_panel p .api-msg-enabled { + color: green; +} +.swagger-section .api-ic .api_information_panel p .api-msg-disabled { + color: red; +} +.swagger-section .api-ic:hover .api_information_panel { + position: absolute; + display: block; +} +.swagger-section .ic-info { + background-position: 0 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .ic-warning { + background-position: -60px 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .ic-error { + background-position: -30px 0; + width: 18px; + margin-top: -6px; + margin-left: 4px; +} +.swagger-section .ic-off { + background-position: -90px 0; + width: 58px; + margin-top: -4px; + cursor: pointer; +} +.swagger-section .ic-on { + background-position: -160px 0; + width: 58px; + margin-top: -4px; + cursor: pointer; +} +.swagger-section #header { + background-color: #89bf04; + padding: 9px 14px 19px 14px; + height: 23px; + min-width: 775px; +} +.swagger-section #input_baseUrl { + width: 400px; +} +.swagger-section #api_selector { + display: block; + clear: none; + float: right; +} +.swagger-section #api_selector .input { + display: inline-block; + clear: none; + margin: 0 10px 0 0; +} +.swagger-section #api_selector input { + font-size: 0.9em; + padding: 3px; + margin: 0; +} +.swagger-section #input_apiKey { + width: 200px; +} +.swagger-section #explore, +.swagger-section #auth_container .authorize__btn { + display: block; + text-decoration: none; + font-weight: bold; + padding: 6px 8px; + font-size: 0.9em; + color: white; + background-color: #547f00; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -o-border-radius: 4px; + -ms-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; +} +.swagger-section #explore:hover, +.swagger-section #auth_container .authorize__btn:hover { + background-color: #547f00; +} +.swagger-section #header #logo { + font-size: 1.5em; + font-weight: bold; + text-decoration: none; + color: white; +} +.swagger-section #header #logo .logo__img { + display: block; + float: left; + margin-top: 2px; +} +.swagger-section #header #logo .logo__title { + display: inline-block; + padding: 5px 0 0 10px; +} +.swagger-section #content_message { + margin: 10px 15px; + font-style: italic; + color: #999999; +} +.swagger-section #message-bar { + min-height: 30px; + text-align: center; + padding-top: 10px; +} +.swagger-section .swagger-collapse:before { + content: "-"; +} +.swagger-section .swagger-expand:before { + content: "+"; +} +.swagger-section .error { + outline-color: #cc0000; + background-color: #f2dede; +} diff --git a/phis2-ws/src/main/webapp/api-docs/css/style.css b/phis2-ws/src/main/webapp/api-docs/css/style.css new file mode 100644 index 000000000..0f668c220 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/css/style.css @@ -0,0 +1,251 @@ +.swagger-section #header a#logo { + font-size: 1.5em; + font-weight: bold; + text-decoration: none; + background: transparent url(../images/logo.png) no-repeat left center; + padding: 20px 0 20px 40px; +} +#text-head { + font-size: 80px; + font-family: 'Roboto', sans-serif; + color: #ffffff; + float: right; + margin-right: 20%; +} +.navbar-fixed-top .navbar-nav { + height: auto; +} +.navbar-fixed-top .navbar-brand { + height: auto; +} +.navbar-header { + height: auto; +} +.navbar-inverse { + background-color: #000; + border-color: #000; +} +#navbar-brand { + margin-left: 20%; +} +.navtext { + font-size: 10px; +} +.h1, +h1 { + font-size: 60px; +} +.navbar-default .navbar-header .navbar-brand { + color: #a2dfee; +} +/* tag titles */ +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a { + color: #393939; + font-family: 'Arvo', serif; + font-size: 1.5em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 { + color: #525252; + padding-left: 0px; + display: block; + clear: none; + float: left; + font-family: 'Arvo', serif; + font-weight: bold; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #0A0A0A; +} +.container1 { + width: 1500px; + margin: auto; + margin-top: 0; + background-image: url('../images/shield.png'); + background-repeat: no-repeat; + background-position: -40px -20px; + margin-bottom: 210px; +} +.container-inner { + width: 1200px; + margin: auto; + background-color: rgba(223, 227, 228, 0.75); + padding-bottom: 40px; + padding-top: 40px; + border-radius: 15px; +} +.header-content { + padding: 0; + width: 1000px; +} +.title1 { + font-size: 80px; + font-family: 'Vollkorn', serif; + color: #404040; + text-align: center; + padding-top: 40px; + padding-bottom: 100px; +} +#icon { + margin-top: -18px; +} +.subtext { + font-size: 25px; + font-style: italic; + color: #08b; + text-align: right; + padding-right: 250px; +} +.bg-primary { + background-color: #00468b; +} +.navbar-default .nav > li > a, +.navbar-default .nav > li > a:focus { + color: #08b; +} +.navbar-default .nav > li > a, +.navbar-default .nav > li > a:hover { + color: #08b; +} +.navbar-default .nav > li > a, +.navbar-default .nav > li > a:focus:hover { + color: #08b; +} +.text-faded { + font-size: 25px; + font-family: 'Vollkorn', serif; +} +.section-heading { + font-family: 'Vollkorn', serif; + font-size: 45px; + padding-bottom: 10px; +} +hr { + border-color: #00468b; + padding-bottom: 10px; +} +.description { + margin-top: 20px; + padding-bottom: 200px; +} +.description li { + font-family: 'Vollkorn', serif; + font-size: 25px; + color: #525252; + margin-left: 28%; + padding-top: 5px; +} +.gap { + margin-top: 200px; +} +.troubleshootingtext { + color: rgba(255, 255, 255, 0.7); + padding-left: 30%; +} +.troubleshootingtext li { + list-style-type: circle; + font-size: 25px; + padding-bottom: 5px; +} +.overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; +} +.block.response_body.json:hover { + cursor: pointer; +} +.backdrop { + color: blue; +} +#myModal { + height: 100%; +} +.modal-backdrop { + bottom: 0; + position: fixed; +} +.curl { + padding: 10px; + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + font-size: 0.9em; + max-height: 400px; + margin-top: 5px; + overflow-y: auto; + background-color: #fcf6db; + border: 1px solid #e5e0c6; + border-radius: 4px; +} +.curl_title { + font-size: 1.1em; + margin: 0; + padding: 15px 0 5px; + font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif; + font-weight: 500; + line-height: 1.1; +} +.footer { + display: none; +} +.swagger-section .swagger-ui-wrap h2 { + padding: 0; +} +h2 { + margin: 0; + margin-bottom: 5px; +} +.markdown p { + font-size: 15px; + font-family: 'Arvo', serif; +} +.swagger-section .swagger-ui-wrap .code { + font-size: 15px; + font-family: 'Arvo', serif; +} +.swagger-section .swagger-ui-wrap b { + font-family: 'Arvo', serif; +} +#signin:hover { + cursor: pointer; +} +.dropdown-menu { + padding: 15px; +} +.navbar-right .dropdown-menu { + left: 0; + right: auto; +} +#signinbutton { + width: 100%; + height: 32px; + font-size: 13px; + font-weight: bold; + color: #08b; +} +.navbar-default .nav > li .details { + color: #000000; + text-transform: none; + font-size: 15px; + font-weight: normal; + font-family: 'Open Sans', sans-serif; + font-style: italic; + line-height: 20px; + top: -2px; +} +.navbar-default .nav > li .details:hover { + color: black; +} +#signout { + width: 100%; + height: 32px; + font-size: 13px; + font-weight: bold; + color: #08b; +} + diff --git a/phis2-ws/src/main/webapp/api-docs/css/typography.css b/phis2-ws/src/main/webapp/api-docs/css/typography.css new file mode 100644 index 000000000..efb785fab --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/css/typography.css @@ -0,0 +1,14 @@ +/* Google Font's Droid Sans */ +@font-face { + font-family: 'Droid Sans'; + font-style: normal; + font-weight: 400; + src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf'), format('truetype'); +} +/* Google Font's Droid Sans Bold */ +@font-face { + font-family: 'Droid Sans'; + font-style: normal; + font-weight: 700; + src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf'), format('truetype'); +} diff --git a/phis2-ws/src/main/webapp/api-docs/fonts/DroidSans-Bold.ttf b/phis2-ws/src/main/webapp/api-docs/fonts/DroidSans-Bold.ttf new file mode 100644 index 000000000..036c4d135 Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/fonts/DroidSans-Bold.ttf differ diff --git a/phis2-ws/src/main/webapp/api-docs/fonts/DroidSans.ttf b/phis2-ws/src/main/webapp/api-docs/fonts/DroidSans.ttf new file mode 100644 index 000000000..e517a0c5b Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/fonts/DroidSans.ttf differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/collapse.gif b/phis2-ws/src/main/webapp/api-docs/images/collapse.gif new file mode 100644 index 000000000..8843e8ce5 Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/collapse.gif differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/expand.gif b/phis2-ws/src/main/webapp/api-docs/images/expand.gif new file mode 100644 index 000000000..477bf1371 Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/expand.gif differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/explorer_icons.png b/phis2-ws/src/main/webapp/api-docs/images/explorer_icons.png new file mode 100644 index 000000000..be43b2739 Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/explorer_icons.png differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/favicon-16x16.png b/phis2-ws/src/main/webapp/api-docs/images/favicon-16x16.png new file mode 100644 index 000000000..0f7e13b0d Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/favicon-16x16.png differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/favicon-32x32.png b/phis2-ws/src/main/webapp/api-docs/images/favicon-32x32.png new file mode 100644 index 000000000..b0a3352ff Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/favicon-32x32.png differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/favicon.ico b/phis2-ws/src/main/webapp/api-docs/images/favicon.ico new file mode 100644 index 000000000..8b60bcf06 Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/favicon.ico differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/logo_small.png b/phis2-ws/src/main/webapp/api-docs/images/logo_small.png new file mode 100644 index 000000000..ce3908e3f Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/logo_small.png differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/pet_store_api.png b/phis2-ws/src/main/webapp/api-docs/images/pet_store_api.png new file mode 100644 index 000000000..1192ad8cd Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/pet_store_api.png differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/throbber.gif b/phis2-ws/src/main/webapp/api-docs/images/throbber.gif new file mode 100644 index 000000000..063938892 Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/throbber.gif differ diff --git a/phis2-ws/src/main/webapp/api-docs/images/wordnik_api.png b/phis2-ws/src/main/webapp/api-docs/images/wordnik_api.png new file mode 100644 index 000000000..dc0ddab13 Binary files /dev/null and b/phis2-ws/src/main/webapp/api-docs/images/wordnik_api.png differ diff --git a/phis2-ws/src/main/webapp/api-docs/index.html b/phis2-ws/src/main/webapp/api-docs/index.html new file mode 100644 index 000000000..90f631272 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/index.html @@ -0,0 +1,119 @@ + + + + + Swagger UI - PhenomeAPI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+
+ + diff --git a/phis2-ws/src/main/webapp/api-docs/lang/en.js b/phis2-ws/src/main/webapp/api-docs/lang/en.js new file mode 100644 index 000000000..918313665 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/en.js @@ -0,0 +1,56 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"Warning: Deprecated", + "Implementation Notes":"Implementation Notes", + "Response Class":"Response Class", + "Status":"Status", + "Parameters":"Parameters", + "Parameter":"Parameter", + "Value":"Value", + "Description":"Description", + "Parameter Type":"Parameter Type", + "Data Type":"Data Type", + "Response Messages":"Response Messages", + "HTTP Status Code":"HTTP Status Code", + "Reason":"Reason", + "Response Model":"Response Model", + "Request URL":"Request URL", + "Response Body":"Response Body", + "Response Code":"Response Code", + "Response Headers":"Response Headers", + "Hide Response":"Hide Response", + "Headers":"Headers", + "Try it out!":"Try it out!", + "Show/Hide":"Show/Hide", + "List Operations":"List Operations", + "Expand Operations":"Expand Operations", + "Raw":"Raw", + "can't parse JSON. Raw result":"can't parse JSON. Raw result", + "Example Value":"Example Value", + "Model Schema":"Model Schema", + "Model":"Model", + "Click to set as parameter value":"Click to set as parameter value", + "apply":"apply", + "Username":"Username", + "Password":"Password", + "Terms of service":"Terms of service", + "Created by":"Created by", + "See more at":"See more at", + "Contact the developer":"Contact the developer", + "api version":"api version", + "Response Content Type":"Response Content Type", + "Parameter content type:":"Parameter content type:", + "fetching resource":"fetching resource", + "fetching resource list":"fetching resource list", + "Explore":"Explore", + "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"Can't read from server. It may not have the appropriate access-control-origin settings.", + "Please specify the protocol for":"Please specify the protocol for", + "Can't read swagger JSON from":"Can't read swagger JSON from", + "Finished Loading Resource Information. Rendering Swagger UI":"Finished Loading Resource Information. Rendering Swagger UI", + "Unable to read api":"Unable to read api", + "from path":"from path", + "server returned":"server returned" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/es.js b/phis2-ws/src/main/webapp/api-docs/lang/es.js new file mode 100644 index 000000000..13fa015e6 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/es.js @@ -0,0 +1,53 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"Advertencia: Obsoleto", + "Implementation Notes":"Notas de implementación", + "Response Class":"Clase de la Respuesta", + "Status":"Status", + "Parameters":"Parámetros", + "Parameter":"Parámetro", + "Value":"Valor", + "Description":"Descripción", + "Parameter Type":"Tipo del Parámetro", + "Data Type":"Tipo del Dato", + "Response Messages":"Mensajes de la Respuesta", + "HTTP Status Code":"Código de Status HTTP", + "Reason":"Razón", + "Response Model":"Modelo de la Respuesta", + "Request URL":"URL de la Solicitud", + "Response Body":"Cuerpo de la Respuesta", + "Response Code":"Código de la Respuesta", + "Response Headers":"Encabezados de la Respuesta", + "Hide Response":"Ocultar Respuesta", + "Try it out!":"Pruébalo!", + "Show/Hide":"Mostrar/Ocultar", + "List Operations":"Listar Operaciones", + "Expand Operations":"Expandir Operaciones", + "Raw":"Crudo", + "can't parse JSON. Raw result":"no puede parsear el JSON. Resultado crudo", + "Example Value":"Valor de Ejemplo", + "Model Schema":"Esquema del Modelo", + "Model":"Modelo", + "apply":"aplicar", + "Username":"Nombre de usuario", + "Password":"Contraseña", + "Terms of service":"Términos de Servicio", + "Created by":"Creado por", + "See more at":"Ver más en", + "Contact the developer":"Contactar al desarrollador", + "api version":"versión de la api", + "Response Content Type":"Tipo de Contenido (Content Type) de la Respuesta", + "fetching resource":"buscando recurso", + "fetching resource list":"buscando lista del recurso", + "Explore":"Explorar", + "Show Swagger Petstore Example Apis":"Mostrar Api Ejemplo de Swagger Petstore", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"No se puede leer del servidor. Tal vez no tiene la configuración de control de acceso de origen (access-control-origin) apropiado.", + "Please specify the protocol for":"Por favor, especificar el protocola para", + "Can't read swagger JSON from":"No se puede leer el JSON de swagger desde", + "Finished Loading Resource Information. Rendering Swagger UI":"Finalizada la carga del recurso de Información. Mostrando Swagger UI", + "Unable to read api":"No se puede leer la api", + "from path":"desde ruta", + "server returned":"el servidor retornó" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/fr.js b/phis2-ws/src/main/webapp/api-docs/lang/fr.js new file mode 100644 index 000000000..388dff14b --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/fr.js @@ -0,0 +1,54 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"Avertissement : Obsolète", + "Implementation Notes":"Notes d'implémentation", + "Response Class":"Classe de la réponse", + "Status":"Statut", + "Parameters":"Paramètres", + "Parameter":"Paramètre", + "Value":"Valeur", + "Description":"Description", + "Parameter Type":"Type du paramètre", + "Data Type":"Type de données", + "Response Messages":"Messages de la réponse", + "HTTP Status Code":"Code de statut HTTP", + "Reason":"Raison", + "Response Model":"Modèle de réponse", + "Request URL":"URL appelée", + "Response Body":"Corps de la réponse", + "Response Code":"Code de la réponse", + "Response Headers":"En-têtes de la réponse", + "Hide Response":"Cacher la réponse", + "Headers":"En-têtes", + "Try it out!":"Testez !", + "Show/Hide":"Afficher/Masquer", + "List Operations":"Liste des opérations", + "Expand Operations":"Développer les opérations", + "Raw":"Brut", + "can't parse JSON. Raw result":"impossible de décoder le JSON. Résultat brut", + "Example Value":"Exemple la valeur", + "Model Schema":"Définition du modèle", + "Model":"Modèle", + "apply":"appliquer", + "Username":"Nom d'utilisateur", + "Password":"Mot de passe", + "Terms of service":"Conditions de service", + "Created by":"Créé par", + "See more at":"Voir plus sur", + "Contact the developer":"Contacter le développeur", + "api version":"version de l'api", + "Response Content Type":"Content Type de la réponse", + "fetching resource":"récupération de la ressource", + "fetching resource list":"récupération de la liste de ressources", + "Explore":"Explorer", + "Show Swagger Petstore Example Apis":"Montrer les Apis de l'exemple Petstore de Swagger", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"Impossible de lire à partir du serveur. Il se peut que les réglages access-control-origin ne soient pas appropriés.", + "Please specify the protocol for":"Veuillez spécifier un protocole pour", + "Can't read swagger JSON from":"Impossible de lire le JSON swagger à partir de", + "Finished Loading Resource Information. Rendering Swagger UI":"Chargement des informations terminé. Affichage de Swagger UI", + "Unable to read api":"Impossible de lire l'api", + "from path":"à partir du chemin", + "server returned":"réponse du serveur" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/geo.js b/phis2-ws/src/main/webapp/api-docs/lang/geo.js new file mode 100644 index 000000000..609c20d9c --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/geo.js @@ -0,0 +1,56 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"ყურადღება: აღარ გამოიყენება", + "Implementation Notes":"იმპლემენტაციის აღწერა", + "Response Class":"რესპონს კლასი", + "Status":"სტატუსი", + "Parameters":"პარამეტრები", + "Parameter":"პარამეტრი", + "Value":"მნიშვნელობა", + "Description":"აღწერა", + "Parameter Type":"პარამეტრის ტიპი", + "Data Type":"მონაცემის ტიპი", + "Response Messages":"პასუხი", + "HTTP Status Code":"HTTP სტატუსი", + "Reason":"მიზეზი", + "Response Model":"რესპონს მოდელი", + "Request URL":"მოთხოვნის URL", + "Response Body":"პასუხის სხეული", + "Response Code":"პასუხის კოდი", + "Response Headers":"პასუხის ჰედერები", + "Hide Response":"დამალე პასუხი", + "Headers":"ჰედერები", + "Try it out!":"ცადე !", + "Show/Hide":"გამოჩენა/დამალვა", + "List Operations":"ოპერაციების სია", + "Expand Operations":"ოპერაციები ვრცლად", + "Raw":"ნედლი", + "can't parse JSON. Raw result":"JSON-ის დამუშავება ვერ მოხერხდა. ნედლი პასუხი", + "Example Value":"მაგალითი", + "Model Schema":"მოდელის სტრუქტურა", + "Model":"მოდელი", + "Click to set as parameter value":"პარამეტრისთვის მნიშვნელობის მისანიჭებლად, დააკლიკე", + "apply":"გამოყენება", + "Username":"მოხმარებელი", + "Password":"პაროლი", + "Terms of service":"მომსახურების პირობები", + "Created by":"შექმნა", + "See more at":"ნახე ვრცლად", + "Contact the developer":"დაუკავშირდი დეველოპერს", + "api version":"api ვერსია", + "Response Content Type":"პასუხის კონტენტის ტიპი", + "Parameter content type:":"პარამეტრის კონტენტის ტიპი:", + "fetching resource":"რესურსების მიღება", + "fetching resource list":"რესურსების სიის მიღება", + "Explore":"ნახვა", + "Show Swagger Petstore Example Apis":"ნახე Swagger Petstore სამაგალითო Api", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"სერვერთან დაკავშირება ვერ ხერხდება. შეამოწმეთ access-control-origin.", + "Please specify the protocol for":"მიუთითეთ პროტოკოლი", + "Can't read swagger JSON from":"swagger JSON წაკითხვა ვერ მოხერხდა", + "Finished Loading Resource Information. Rendering Swagger UI":"რესურსების ჩატვირთვა სრულდება. Swagger UI რენდერდება", + "Unable to read api":"api წაკითხვა ვერ მოხერხდა", + "from path":"მისამართიდან", + "server returned":"სერვერმა დააბრუნა" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/it.js b/phis2-ws/src/main/webapp/api-docs/lang/it.js new file mode 100644 index 000000000..8529c2a90 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/it.js @@ -0,0 +1,52 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"Attenzione: Deprecato", + "Implementation Notes":"Note di implementazione", + "Response Class":"Classe della risposta", + "Status":"Stato", + "Parameters":"Parametri", + "Parameter":"Parametro", + "Value":"Valore", + "Description":"Descrizione", + "Parameter Type":"Tipo di parametro", + "Data Type":"Tipo di dato", + "Response Messages":"Messaggi della risposta", + "HTTP Status Code":"Codice stato HTTP", + "Reason":"Motivo", + "Response Model":"Modello di risposta", + "Request URL":"URL della richiesta", + "Response Body":"Corpo della risposta", + "Response Code":"Oggetto della risposta", + "Response Headers":"Intestazioni della risposta", + "Hide Response":"Nascondi risposta", + "Try it out!":"Provalo!", + "Show/Hide":"Mostra/Nascondi", + "List Operations":"Mostra operazioni", + "Expand Operations":"Espandi operazioni", + "Raw":"Grezzo (raw)", + "can't parse JSON. Raw result":"non è possibile parsare il JSON. Risultato grezzo (raw).", + "Model Schema":"Schema del modello", + "Model":"Modello", + "apply":"applica", + "Username":"Nome utente", + "Password":"Password", + "Terms of service":"Condizioni del servizio", + "Created by":"Creato da", + "See more at":"Informazioni aggiuntive:", + "Contact the developer":"Contatta lo sviluppatore", + "api version":"versione api", + "Response Content Type":"Tipo di contenuto (content type) della risposta", + "fetching resource":"recuperando la risorsa", + "fetching resource list":"recuperando lista risorse", + "Explore":"Esplora", + "Show Swagger Petstore Example Apis":"Mostra le api di esempio di Swagger Petstore", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"Non è possibile leggere dal server. Potrebbe non avere le impostazioni di controllo accesso origine (access-control-origin) appropriate.", + "Please specify the protocol for":"Si prega di specificare il protocollo per", + "Can't read swagger JSON from":"Impossibile leggere JSON swagger da:", + "Finished Loading Resource Information. Rendering Swagger UI":"Lettura informazioni risorse termianta. Swagger UI viene mostrata", + "Unable to read api":"Impossibile leggere la api", + "from path":"da cartella", + "server returned":"il server ha restituito" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/ja.js b/phis2-ws/src/main/webapp/api-docs/lang/ja.js new file mode 100644 index 000000000..3207bfc0b --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/ja.js @@ -0,0 +1,53 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"警告: 廃止予定", + "Implementation Notes":"実装メモ", + "Response Class":"レスポンスクラス", + "Status":"ステータス", + "Parameters":"パラメータ群", + "Parameter":"パラメータ", + "Value":"値", + "Description":"説明", + "Parameter Type":"パラメータタイプ", + "Data Type":"データタイプ", + "Response Messages":"レスポンスメッセージ", + "HTTP Status Code":"HTTPステータスコード", + "Reason":"理由", + "Response Model":"レスポンスモデル", + "Request URL":"リクエストURL", + "Response Body":"レスポンスボディ", + "Response Code":"レスポンスコード", + "Response Headers":"レスポンスヘッダ", + "Hide Response":"レスポンスを隠す", + "Headers":"ヘッダ", + "Try it out!":"実際に実行!", + "Show/Hide":"表示/非表示", + "List Operations":"操作一覧", + "Expand Operations":"操作の展開", + "Raw":"Raw", + "can't parse JSON. Raw result":"JSONへ解釈できません. 未加工の結果", + "Model Schema":"モデルスキーマ", + "Model":"モデル", + "apply":"実行", + "Username":"ユーザ名", + "Password":"パスワード", + "Terms of service":"サービス利用規約", + "Created by":"Created by", + "See more at":"See more at", + "Contact the developer":"開発者に連絡", + "api version":"APIバージョン", + "Response Content Type":"レスポンス コンテンツタイプ", + "fetching resource":"リソースの取得", + "fetching resource list":"リソース一覧の取得", + "Explore":"Explore", + "Show Swagger Petstore Example Apis":"SwaggerペットストアAPIの表示", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"サーバから読み込めません. 適切なaccess-control-origin設定を持っていない可能性があります.", + "Please specify the protocol for":"プロトコルを指定してください", + "Can't read swagger JSON from":"次からswagger JSONを読み込めません", + "Finished Loading Resource Information. Rendering Swagger UI":"リソース情報の読み込みが完了しました. Swagger UIを描画しています", + "Unable to read api":"APIを読み込めません", + "from path":"次のパスから", + "server returned":"サーバからの返答" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/ko-kr.js b/phis2-ws/src/main/webapp/api-docs/lang/ko-kr.js new file mode 100644 index 000000000..03c7626d7 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/ko-kr.js @@ -0,0 +1,53 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"경고:폐기예정됨", + "Implementation Notes":"구현 노트", + "Response Class":"응답 클래스", + "Status":"상태", + "Parameters":"매개변수들", + "Parameter":"매개변수", + "Value":"값", + "Description":"설명", + "Parameter Type":"매개변수 타입", + "Data Type":"데이터 타입", + "Response Messages":"응답 메세지", + "HTTP Status Code":"HTTP 상태 코드", + "Reason":"원인", + "Response Model":"응답 모델", + "Request URL":"요청 URL", + "Response Body":"응답 본문", + "Response Code":"응답 코드", + "Response Headers":"응답 헤더", + "Hide Response":"응답 숨기기", + "Headers":"헤더", + "Try it out!":"써보기!", + "Show/Hide":"보이기/숨기기", + "List Operations":"목록 작업", + "Expand Operations":"전개 작업", + "Raw":"원본", + "can't parse JSON. Raw result":"JSON을 파싱할수 없음. 원본결과:", + "Model Schema":"모델 스키마", + "Model":"모델", + "apply":"적용", + "Username":"사용자 이름", + "Password":"암호", + "Terms of service":"이용약관", + "Created by":"작성자", + "See more at":"추가정보:", + "Contact the developer":"개발자에게 문의", + "api version":"api버전", + "Response Content Type":"응답Content Type", + "fetching resource":"리소스 가져오기", + "fetching resource list":"리소스 목록 가져오기", + "Explore":"탐색", + "Show Swagger Petstore Example Apis":"Swagger Petstore 예제 보기", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"서버로부터 읽어들일수 없습니다. access-control-origin 설정이 올바르지 않을수 있습니다.", + "Please specify the protocol for":"다음을 위한 프로토콜을 정하세요", + "Can't read swagger JSON from":"swagger JSON 을 다음으로 부터 읽을수 없습니다", + "Finished Loading Resource Information. Rendering Swagger UI":"리소스 정보 불러오기 완료. Swagger UI 랜더링", + "Unable to read api":"api를 읽을 수 없습니다.", + "from path":"다음 경로로 부터", + "server returned":"서버 응답함." +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/pl.js b/phis2-ws/src/main/webapp/api-docs/lang/pl.js new file mode 100644 index 000000000..ce41e9179 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/pl.js @@ -0,0 +1,53 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"Uwaga: Wycofane", + "Implementation Notes":"Uwagi Implementacji", + "Response Class":"Klasa Odpowiedzi", + "Status":"Status", + "Parameters":"Parametry", + "Parameter":"Parametr", + "Value":"Wartość", + "Description":"Opis", + "Parameter Type":"Typ Parametru", + "Data Type":"Typ Danych", + "Response Messages":"Wiadomości Odpowiedzi", + "HTTP Status Code":"Kod Statusu HTTP", + "Reason":"Przyczyna", + "Response Model":"Model Odpowiedzi", + "Request URL":"URL Wywołania", + "Response Body":"Treść Odpowiedzi", + "Response Code":"Kod Odpowiedzi", + "Response Headers":"Nagłówki Odpowiedzi", + "Hide Response":"Ukryj Odpowiedź", + "Headers":"Nagłówki", + "Try it out!":"Wypróbuj!", + "Show/Hide":"Pokaż/Ukryj", + "List Operations":"Lista Operacji", + "Expand Operations":"Rozwiń Operacje", + "Raw":"Nieprzetworzone", + "can't parse JSON. Raw result":"nie można przetworzyć pliku JSON. Nieprzetworzone dane", + "Model Schema":"Schemat Modelu", + "Model":"Model", + "apply":"użyj", + "Username":"Nazwa użytkownika", + "Password":"Hasło", + "Terms of service":"Warunki używania", + "Created by":"Utworzone przez", + "See more at":"Zobacz więcej na", + "Contact the developer":"Kontakt z deweloperem", + "api version":"wersja api", + "Response Content Type":"Typ Zasobu Odpowiedzi", + "fetching resource":"ładowanie zasobu", + "fetching resource list":"ładowanie listy zasobów", + "Explore":"Eksploruj", + "Show Swagger Petstore Example Apis":"Pokaż Przykładowe Api Swagger Petstore", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"Brak połączenia z serwerem. Może on nie mieć odpowiednich ustawień access-control-origin.", + "Please specify the protocol for":"Proszę podać protokół dla", + "Can't read swagger JSON from":"Nie można odczytać swagger JSON z", + "Finished Loading Resource Information. Rendering Swagger UI":"Ukończono Ładowanie Informacji o Zasobie. Renderowanie Swagger UI", + "Unable to read api":"Nie można odczytać api", + "from path":"ze ścieżki", + "server returned":"serwer zwrócił" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/pt.js b/phis2-ws/src/main/webapp/api-docs/lang/pt.js new file mode 100644 index 000000000..f2e7c13d4 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/pt.js @@ -0,0 +1,53 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"Aviso: Depreciado", + "Implementation Notes":"Notas de Implementação", + "Response Class":"Classe de resposta", + "Status":"Status", + "Parameters":"Parâmetros", + "Parameter":"Parâmetro", + "Value":"Valor", + "Description":"Descrição", + "Parameter Type":"Tipo de parâmetro", + "Data Type":"Tipo de dados", + "Response Messages":"Mensagens de resposta", + "HTTP Status Code":"Código de status HTTP", + "Reason":"Razão", + "Response Model":"Modelo resposta", + "Request URL":"URL requisição", + "Response Body":"Corpo da resposta", + "Response Code":"Código da resposta", + "Response Headers":"Cabeçalho da resposta", + "Headers":"Cabeçalhos", + "Hide Response":"Esconder resposta", + "Try it out!":"Tente agora!", + "Show/Hide":"Mostrar/Esconder", + "List Operations":"Listar operações", + "Expand Operations":"Expandir operações", + "Raw":"Cru", + "can't parse JSON. Raw result":"Falha ao analisar JSON. Resulto cru", + "Model Schema":"Modelo esquema", + "Model":"Modelo", + "apply":"Aplicar", + "Username":"Usuário", + "Password":"Senha", + "Terms of service":"Termos do serviço", + "Created by":"Criado por", + "See more at":"Veja mais em", + "Contact the developer":"Contate o desenvolvedor", + "api version":"Versão api", + "Response Content Type":"Tipo de conteúdo da resposta", + "fetching resource":"busca recurso", + "fetching resource list":"buscando lista de recursos", + "Explore":"Explorar", + "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"Não é possível ler do servidor. Pode não ter as apropriadas configurações access-control-origin", + "Please specify the protocol for":"Por favor especifique o protocolo", + "Can't read swagger JSON from":"Não é possível ler o JSON Swagger de", + "Finished Loading Resource Information. Rendering Swagger UI":"Carregar informação de recurso finalizada. Renderizando Swagger UI", + "Unable to read api":"Não foi possível ler api", + "from path":"do caminho", + "server returned":"servidor retornou" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/ru.js b/phis2-ws/src/main/webapp/api-docs/lang/ru.js new file mode 100644 index 000000000..592744e95 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/ru.js @@ -0,0 +1,56 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"Предупреждение: Устарело", + "Implementation Notes":"Заметки", + "Response Class":"Пример ответа", + "Status":"Статус", + "Parameters":"Параметры", + "Parameter":"Параметр", + "Value":"Значение", + "Description":"Описание", + "Parameter Type":"Тип параметра", + "Data Type":"Тип данных", + "HTTP Status Code":"HTTP код", + "Reason":"Причина", + "Response Model":"Структура ответа", + "Request URL":"URL запроса", + "Response Body":"Тело ответа", + "Response Code":"HTTP код ответа", + "Response Headers":"Заголовки ответа", + "Hide Response":"Спрятать ответ", + "Headers":"Заголовки", + "Response Messages":"Что может прийти в ответ", + "Try it out!":"Попробовать!", + "Show/Hide":"Показать/Скрыть", + "List Operations":"Операции кратко", + "Expand Operations":"Операции подробно", + "Raw":"В сыром виде", + "can't parse JSON. Raw result":"Не удается распарсить ответ:", + "Example Value":"Пример", + "Model Schema":"Структура", + "Model":"Описание", + "Click to set as parameter value":"Нажмите, чтобы испльзовать в качестве значения параметра", + "apply":"применить", + "Username":"Имя пользователя", + "Password":"Пароль", + "Terms of service":"Условия использования", + "Created by":"Разработано", + "See more at":"Еще тут", + "Contact the developer":"Связаться с разработчиком", + "api version":"Версия API", + "Response Content Type":"Content Type ответа", + "Parameter content type:":"Content Type параметра:", + "fetching resource":"Получение ресурса", + "fetching resource list":"Получение ресурсов", + "Explore":"Показать", + "Show Swagger Petstore Example Apis":"Показать примеры АПИ", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"Не удается получить ответ от сервера. Возможно, проблема с настройками доступа", + "Please specify the protocol for":"Пожалуйста, укажите протокол для", + "Can't read swagger JSON from":"Не получается прочитать swagger json из", + "Finished Loading Resource Information. Rendering Swagger UI":"Загрузка информации о ресурсах завершена. Рендерим", + "Unable to read api":"Не удалось прочитать api", + "from path":"по адресу", + "server returned":"сервер сказал" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/tr.js b/phis2-ws/src/main/webapp/api-docs/lang/tr.js new file mode 100644 index 000000000..16426a9c3 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/tr.js @@ -0,0 +1,53 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"Uyarı: Deprecated", + "Implementation Notes":"Gerçekleştirim Notları", + "Response Class":"Dönen Sınıf", + "Status":"Statü", + "Parameters":"Parametreler", + "Parameter":"Parametre", + "Value":"Değer", + "Description":"Açıklama", + "Parameter Type":"Parametre Tipi", + "Data Type":"Veri Tipi", + "Response Messages":"Dönüş Mesajı", + "HTTP Status Code":"HTTP Statü Kodu", + "Reason":"Gerekçe", + "Response Model":"Dönüş Modeli", + "Request URL":"İstek URL", + "Response Body":"Dönüş İçeriği", + "Response Code":"Dönüş Kodu", + "Response Headers":"Dönüş Üst Bilgileri", + "Hide Response":"Dönüşü Gizle", + "Headers":"Üst Bilgiler", + "Try it out!":"Dene!", + "Show/Hide":"Göster/Gizle", + "List Operations":"Operasyonları Listele", + "Expand Operations":"Operasyonları Aç", + "Raw":"Ham", + "can't parse JSON. Raw result":"JSON çözümlenemiyor. Ham sonuç", + "Model Schema":"Model Şema", + "Model":"Model", + "apply":"uygula", + "Username":"Kullanıcı Adı", + "Password":"Parola", + "Terms of service":"Servis şartları", + "Created by":"Oluşturan", + "See more at":"Daha fazlası için", + "Contact the developer":"Geliştirici ile İletişime Geçin", + "api version":"api versiyon", + "Response Content Type":"Dönüş İçerik Tipi", + "fetching resource":"kaynak getiriliyor", + "fetching resource list":"kaynak listesi getiriliyor", + "Explore":"Keşfet", + "Show Swagger Petstore Example Apis":"Swagger Petstore Örnek Api'yi Gör", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"Sunucudan okuma yapılamıyor. Sunucu access-control-origin ayarlarınızı kontrol edin.", + "Please specify the protocol for":"Lütfen istenen adres için protokol belirtiniz", + "Can't read swagger JSON from":"Swagger JSON bu kaynaktan okunamıyor", + "Finished Loading Resource Information. Rendering Swagger UI":"Kaynak baglantısı tamamlandı. Swagger UI gösterime hazırlanıyor", + "Unable to read api":"api okunamadı", + "from path":"yoldan", + "server returned":"sunucuya dönüldü" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lang/translator.js b/phis2-ws/src/main/webapp/api-docs/lang/translator.js new file mode 100644 index 000000000..ffb879f9a --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/translator.js @@ -0,0 +1,39 @@ +'use strict'; + +/** + * Translator for documentation pages. + * + * To enable translation you should include one of language-files in your index.html + * after . + * For example - + * + * If you wish to translate some new texts you should do two things: + * 1. Add a new phrase pair ("New Phrase": "New Translation") into your language file (for example lang/ru.js). It will be great if you add it in other language files too. + * 2. Mark that text it templates this way New Phrase or . + * The main thing here is attribute data-sw-translate. Only inner html, title-attribute and value-attribute are going to translate. + * + */ +window.SwaggerTranslator = { + + _words:[], + + translate: function(sel) { + var $this = this; + sel = sel || '[data-sw-translate]'; + + $(sel).each(function() { + $(this).html($this._tryTranslate($(this).html())); + + $(this).val($this._tryTranslate($(this).val())); + $(this).attr('title', $this._tryTranslate($(this).attr('title'))); + }); + }, + + _tryTranslate: function(word) { + return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word; + }, + + learn: function(wordsMap) { + this._words = wordsMap; + } +}; diff --git a/phis2-ws/src/main/webapp/api-docs/lang/zh-cn.js b/phis2-ws/src/main/webapp/api-docs/lang/zh-cn.js new file mode 100644 index 000000000..570319ba1 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lang/zh-cn.js @@ -0,0 +1,53 @@ +'use strict'; + +/* jshint quotmark: double */ +window.SwaggerTranslator.learn({ + "Warning: Deprecated":"警告:已过时", + "Implementation Notes":"实现备注", + "Response Class":"响应类", + "Status":"状态", + "Parameters":"参数", + "Parameter":"参数", + "Value":"值", + "Description":"描述", + "Parameter Type":"参数类型", + "Data Type":"数据类型", + "Response Messages":"响应消息", + "HTTP Status Code":"HTTP状态码", + "Reason":"原因", + "Response Model":"响应模型", + "Request URL":"请求URL", + "Response Body":"响应体", + "Response Code":"响应码", + "Response Headers":"响应头", + "Hide Response":"隐藏响应", + "Headers":"头", + "Try it out!":"试一下!", + "Show/Hide":"显示/隐藏", + "List Operations":"显示操作", + "Expand Operations":"展开操作", + "Raw":"原始", + "can't parse JSON. Raw result":"无法解析JSON. 原始结果", + "Model Schema":"模型架构", + "Model":"模型", + "apply":"应用", + "Username":"用户名", + "Password":"密码", + "Terms of service":"服务条款", + "Created by":"创建者", + "See more at":"查看更多:", + "Contact the developer":"联系开发者", + "api version":"api版本", + "Response Content Type":"响应Content Type", + "fetching resource":"正在获取资源", + "fetching resource list":"正在获取资源列表", + "Explore":"浏览", + "Show Swagger Petstore Example Apis":"显示 Swagger Petstore 示例 Apis", + "Can't read from server. It may not have the appropriate access-control-origin settings.":"无法从服务器读取。可能没有正确设置access-control-origin。", + "Please specify the protocol for":"请指定协议:", + "Can't read swagger JSON from":"无法读取swagger JSON于", + "Finished Loading Resource Information. Rendering Swagger UI":"已加载资源信息。正在渲染Swagger UI", + "Unable to read api":"无法读取api", + "from path":"从路径", + "server returned":"服务器返回" +}); diff --git a/phis2-ws/src/main/webapp/api-docs/lib/backbone-min.js b/phis2-ws/src/main/webapp/api-docs/lib/backbone-min.js new file mode 100644 index 000000000..a3f544be6 --- /dev/null +++ b/phis2-ws/src/main/webapp/api-docs/lib/backbone-min.js @@ -0,0 +1,15 @@ +// Backbone.js 1.1.2 + +(function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('