This is an automated email from the git hooks/post-receive script. New change to branch refonte-rest in repository coselmar. See https://gitlab.nuiton.org/codelutin/coselmar.git at 6e0a140 upgrade jwt lib This branch includes the following new commits: new 850661b let's migrate from old webmotion to Resteasy for REST APIs new 64e85dc update dependencies list new 6e0a140 upgrade jwt lib The 3 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit 6e0a1403e72d6a78441ee55e026a3b8b3f6dbfef Author: Yannick Martel <martel@©odelutin.com> Date: Tue Jun 4 18:10:13 2019 +0200 upgrade jwt lib commit 64e85dc349db28bbe792a3c086401dcf1391d3a7 Author: Yannick Martel <martel@©odelutin.com> Date: Tue Jun 4 16:23:16 2019 +0200 update dependencies list commit 850661b810bac22323892a92d266156b067d00cb Author: Yannick Martel <martel@©odelutin.com> Date: Tue Jun 4 11:49:40 2019 +0200 let's migrate from old webmotion to Resteasy for REST APIs -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch refonte-rest in repository coselmar. See https://gitlab.nuiton.org/codelutin/coselmar.git commit 850661b810bac22323892a92d266156b067d00cb Author: Yannick Martel <martel@©odelutin.com> Date: Tue Jun 4 11:49:40 2019 +0200 let's migrate from old webmotion to Resteasy for REST APIs --- coselmar-rest/pom.xml | 75 +++- .../fr/ifremer/coselmar/beans/DocumentBean.java | 2 +- .../ifremer/coselmar/beans/DocumentSearchBean.java | 12 +- .../ifremer/coselmar/beans/QuestionSearchBean.java | 2 + .../java/fr/ifremer/coselmar/beans/UserBean.java | 2 + .../fr/ifremer/coselmar/beans/UserSearchBean.java | 6 + .../coselmar/converter/JacksonMapperUtil.java | 29 ++ .../providers/CoselmarJacksonProvider.java | 32 ++ .../providers/CoselmarParamConverterProvider.java | 92 +++++ .../coselmar/providers/DateParamConverter.java | 28 ++ .../providers/DocumentBeanParamConverter.java | 36 ++ .../DocumentSearchBeanParamConverter.java | 36 ++ .../DocumentSearchExampleParamConverter.java | 36 ++ .../coselmar/providers/LinkBeanParamConverter.java | 36 ++ .../providers/QuestionBeanParamConverter.java | 36 ++ .../QuestionSearchBeanParamConverter.java | 36 ++ .../QuestionSearchExampleParamConverter.java | 36 ++ .../providers/SearchExampleParamConverter.java | 36 ++ .../coselmar/providers/UserBeanParamConverter.java | 36 ++ .../providers/UserSearchBeanParamConverter.java | 36 ++ .../coselmar/services/CoselmarApplication.java | 92 +++++ .../ifremer/coselmar/services/CoselmarRender.java | 75 ---- .../services/CoselmarRestApplicationListener.java | 62 ++- .../services/CoselmarRestRequestContext.java | 58 --- .../coselmar/services/CoselmarRestUtil.java | 61 --- .../CoselmarServicesApplicationContext.java | 21 +- .../services/CoselmarWebServiceSupport.java | 117 +----- .../errors/CoselmarTechnicalExceptionMapper.java | 23 ++ .../errors/InvalidCredentialExceptionMapper.java | 22 + .../errors/MailAlreadyExistingExceptionMapper.java | 22 + .../services/errors/NoResultExceptionMapper.java | 22 + .../errors/TopiaNoResultExceptionMapper.java | 23 ++ .../errors/UnauthorizedExceptionMapper.java | 22 + .../services/filter/CORSResponseFilter.java | 46 +++ .../services/filter/CoselmarRestRequestFilter.java | 92 ++--- .../filter/CoselmarTopiaTransactionFilter.java | 7 + .../CoselmarRestRequestContextInjector.java | 53 --- .../injector/CoselmarServicesInjector.java | 56 --- .../coselmar/services/v1/AdminWebService.java | 52 ++- .../coselmar/services/v1/DocumentsWebService.java | 431 ++++++++++++-------- .../ifremer/coselmar/services/v1/ErrorAction.java | 83 ---- .../coselmar/services/v1/GeneralWebService.java | 16 +- .../coselmar/services/v1/HealthService.java | 22 +- .../coselmar/services/v1/QuestionsWebService.java | 446 +++++++++++++-------- .../coselmar/services/v1/UsersWebService.java | 240 ++++++----- coselmar-rest/src/main/resources/mapping | 90 ++--- .../services/AbstractCoselmarWebServiceTest.java | 26 +- .../coselmar/services/QuestionsWebServiceTest.java | 441 ++++++++++---------- .../coselmar/services/UsersWebServiceTest.java | 106 +++-- .../services/v1/DocumentsWebServiceTest.java | 4 +- .../src/main/webapp/js/coselmar-controllers.js | 1 + pom.xml | 88 +++- 52 files changed, 2143 insertions(+), 1417 deletions(-) diff --git a/coselmar-rest/pom.xml b/coselmar-rest/pom.xml index 0fa57c0..e4e3ba4 100644 --- a/coselmar-rest/pom.xml +++ b/coselmar-rest/pom.xml @@ -115,9 +115,57 @@ </dependency> <!-- web part --> + <!-- REASTEasy, for webservices --> <dependency> - <groupId>org.debux.webmotion</groupId> - <artifactId>webmotion</artifactId> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-jaxrs</artifactId> + </dependency> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-jaxb-provider</artifactId> + <scope>runtime</scope> + </dependency> + <!-- Multipart support --> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-multipart-provider</artifactId> + </dependency> + <!-- to run test on API --> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-undertow</artifactId> + <scope>test</scope> + </dependency> + + <!-- transitive dependency from resteasy --> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>javax.ws.rs-api</artifactId> + </dependency> + <!--<dependency>--> + <!--<groupId>org.jboss.spec.javax.ws.rs</groupId>--> + <!--<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>--> + <!--<version>1.0.0.Final</version>--> + <!--</dependency>--> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + </dependency> + + <!-- needed for Servlet 3 --> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-servlet-initializer</artifactId> + <scope>runtime</scope> + </dependency> + <!-- needed for JSON response --> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-jackson2-provider</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> @@ -190,24 +238,6 @@ <groupId>org.nuiton.topia</groupId> <artifactId>topia-junit</artifactId> </dependency> - <dependency> - <groupId>org.debux.webmotion</groupId> - <artifactId>webmotion-unittest</artifactId> - <exclusions> - <exclusion> - <groupId>org.apache.tomcat.embed</groupId> - <artifactId>tomcat-embed-logging-juli</artifactId> - </exclusion> - <exclusion> - <groupId>org.apache.tomcat.embed</groupId> - <artifactId>tomcat-embed-jasper</artifactId> - </exclusion> - <exclusion> - <groupId>org.eclipse.jdt.core.compiler</groupId> - <artifactId>ecj</artifactId> - </exclusion> - </exclusions> - </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> @@ -218,6 +248,11 @@ <artifactId>fluent-hc</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpcore</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/DocumentBean.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/DocumentBean.java index 5ec0d92..c7cef09 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/DocumentBean.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/DocumentBean.java @@ -68,7 +68,7 @@ public class DocumentBean implements Serializable { return new DocumentBean(); } - private DocumentBean(){} + public DocumentBean(){} public DocumentBean(String id, String name, String ownerName, String ownerId, String privacy, Date depositDate, Collection<String> keywords, diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/DocumentSearchBean.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/DocumentSearchBean.java index 17a7599..718ef0e 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/DocumentSearchBean.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/DocumentSearchBean.java @@ -47,13 +47,15 @@ public class DocumentSearchBean extends DocumentBean { protected Date publicationBeforeDate; + public DocumentSearchBean() { + } public DocumentSearchBean(String id, String name, String ownerName, String ownerId, String privacy, - Date depositDate, Collection<String> keywords, - String type, String summary, String language, Date publicationDate, - String authors, String license, String copyright, - boolean withFile, String mimeType, String externalUrl, - String comment, String fileName, String citation) { + Date depositDate, Collection<String> keywords, + String type, String summary, String language, Date publicationDate, + String authors, String license, String copyright, + boolean withFile, String mimeType, String externalUrl, + String comment, String fileName, String citation) { super(id, name, ownerName, ownerId, privacy, depositDate, keywords, type, summary, language, publicationDate, authors, license, copyright, withFile, mimeType, externalUrl, comment, fileName, citation); } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/QuestionSearchBean.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/QuestionSearchBean.java index d1fd0f7..39e7029 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/QuestionSearchBean.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/QuestionSearchBean.java @@ -51,6 +51,8 @@ public class QuestionSearchBean extends QuestionBean { protected String contributorId; protected String clientId; + public QuestionSearchBean() { + } public Integer getLimit() { return limit; diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserBean.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserBean.java index d4a2c7e..0a91e9f 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserBean.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserBean.java @@ -52,6 +52,8 @@ public class UserBean implements Serializable { protected String phoneNumber; protected boolean active; + public UserBean() {} + public UserBean(String id, String firstName, String name, String mail, String phoneNumber, String role, String qualification, String organization, boolean active) { diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserSearchBean.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserSearchBean.java index e4d1eb9..49d7f81 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserSearchBean.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserSearchBean.java @@ -38,6 +38,8 @@ public class UserSearchBean extends UserBean { protected List<String> fullTextSearch; protected boolean activeAndInactive; + public UserSearchBean() {} + public UserSearchBean(String id, String firstName, String name, String mail, String phoneNumber, String role, String qualification, String organization, boolean active) { super(id, firstName, name, mail, phoneNumber, role, qualification, organization, active); } @@ -73,4 +75,8 @@ public class UserSearchBean extends UserBean { public void setActiveAndInactive(boolean activeAndInactive) { this.activeAndInactive = activeAndInactive; } + + public void setShowDisable(boolean showDisable) { + this.activeAndInactive = showDisable; + } } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/converter/JacksonMapperUtil.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/converter/JacksonMapperUtil.java new file mode 100644 index 0000000..ad7f2c4 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/converter/JacksonMapperUtil.java @@ -0,0 +1,29 @@ +package fr.ifremer.coselmar.converter; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class JacksonMapperUtil { + + public static ObjectMapper getMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + objectMapper.enable(SerializationFeature.EAGER_SERIALIZER_FETCH); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withSetterVisibility(JsonAutoDetect.Visibility.ANY) + .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)); + + return objectMapper; + + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/CoselmarJacksonProvider.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/CoselmarJacksonProvider.java new file mode 100644 index 0000000..a288117 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/CoselmarJacksonProvider.java @@ -0,0 +1,32 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; +import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider; +import org.nuiton.util.pagination.PaginationResult; + +import javax.ws.rs.Consumes; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.Produces; +import javax.ws.rs.ext.ParamConverter; +import javax.ws.rs.ext.Provider; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +@Provider +@Consumes({"application/json", "application/*+json", "text/json"}) +@Produces({"application/json", "application/*+json", "text/json"}) +public class CoselmarJacksonProvider extends ResteasyJackson2Provider { + + public CoselmarJacksonProvider() { + ObjectMapper mapper = JacksonMapperUtil.getMapper(); + this.setMapper(mapper); + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/CoselmarParamConverterProvider.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/CoselmarParamConverterProvider.java new file mode 100644 index 0000000..3ede74f --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/CoselmarParamConverterProvider.java @@ -0,0 +1,92 @@ +package fr.ifremer.coselmar.providers; + +/*- + * #%L + * DEPHY-Graph + * %% + * Copyright (C) 2018 - 2019 Inra + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import fr.ifremer.coselmar.beans.DocumentBean; +import fr.ifremer.coselmar.beans.DocumentSearchBean; +import fr.ifremer.coselmar.beans.DocumentSearchExample; +import fr.ifremer.coselmar.beans.LinkBean; +import fr.ifremer.coselmar.beans.QuestionBean; +import fr.ifremer.coselmar.beans.QuestionSearchBean; +import fr.ifremer.coselmar.beans.QuestionSearchExample; +import fr.ifremer.coselmar.beans.SearchExample; +import fr.ifremer.coselmar.beans.UserBean; +import fr.ifremer.coselmar.beans.UserSearchBean; +import org.nuiton.util.pagination.PaginationResult; + +import javax.ws.rs.ext.ParamConverter; +import javax.ws.rs.ext.ParamConverterProvider; +import javax.ws.rs.ext.Provider; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Date; + +/** + * @author ymartel (martel@codelutin.com) + */ +@Provider +public class CoselmarParamConverterProvider implements ParamConverterProvider { + + @Override + public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) { + + // Specific converter for Metadata param. + if (rawType.isAssignableFrom(Date.class)) { + return (ParamConverter<T>) new DateParamConverter(); + + } else if (rawType.isAssignableFrom(UserBean.class)) { + return (ParamConverter<T>) new UserBeanParamConverter(); + + } else if (rawType.isAssignableFrom(UserSearchBean.class)) { + return (ParamConverter<T>) new UserSearchBeanParamConverter(); + + } else if (rawType.isAssignableFrom(QuestionBean.class)) { + return (ParamConverter<T>) new QuestionBeanParamConverter(); + + } else if (rawType.isAssignableFrom(QuestionSearchBean.class)) { + return (ParamConverter<T>) new QuestionSearchBeanParamConverter(); + + } else if (rawType.isAssignableFrom(SearchExample.class)) { + return (ParamConverter<T>) new SearchExampleParamConverter(); + + } else if (rawType.isAssignableFrom(QuestionSearchExample.class)) { + return (ParamConverter<T>) new QuestionSearchExampleParamConverter(); + + } else if (rawType.isAssignableFrom(DocumentSearchExample.class)) { + return (ParamConverter<T>) new DocumentSearchExampleParamConverter(); + + } else if (rawType.isAssignableFrom(DocumentBean.class)) { + return (ParamConverter<T>) new DocumentBeanParamConverter(); + + } else if (rawType.isAssignableFrom(DocumentSearchBean.class)) { + return (ParamConverter<T>) new DocumentSearchBeanParamConverter(); + + } else if (rawType.isAssignableFrom(LinkBean.class)) { + return (ParamConverter<T>) new LinkBeanParamConverter(); + + } + + return null; + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DateParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DateParamConverter.java new file mode 100644 index 0000000..df9ae49 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DateParamConverter.java @@ -0,0 +1,28 @@ +package fr.ifremer.coselmar.providers; + +import org.apache.commons.lang3.StringUtils; + +import javax.ws.rs.ext.ParamConverter; +import java.util.Date; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class DateParamConverter implements ParamConverter<Date> { + @Override + public Date fromString(String value) { + if (StringUtils.isNotBlank(value)) { + long time = Long.parseLong(value); + return new Date(time); + } + return null; + } + + @Override + public String toString(Date value) { + if (value != null) { + return String.valueOf(value.getTime()); + } + return null; + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DocumentBeanParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DocumentBeanParamConverter.java new file mode 100644 index 0000000..a611f97 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DocumentBeanParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.DocumentBean; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class DocumentBeanParamConverter implements ParamConverter<DocumentBean> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public DocumentBean fromString(String value) { + try { + return mapper.readerFor(DocumentBean.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(DocumentBean value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DocumentSearchBeanParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DocumentSearchBeanParamConverter.java new file mode 100644 index 0000000..f36875a --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DocumentSearchBeanParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.DocumentSearchBean; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class DocumentSearchBeanParamConverter implements ParamConverter<DocumentSearchBean> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public DocumentSearchBean fromString(String value) { + try { + return mapper.readerFor(DocumentSearchBean.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(DocumentSearchBean value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DocumentSearchExampleParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DocumentSearchExampleParamConverter.java new file mode 100644 index 0000000..818abc9 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/DocumentSearchExampleParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.DocumentSearchExample; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class DocumentSearchExampleParamConverter implements ParamConverter<DocumentSearchExample> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public DocumentSearchExample fromString(String value) { + try { + return mapper.readerFor(DocumentSearchExample.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(DocumentSearchExample value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/LinkBeanParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/LinkBeanParamConverter.java new file mode 100644 index 0000000..8fae188 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/LinkBeanParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.LinkBean; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class LinkBeanParamConverter implements ParamConverter<LinkBean> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public LinkBean fromString(String value) { + try { + return mapper.readerFor(LinkBean.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(LinkBean value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/QuestionBeanParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/QuestionBeanParamConverter.java new file mode 100644 index 0000000..95889ec --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/QuestionBeanParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.QuestionBean; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class QuestionBeanParamConverter implements ParamConverter<QuestionBean> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public QuestionBean fromString(String value) { + try { + return mapper.readerFor(QuestionBean.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(QuestionBean value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/QuestionSearchBeanParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/QuestionSearchBeanParamConverter.java new file mode 100644 index 0000000..43b525a --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/QuestionSearchBeanParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.QuestionSearchBean; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class QuestionSearchBeanParamConverter implements ParamConverter<QuestionSearchBean> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public QuestionSearchBean fromString(String value) { + try { + return mapper.readerFor(QuestionSearchBean.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(QuestionSearchBean value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/QuestionSearchExampleParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/QuestionSearchExampleParamConverter.java new file mode 100644 index 0000000..6aa3170 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/QuestionSearchExampleParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.QuestionSearchExample; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class QuestionSearchExampleParamConverter implements ParamConverter<QuestionSearchExample> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public QuestionSearchExample fromString(String value) { + try { + return mapper.readerFor(QuestionSearchExample.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(QuestionSearchExample value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/SearchExampleParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/SearchExampleParamConverter.java new file mode 100644 index 0000000..7071aec --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/SearchExampleParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.SearchExample; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class SearchExampleParamConverter implements ParamConverter<SearchExample> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public SearchExample fromString(String value) { + try { + return mapper.readerFor(SearchExample.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(SearchExample value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/UserBeanParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/UserBeanParamConverter.java new file mode 100644 index 0000000..3b06080 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/UserBeanParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.UserBean; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class UserBeanParamConverter implements ParamConverter<UserBean> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public UserBean fromString(String value) { + try { + return mapper.readerFor(UserBean.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(UserBean value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/UserSearchBeanParamConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/UserSearchBeanParamConverter.java new file mode 100644 index 0000000..1a3956b --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/providers/UserSearchBeanParamConverter.java @@ -0,0 +1,36 @@ +package fr.ifremer.coselmar.providers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.ifremer.coselmar.beans.UserSearchBean; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.ext.ParamConverter; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class UserSearchBeanParamConverter implements ParamConverter<UserSearchBean> { + + protected final ObjectMapper mapper = JacksonMapperUtil.getMapper(); + + @Override + public UserSearchBean fromString(String value) { + try { + return mapper.readerFor(UserSearchBean.class).readValue(value); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + @Override + public String toString(UserSearchBean value) { + try { + return mapper.writer().writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new ProcessingException(e); + } + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarApplication.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarApplication.java new file mode 100644 index 0000000..c5ff69b --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarApplication.java @@ -0,0 +1,92 @@ +package fr.ifremer.coselmar.services; + +import fr.ifremer.coselmar.beans.DocumentBean; +import fr.ifremer.coselmar.beans.DocumentSearchBean; +import fr.ifremer.coselmar.beans.DocumentSearchExample; +import fr.ifremer.coselmar.beans.LinkBean; +import fr.ifremer.coselmar.beans.QuestionBean; +import fr.ifremer.coselmar.beans.QuestionSearchBean; +import fr.ifremer.coselmar.beans.QuestionSearchExample; +import fr.ifremer.coselmar.beans.SearchExample; +import fr.ifremer.coselmar.beans.UserBean; +import fr.ifremer.coselmar.beans.UserSearchBean; +import fr.ifremer.coselmar.providers.CoselmarParamConverterProvider; +import fr.ifremer.coselmar.providers.CoselmarJacksonProvider; +import fr.ifremer.coselmar.services.errors.CoselmarTechnicalExceptionMapper; +import fr.ifremer.coselmar.services.errors.InvalidCredentialExceptionMapper; +import fr.ifremer.coselmar.services.errors.MailAlreadyExistingExceptionMapper; +import fr.ifremer.coselmar.services.errors.NoResultExceptionMapper; +import fr.ifremer.coselmar.services.errors.TopiaNoResultExceptionMapper; +import fr.ifremer.coselmar.services.errors.UnauthorizedExceptionMapper; +import fr.ifremer.coselmar.services.filter.CORSResponseFilter; +import fr.ifremer.coselmar.services.filter.CoselmarRestRequestFilter; +import fr.ifremer.coselmar.services.filter.CoselmarTopiaTransactionFilter; +import fr.ifremer.coselmar.services.v1.AdminWebService; +import fr.ifremer.coselmar.services.v1.DocumentsWebService; +import fr.ifremer.coselmar.services.v1.GeneralWebService; +import fr.ifremer.coselmar.services.v1.HealthService; +import fr.ifremer.coselmar.services.v1.QuestionsWebService; +import fr.ifremer.coselmar.services.v1.UsersWebService; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; +import java.util.HashSet; +import java.util.Set; + +/** + * @author ymartel (martel@codelutin.com) + */ +@ApplicationPath("") +public class CoselmarApplication extends Application { + + Set<Object> singletons; + + public CoselmarApplication() {} + + @Override + public Set<Object> getSingletons() { + if (singletons == null) { + singletons = new HashSet<>(); + + singletons.add(new AdminWebService()); + singletons.add(new DocumentsWebService()); + singletons.add(new GeneralWebService()); + singletons.add(new QuestionsWebService()); + singletons.add(new UsersWebService()); + singletons.add(new HealthService()); + + singletons.add(new CoselmarRestRequestFilter()); + singletons.add(new CORSResponseFilter()); + singletons.add(new CoselmarTopiaTransactionFilter()); + + singletons.add(new CoselmarParamConverterProvider()); + singletons.add(new CoselmarJacksonProvider()); + } + return singletons; + } + + @Override + public Set<Class<?>> getClasses() { + Set<Class<?>> classes = new HashSet<>(); + + classes.add(InvalidCredentialExceptionMapper.class); + classes.add(UnauthorizedExceptionMapper.class); + classes.add(NoResultExceptionMapper.class); + classes.add(MailAlreadyExistingExceptionMapper.class); + classes.add(CoselmarTechnicalExceptionMapper.class); + classes.add(TopiaNoResultExceptionMapper.class); + + classes.add(UserBean.class); + classes.add(UserSearchBean.class); + classes.add(DocumentBean.class); + classes.add(DocumentSearchBean.class); + classes.add(QuestionBean.class); + classes.add(QuestionSearchBean.class); + classes.add(QuestionSearchExample.class); + classes.add(DocumentSearchExample.class); + classes.add(SearchExample.class); + classes.add(LinkBean.class); + + return classes; + } +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRender.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRender.java deleted file mode 100644 index 8b9337f..0000000 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRender.java +++ /dev/null @@ -1,75 +0,0 @@ -package fr.ifremer.coselmar.services; - -/* - * #%L - * Coselmar :: Rest Services - * $Id:$ - * $HeadURL:$ - * %% - * Copyright (C) 2014 Ifremer, Code Lutin - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -import fr.ifremer.coselmar.converter.JsonHelper; -import org.debux.webmotion.server.call.Call; -import org.debux.webmotion.server.call.HttpContext; -import org.debux.webmotion.server.mapping.Mapping; -import org.debux.webmotion.server.render.Render; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; - -/** - * @author ymartel <martel@codelutin.com> - */ -public class CoselmarRender<T> extends Render { - - protected T model; - - public CoselmarRender(T model) { - this.model = model; - } - - @Override - public void create(Mapping mapping, Call call) throws IOException, ServletException { - - HttpContext context = call.getContext(); - HttpServletResponse response = context.getResponse(); - response.setContentType("application/json"); - - CoselmarServicesApplicationContext applicationContext = - CoselmarServicesApplicationContext.getApplicationContext(context.getServletContext()); - - //TODO ymartel 20141030 : think about formException render -// if (model instanceof InvalidFormException) { -// -// response.setStatus(HttpServletResponse.SC_BAD_REQUEST); -// -// } - - boolean devMode = applicationContext.getApplicationConfig().isDevMode(); - - JsonHelper gson = new JsonHelper(devMode); - String json = gson.toJson(model); - - PrintWriter out = context.getOut(); - out.print(json); - - } -} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestApplicationListener.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestApplicationListener.java index c6396e5..3a858db 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestApplicationListener.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestApplicationListener.java @@ -33,22 +33,17 @@ import fr.ifremer.coselmar.beans.QuestionSearchBean; import fr.ifremer.coselmar.beans.QuestionSearchExample; import fr.ifremer.coselmar.beans.UserBean; import fr.ifremer.coselmar.beans.UserSearchBean; -import fr.ifremer.coselmar.converter.DateConverter; -import fr.ifremer.coselmar.converter.JsonArrayConverter; -import fr.ifremer.coselmar.converter.JsonConverter; -import fr.ifremer.coselmar.services.injector.CoselmarRestRequestContextInjector; -import fr.ifremer.coselmar.services.injector.CoselmarServicesInjector; -import org.debux.webmotion.server.WebMotionServerListener; -import org.debux.webmotion.server.call.ServerContext; -import org.debux.webmotion.server.mapping.Mapping; -import java.util.Date; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; import java.util.Set; /** * @author ymartel <martel@codelutin.com> */ -public class CoselmarRestApplicationListener implements WebMotionServerListener { +@WebListener +public class CoselmarRestApplicationListener implements ServletContextListener { protected static final Set<Class<?>> BEAN_TYPES = Sets.<Class<?>>newHashSet( DocumentBean.class, @@ -62,39 +57,39 @@ public class CoselmarRestApplicationListener implements WebMotionServerListener ); @Override - public void onStart(Mapping mapping, ServerContext serverContext) { + public void contextInitialized(ServletContextEvent sce) { // init application context // CoselmarServicesApplicationContext applicationContext = - CoselmarServicesApplicationContext.getApplicationContext(); - - CoselmarServicesApplicationContext.setApplicationContext(serverContext.getServletContext(), applicationContext); - - // init converters // - - serverContext.addConverter(new DateConverter(), Date.class); - - for (Class<?> beanType : BEAN_TYPES) { - - serverContext.addConverter(JsonConverter.newConverter(beanType), beanType); - - JsonArrayConverter<?> converter = JsonArrayConverter.newConverter(beanType); - serverContext.addConverter(converter, converter.getDefaultType()); - - } - - // init injectors // - serverContext.addInjector(new CoselmarRestRequestContextInjector()); - serverContext.addInjector(new CoselmarServicesInjector()); + CoselmarServicesApplicationContext.getInstance(); + + CoselmarServicesApplicationContext.setApplicationContext(sce.getServletContext(), applicationContext); + +// // init converters // +// +// sce.addConverter(new DateConverter(), Date.class); +// +// for (Class<?> beanType : BEAN_TYPES) { +// +// serverContext.addConverter(JsonConverter.newConverter(beanType), beanType); +// +// JsonArrayConverter<?> converter = JsonArrayConverter.newConverter(beanType); +// serverContext.addConverter(converter, converter.getDefaultType()); +// +// } +// +// // init injectors // +// serverContext.addInjector(new CoselmarRestRequestContextInjector()); +// serverContext.addInjector(new CoselmarServicesInjector()); } @Override - public void onStop(ServerContext serverContext) { + public void contextDestroyed(ServletContextEvent sce) { // Get application context CoselmarServicesApplicationContext applicationContext = - CoselmarServicesApplicationContext.getApplicationContext(serverContext.getServletContext()); + CoselmarServicesApplicationContext.getApplicationContext(sce.getServletContext()); // close it (and all underlined resources) if (applicationContext != null) { @@ -102,4 +97,5 @@ public class CoselmarRestApplicationListener implements WebMotionServerListener } } + } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestRequestContext.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestRequestContext.java deleted file mode 100644 index a6eab47..0000000 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestRequestContext.java +++ /dev/null @@ -1,58 +0,0 @@ -package fr.ifremer.coselmar.services; - -/* - * #%L - * Coselmar :: Rest Services - * $Id:$ - * $HeadURL:$ - * %% - * Copyright (C) 2014 Ifremer, Code Lutin - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -import org.debux.webmotion.server.call.HttpContext; - -/** - * @author ymartel <martel@codelutin.com> - */ -public class CoselmarRestRequestContext { - - protected static final String REQUEST_COSELMAR_REQUEST_CONTEXT = "coselmar_CoselmarRequestContext"; - - public static CoselmarRestRequestContext getRequestContext(HttpContext httpContext) { - - CoselmarRestRequestContext result = (CoselmarRestRequestContext) - httpContext.getRequest().getAttribute(REQUEST_COSELMAR_REQUEST_CONTEXT); - return result; - } - - public static void setRequestContext(HttpContext httpContext, - CoselmarRestRequestContext requestContext) { - httpContext.getRequest().setAttribute(REQUEST_COSELMAR_REQUEST_CONTEXT, requestContext); - } - - protected CoselmarServicesContext servicesContext; - - public void setServicesContext(CoselmarServicesContext servicesContext) { - this.servicesContext = servicesContext; - } - - public CoselmarServicesContext getServicesContext() { - return this.servicesContext; - } - -} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestUtil.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestUtil.java deleted file mode 100644 index 005fc6e..0000000 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestUtil.java +++ /dev/null @@ -1,61 +0,0 @@ -package fr.ifremer.coselmar.services; - -/* - * #%L - * Coselmar :: Rest Services - * $Id:$ - * $HeadURL:$ - * %% - * Copyright (C) 2014 Ifremer, Code Lutin - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -import org.apache.commons.lang3.StringUtils; -import org.debux.webmotion.server.call.HttpContext; - -import javax.servlet.http.HttpServletResponse; - -/** - * @author ymartel <martel@codelutin.com> - */ -public final class CoselmarRestUtil { - - public static final String HEADER_ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; - - public static final String HEADER_ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; - - private CoselmarRestUtil() { - throw new IllegalAccessError("Utility class"); - } - - public static void prepareResponse(HttpContext context) { - - HttpServletResponse response = context.getResponse(); - response.setHeader(HttpContext.HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*"); - response.setHeader(HttpContext.HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); - - } - - public static void addOptionCorsHeaders(HttpContext context) { - - String requestHeaders = context.getHeader(HEADER_ACCESS_CONTROL_REQUEST_HEADERS); - - if (StringUtils.isNotBlank(requestHeaders)) { - context.getResponse().addHeader(HEADER_ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders); - } - } -} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarServicesApplicationContext.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarServicesApplicationContext.java index e1b414f..373d232 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarServicesApplicationContext.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarServicesApplicationContext.java @@ -37,6 +37,8 @@ import org.apache.log4j.PropertyConfigurator; import org.nuiton.i18n.I18n; import org.nuiton.i18n.init.DefaultI18nInitializer; import org.nuiton.i18n.init.I18nInitializer; +import org.nuiton.topia.persistence.BeanTopiaConfiguration; +import org.nuiton.topia.persistence.TopiaConfigurationBuilder; import javax.servlet.ServletContext; import java.io.File; @@ -56,25 +58,26 @@ public class CoselmarServicesApplicationContext implements CoselmarApplicationCo protected static final String APPLICATION_CONTEXT_PARAMETER = "coselmar_CoselmarApplicationContext"; - protected static CoselmarServicesApplicationContext applicationContext; + protected static CoselmarServicesApplicationContext instance; - public static CoselmarServicesApplicationContext getApplicationContext() { + public static CoselmarServicesApplicationContext getInstance() { - if (applicationContext == null) { + if (instance == null) { CoselmarServicesConfig applicationConfig = new CoselmarServicesConfig("coselmar-services.properties"); Map<String, String> topiaProperties = applicationConfig.getTopiaProperties(); - CoselmarTopiaApplicationContext coselmarTopiaApplicationContext = new CoselmarTopiaApplicationContext(topiaProperties); + BeanTopiaConfiguration topiaConfiguration = new TopiaConfigurationBuilder().readMap(topiaProperties); + CoselmarTopiaApplicationContext coselmarTopiaApplicationContext = new CoselmarTopiaApplicationContext(topiaConfiguration); LuceneUtils luceneUtils = new LuceneUtils(applicationConfig); - applicationContext = new CoselmarServicesApplicationContext(applicationConfig, coselmarTopiaApplicationContext, luceneUtils); + instance = new CoselmarServicesApplicationContext(applicationConfig, coselmarTopiaApplicationContext, luceneUtils); - applicationContext.init(); + instance.init(); } - return applicationContext; + return instance; } public static CoselmarServicesApplicationContext getApplicationContext(ServletContext servletContext) { @@ -83,8 +86,8 @@ public class CoselmarServicesApplicationContext implements CoselmarApplicationCo return result; } - public static void setApplicationContext(CoselmarServicesApplicationContext applicationContext) { - CoselmarServicesApplicationContext.applicationContext = applicationContext; + public static void setInstance(CoselmarServicesApplicationContext instance) { + CoselmarServicesApplicationContext.instance = instance; } public static void setApplicationContext(ServletContext servletContext, diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java index 8bbcf8c..87f1d8d 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java @@ -27,35 +27,25 @@ package fr.ifremer.coselmar.services; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.JWTVerifyException; import fr.ifremer.coselmar.beans.UserWebToken; -import fr.ifremer.coselmar.config.CoselmarServicesConfig; import fr.ifremer.coselmar.exceptions.CoselmarTechnicalException; import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; -import fr.ifremer.coselmar.persistence.entity.CoselmarUserGroupTopiaDao; -import fr.ifremer.coselmar.persistence.entity.CoselmarUserTopiaDao; -import fr.ifremer.coselmar.persistence.entity.DocumentTopiaDao; -import fr.ifremer.coselmar.persistence.entity.QuestionTopiaDao; import fr.ifremer.coselmar.services.errors.InvalidCredentialException; -import fr.ifremer.coselmar.services.v1.DocumentsWebService; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.debux.webmotion.server.WebMotionController; -import org.debux.webmotion.server.call.HttpContext; import org.nuiton.topia.persistence.TopiaNoResultException; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; -import java.util.Date; -import java.util.Locale; import java.util.Map; /** * @author ymartel <martel@codelutin.com> */ -public abstract class CoselmarWebServiceSupport extends WebMotionController implements CoselmarService { +public abstract class CoselmarWebServiceSupport implements CoselmarService { private static final Log log = LogFactory.getLog(CoselmarWebServiceSupport.class); protected static final String AUTHORIZATION_HEADER = "Authorization"; @@ -67,87 +57,6 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl this.servicesContext = servicesContext; } - protected CoselmarServicesContext getServicesContext() { - //try to get it from Request context - HttpContext context; - try { - context = getContext(); - } catch (NullPointerException e) { - // not web context ? //XXX ymartel use because unit test on Documents mass import are not in web context ... - context = null; - } - if (context != null) { - CoselmarRestRequestContext requestContext = CoselmarRestRequestContext.getRequestContext(context); - this.servicesContext = requestContext.getServicesContext(); - } - return this.servicesContext; - } - - // Delegate serviceContext // - - protected Date getNow() { - return getServicesContext().getNow(); - } - - protected String getCleanMail(String email) { - return getServicesContext().getCleanMail(email); - } - - protected CoselmarPersistenceContext getPersistenceContext() { - return getServicesContext().getPersistenceContext(); - } - - protected CoselmarServicesConfig getCoselmarServicesConfig() { - return getServicesContext().getCoselmarServicesConfig(); - } - - protected Locale getLocale() { - return servicesContext.getLocale(); - } - - // Services // - - public DocumentsWebService getDocumentsService() { - return newService(DocumentsWebService.class); - } - - protected <E extends CoselmarService> E newService(Class<E> serviceClass) { - return getServicesContext().newService(serviceClass); - } - - // Persistence // - protected DocumentTopiaDao getDocumentDao() { - return getPersistenceContext().getDocumentDao(); - } - - protected CoselmarUserTopiaDao getCoselmarUserDao() { - return getPersistenceContext().getCoselmarUserDao(); - } - - protected CoselmarUserGroupTopiaDao getCoselmarUserGroupDao() { - return getPersistenceContext().getCoselmarUserGroupDao(); - } - - protected QuestionTopiaDao getQuestionDao() { - return getPersistenceContext().getQuestionDao(); - } - - public void commit() { - getPersistenceContext().commit(); - } - - public void rollback() { - getPersistenceContext().rollback(); - } - - /** - * Just get the Authorization header from context - * @return - */ - protected String getAuthorizationHeader() { - return getContext().getHeader(AUTHORIZATION_HEADER); - } - /** * Check the authorization code. * Get the token from the authorization and reconstitute JWT user token. @@ -157,13 +66,13 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl * * @throws InvalidCredentialException if token is not valid. * - * @Deprecated since 0.8 : prefer use {@link #checkUserAuthentication(String)} that also check user validity. + * @Deprecated since 0.8 : prefer use {@link #checkUserAuthentication(CoselmarServicesContext, String)} that also check user validity. */ @Deprecated protected UserWebToken checkAuthentication(String authorization) throws InvalidCredentialException { try { - String webSecurityKey = getServicesContext().getCoselmarServicesConfig().getWebSecurityKey(); + String webSecurityKey = CoselmarServicesApplicationContext.getInstance().getApplicationConfig().getWebSecurityKey(); JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); String token = StringUtils.replace(authorization, "Bearer ", ""); @@ -214,10 +123,10 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl * * @throws InvalidCredentialException if token is not valid or if user does not exist. */ - protected CoselmarUser checkUserAuthentication(String authorization) throws InvalidCredentialException { + protected CoselmarUser checkUserAuthentication(CoselmarServicesContext servicesContext, String authorization) throws InvalidCredentialException { try { - String webSecurityKey = getServicesContext().getCoselmarServicesConfig().getWebSecurityKey(); + String webSecurityKey = CoselmarServicesApplicationContext.getInstance().getApplicationConfig().getWebSecurityKey(); JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); String token = StringUtils.replace(authorization, "Bearer ", ""); @@ -225,8 +134,8 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl UserWebToken userWebToken = new UserWebToken(claims); // check user still exist - String userId = getFullUserIdFromShort(userWebToken.getUserId()); - CoselmarUser coselmarUser = getCoselmarUserDao().forTopiaIdEquals(userId).findAny(); + String userId = getFullUserIdFromShort(servicesContext.getPersistenceContext(), userWebToken.getUserId()); + CoselmarUser coselmarUser = servicesContext.getPersistenceContext().getCoselmarUserDao().forTopiaIdEquals(userId).findAny(); return coselmarUser; @@ -277,8 +186,8 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl * * @return the complete id, with class FQN. */ - protected String getFullUserIdFromShort(String shortUserId) { - return CoselmarUser.class.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + shortUserId; + protected String getFullUserIdFromShort(CoselmarPersistenceContext persistenceContext, String shortUserId) { + return CoselmarUser.class.getCanonicalName() + persistenceContext.getTopiaIdFactory().getSeparator() + shortUserId; } /** @@ -289,8 +198,8 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl * * @return the complete id, with class FQN. */ - protected String getFullIdFromShort(Class clazz, String shortId) { - return clazz.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + shortId; + protected String getFullIdFromShort(CoselmarPersistenceContext persistenceContext, Class clazz, String shortId) { + return clazz.getCanonicalName() + persistenceContext.getTopiaIdFactory().getSeparator() + shortId; } /** @@ -300,7 +209,7 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl * * @return a light id. */ - protected String getShortIdFromFull(String fullId) { - return getPersistenceContext().getTopiaIdFactory().getRandomPart(fullId); + protected String getShortIdFromFull(CoselmarPersistenceContext persistenceContext, String fullId) { + return persistenceContext.getTopiaIdFactory().getRandomPart(fullId); } } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/CoselmarTechnicalExceptionMapper.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/CoselmarTechnicalExceptionMapper.java new file mode 100644 index 0000000..55661f8 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/CoselmarTechnicalExceptionMapper.java @@ -0,0 +1,23 @@ +package fr.ifremer.coselmar.services.errors; + +import fr.ifremer.coselmar.exceptions.CoselmarTechnicalException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class CoselmarTechnicalExceptionMapper implements ExceptionMapper<CoselmarTechnicalException> { + + private static final Log log = LogFactory.getLog(CoselmarTechnicalExceptionMapper.class); + + @Override + public Response toResponse(CoselmarTechnicalException exception) { + log.warn("Technical exception", exception); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/InvalidCredentialExceptionMapper.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/InvalidCredentialExceptionMapper.java new file mode 100644 index 0000000..a8ec679 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/InvalidCredentialExceptionMapper.java @@ -0,0 +1,22 @@ +package fr.ifremer.coselmar.services.errors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class InvalidCredentialExceptionMapper implements ExceptionMapper<InvalidCredentialException> { + + private static final Log log = LogFactory.getLog(InvalidCredentialExceptionMapper.class); + + @Override + public Response toResponse(InvalidCredentialException exception) { + log.warn("Authorization failed", exception); + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/MailAlreadyExistingExceptionMapper.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/MailAlreadyExistingExceptionMapper.java new file mode 100644 index 0000000..caf198b --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/MailAlreadyExistingExceptionMapper.java @@ -0,0 +1,22 @@ +package fr.ifremer.coselmar.services.errors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class MailAlreadyExistingExceptionMapper implements ExceptionMapper<MailAlreadyExistingException> { + + private static final Log log = LogFactory.getLog(MailAlreadyExistingExceptionMapper.class); + + @Override + public Response toResponse(MailAlreadyExistingException exception) { + log.warn("Asked mail already existing", exception); + return Response.status(Response.Status.CONFLICT).build(); + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/NoResultExceptionMapper.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/NoResultExceptionMapper.java new file mode 100644 index 0000000..c0a4c31 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/NoResultExceptionMapper.java @@ -0,0 +1,22 @@ +package fr.ifremer.coselmar.services.errors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class NoResultExceptionMapper implements ExceptionMapper<NoResultException> { + + private static final Log log = LogFactory.getLog(NoResultExceptionMapper.class); + + @Override + public Response toResponse(NoResultException exception) { + log.warn("No Result found", exception); + return Response.status(Response.Status.NOT_FOUND).build(); + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/TopiaNoResultExceptionMapper.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/TopiaNoResultExceptionMapper.java new file mode 100644 index 0000000..bfd8661 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/TopiaNoResultExceptionMapper.java @@ -0,0 +1,23 @@ +package fr.ifremer.coselmar.services.errors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.persistence.TopiaNoResultException; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class TopiaNoResultExceptionMapper implements ExceptionMapper<TopiaNoResultException> { + + private static final Log log = LogFactory.getLog(TopiaNoResultExceptionMapper.class); + + @Override + public Response toResponse(TopiaNoResultException exception) { + log.warn("No Result found", exception); + return Response.status(Response.Status.NOT_FOUND).build(); + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/UnauthorizedExceptionMapper.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/UnauthorizedExceptionMapper.java new file mode 100644 index 0000000..ee85cf6 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/errors/UnauthorizedExceptionMapper.java @@ -0,0 +1,22 @@ +package fr.ifremer.coselmar.services.errors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class UnauthorizedExceptionMapper implements ExceptionMapper<UnauthorizedException> { + + private static final Log log = LogFactory.getLog(UnauthorizedExceptionMapper.class); + + @Override + public Response toResponse(UnauthorizedException exception) { + log.warn("Unauthorized exception", exception); + return Response.status(Response.Status.FORBIDDEN).build(); + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CORSResponseFilter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CORSResponseFilter.java new file mode 100644 index 0000000..3836659 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CORSResponseFilter.java @@ -0,0 +1,46 @@ +package fr.ifremer.coselmar.services.filter; + +/*- + * #%L + * DEPHY-Graph + * %% + * Copyright (C) 2018 - 2019 Inra + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.MultivaluedMap; +import java.io.IOException; + +/** + * @author ymartel (martel@codelutin.com) + */ +public class CORSResponseFilter implements ContainerResponseFilter { + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) { + + MultivaluedMap<String, Object> headers = responseContext.getHeaders(); + headers.add("Access-Control-Allow-Origin", "*"); + headers.add("Access-Control-Allow-Credentials", "true"); + headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS"); + headers.add("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, Content-Transfer-Encoding"); + } + +} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CoselmarRestRequestFilter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CoselmarRestRequestFilter.java index 3c75372..38b4ea0 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CoselmarRestRequestFilter.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CoselmarRestRequestFilter.java @@ -24,110 +24,76 @@ package fr.ifremer.coselmar.services.filter; * #L% */ +import com.google.common.collect.ImmutableList; import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; -import fr.ifremer.coselmar.services.CoselmarRestRequestContext; -import fr.ifremer.coselmar.services.CoselmarRestUtil; import fr.ifremer.coselmar.services.CoselmarServicesApplicationContext; import fr.ifremer.coselmar.services.CoselmarServicesContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.debux.webmotion.server.WebMotionFilter; -import org.debux.webmotion.server.call.Call; -import org.debux.webmotion.server.call.HttpContext; -import org.debux.webmotion.server.render.Render; -import org.debux.webmotion.server.render.RenderStatus; +import org.jboss.resteasy.spi.ResteasyProviderFactory; -import javax.servlet.http.HttpServletResponse; +import javax.servlet.annotation.WebFilter; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import java.util.List; import java.util.Locale; /** * @author ymartel <martel@codelutin.com> */ -public class CoselmarRestRequestFilter extends WebMotionFilter { +public class CoselmarRestRequestFilter implements ContainerRequestFilter { public static final String REQUEST_PERMISSION_PARAMETER = "permission"; public static final String REQUEST_HEADER_SESSION_TOKEN = "X-Coselmar-Session-Token"; + public static final ImmutableList<Locale> ACCEPT_LANGUAGES = ImmutableList.of(Locale.FRENCH, Locale.ENGLISH); + /** Logger. */ private static final Log log = LogFactory.getLog(CoselmarRestRequestFilter.class); - public void inject(Call call, HttpContext context) { - - prepareRequestContext(context); - - if (HttpContext.METHOD_OPTIONS.equals(context.getMethod())) { - - Render render = call.getRender(); - - if (render instanceof RenderStatus && - HttpServletResponse.SC_OK == ((RenderStatus) render).getCode()) { - - // operation accepted - CoselmarRestUtil.addOptionCorsHeaders(context); - } - } - - CoselmarRestUtil.prepareResponse(context); - - doProcess(); - - } - - protected CoselmarRestRequestContext prepareRequestContext(HttpContext context) { + @Override + public void filter(ContainerRequestContext context) { Locale locale = getUserLocale(context); CoselmarServicesApplicationContext applicationContext = - CoselmarServicesApplicationContext.getApplicationContext(context.getServletContext()); + CoselmarServicesApplicationContext.getInstance(); CoselmarPersistenceContext persistenceContext = - CoselmarTopiaTransactionFilter.getPersistenceContext(context.getRequest()); + CoselmarTopiaTransactionFilter.getPersistenceContext(context); CoselmarServicesContext serviceContext = applicationContext.newServiceContext(persistenceContext, locale); - CoselmarRestRequestContext requestContext = new CoselmarRestRequestContext(); - requestContext.setServicesContext(serviceContext); - - CoselmarRestRequestContext.setRequestContext(context, requestContext); - - return requestContext; + ResteasyProviderFactory.pushContext(CoselmarServicesContext.class, serviceContext); } - protected Locale getUserLocale(HttpContext context) { - String language = context.getHeader(HttpContext.HEADER_LANGUAGE); + protected Locale getUserLocale(ContainerRequestContext context) { - if (log.isInfoEnabled()) { - log.info("Found Accept-Language: " + language); - } + Locale language = context.getLanguage(); - // search best locale accepted by the server 'fr' or 'en' - if (language != null) { - String[] allLanguage = language.split(","); - int i = 0; - do { - language = allLanguage[i].substring(0, 2); - if ( !language.equals("fr") && !language.equals("en") ) { - // language not accepted - language = null; - } - i++; - } while ( language == null && i < allLanguage.length ); - } + if (! ACCEPT_LANGUAGES.contains(language)) { - if (language == null) { + List<Locale> languages = context.getAcceptableLanguages(); - language = Locale.FRENCH.getLanguage(); + if (log.isDebugEnabled()) { + log.debug("Found Accept-Language: " + languages.stream() + .map(Locale::getDisplayName) + .reduce((l1, l2) -> l1 + ", " + l2)); + } + + language = languages.stream() + .filter(ACCEPT_LANGUAGES::contains) + .findFirst() + .orElse(ACCEPT_LANGUAGES.get(0)); if (log.isInfoEnabled()) { log.info("Use default language: " + language); } - } - Locale locale = new Locale(language); - return locale; + return language; } } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CoselmarTopiaTransactionFilter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CoselmarTopiaTransactionFilter.java index 5ba8f28..046171a 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CoselmarTopiaTransactionFilter.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/filter/CoselmarTopiaTransactionFilter.java @@ -35,6 +35,7 @@ import org.nuiton.web.filter.TypedTopiaTransactionFilter; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; +import javax.ws.rs.container.ContainerRequestContext; /** * @author ymartel <martel@codelutin.com> @@ -69,4 +70,10 @@ public class CoselmarTopiaTransactionFilter extends TypedTopiaTransactionFilter< } } + public static CoselmarPersistenceContext getPersistenceContext(ContainerRequestContext context) { + CoselmarPersistenceContext topiaContext = (CoselmarPersistenceContext) + context.getProperty(TOPIA_TRANSACTION_REQUEST_ATTRIBUTE); + return topiaContext; + } + } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/injector/CoselmarRestRequestContextInjector.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/injector/CoselmarRestRequestContextInjector.java deleted file mode 100644 index a568195..0000000 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/injector/CoselmarRestRequestContextInjector.java +++ /dev/null @@ -1,53 +0,0 @@ -package fr.ifremer.coselmar.services.injector; - -/* - * #%L - * Coselmar :: Rest Services - * $Id:$ - * $HeadURL:$ - * %% - * Copyright (C) 2014 Ifremer, Code Lutin - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -import fr.ifremer.coselmar.services.CoselmarRestRequestContext; -import org.debux.webmotion.server.call.Call; -import org.debux.webmotion.server.call.HttpContext; -import org.debux.webmotion.server.handler.ExecutorParametersInjectorHandler; -import org.debux.webmotion.server.mapping.Mapping; - -import java.lang.reflect.Type; - -/** - * @author ymartel <martel@codelutin.com> - */ -public class CoselmarRestRequestContextInjector implements ExecutorParametersInjectorHandler.Injector { - - @Override - public CoselmarRestRequestContext getValue(Mapping m, Call call, String name, Class<?> type, Type generic) { - - CoselmarRestRequestContext result = null; - if (CoselmarRestRequestContext.class.isAssignableFrom(type)) { - HttpContext httpContext = call.getContext(); - - result = CoselmarRestRequestContext.getRequestContext(httpContext); - } - - return result; - - } -} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/injector/CoselmarServicesInjector.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/injector/CoselmarServicesInjector.java deleted file mode 100644 index 5086302..0000000 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/injector/CoselmarServicesInjector.java +++ /dev/null @@ -1,56 +0,0 @@ -package fr.ifremer.coselmar.services.injector; - -/* - * #%L - * Coselmar :: Rest Services - * $Id:$ - * $HeadURL:$ - * %% - * Copyright (C) 2014 Ifremer, Code Lutin - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -import fr.ifremer.coselmar.services.CoselmarRestRequestContext; -import fr.ifremer.coselmar.services.CoselmarService; -import org.debux.webmotion.server.call.Call; -import org.debux.webmotion.server.call.HttpContext; -import org.debux.webmotion.server.handler.ExecutorParametersInjectorHandler; -import org.debux.webmotion.server.mapping.Mapping; - -import java.lang.reflect.Type; - -/** - * @author ymartel <martel@codelutin.com> - */ -public class CoselmarServicesInjector implements ExecutorParametersInjectorHandler.Injector { - - @Override - public CoselmarService getValue(Mapping m, Call call, String name, Class type, Type generic) { - - CoselmarService result = null; - if (CoselmarService.class.isAssignableFrom(type)) { - HttpContext httpContext = call.getContext(); - - CoselmarRestRequestContext requestContext = - CoselmarRestRequestContext.getRequestContext(httpContext); - result = requestContext.getServicesContext().newService(type); - } - - return result; - - } -} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/AdminWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/AdminWebService.java index 5686aec..ecd7174 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/AdminWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/AdminWebService.java @@ -26,21 +26,29 @@ package fr.ifremer.coselmar.services.v1; import fr.ifremer.coselmar.beans.DocumentBean; import fr.ifremer.coselmar.beans.QuestionBean; -import fr.ifremer.coselmar.beans.UserWebToken; import fr.ifremer.coselmar.converter.BeanEntityConverter; import fr.ifremer.coselmar.exceptions.CoselmarTechnicalException; +import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; +import fr.ifremer.coselmar.persistence.entity.CoselmarUser; import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; import fr.ifremer.coselmar.persistence.entity.Document; import fr.ifremer.coselmar.persistence.entity.Question; +import fr.ifremer.coselmar.services.CoselmarServicesContext; import fr.ifremer.coselmar.services.CoselmarWebServiceSupport; import fr.ifremer.coselmar.services.errors.InvalidCredentialException; import fr.ifremer.coselmar.services.errors.UnauthorizedException; import fr.ifremer.coselmar.services.indexation.DocumentsIndexationService; import fr.ifremer.coselmar.services.indexation.QuestionsIndexationService; import fr.ifremer.coselmar.services.indexation.TikaUtils; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; +import javax.ws.rs.Consumes; +import javax.ws.rs.HeaderParam; +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 java.io.IOException; import java.util.List; @@ -49,18 +57,24 @@ import static org.apache.commons.logging.LogFactory.getLog; /** * @author ymartel <martel@codelutin.com> */ +@Path("") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) public class AdminWebService extends CoselmarWebServiceSupport { private static final Log log = getLog(AdminWebService.class); - public void refreshLuceneIndex() throws UnauthorizedException, InvalidCredentialException { + @POST + @Path("/v1/admin/lucene/index") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public void refreshLuceneIndex(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization) throws UnauthorizedException, InvalidCredentialException { // Check authentication - String authorization = getContext().getHeader("Authorization"); - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser userWeb = checkUserAuthentication(servicesContext, authorization); - // Who is allowed here ? Admin and user himself - if (!StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.ADMIN.name())) { + // Who is allowed here ? Admin only + if (userWeb.getRole() != CoselmarUserRole.ADMIN) { if (log.isDebugEnabled()) { String message = String.format("A non admin user try to refresh lucene index."); log.debug(message); @@ -69,16 +83,17 @@ public class AdminWebService extends CoselmarWebServiceSupport { } - QuestionsIndexationService questionsIndexationService = getServicesContext().newService(QuestionsIndexationService.class); + QuestionsIndexationService questionsIndexationService = servicesContext.newService(QuestionsIndexationService.class); try { - getServicesContext().getLuceneUtils().clearIndex(); - refreshDocumentsIndex(); + servicesContext.getLuceneUtils().clearIndex(); + refreshDocumentsIndex(servicesContext); // Get all questions - List<Question> questions = getQuestionDao().findAll(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + List<Question> questions = persistenceContext.getQuestionDao().findAll(); for (Question question : questions) { - QuestionBean questionBean = BeanEntityConverter.toLightBean(getPersistenceContext().getTopiaIdFactory(), question); + QuestionBean questionBean = BeanEntityConverter.toLightBean(persistenceContext.getTopiaIdFactory(), question); questionsIndexationService.indexQuestion(questionBean); } @@ -94,23 +109,24 @@ public class AdminWebService extends CoselmarWebServiceSupport { } } - protected void refreshDocumentsIndex() throws IOException { - DocumentsIndexationService documentsIndexationService = getServicesContext().newService(DocumentsIndexationService.class); + protected void refreshDocumentsIndex(CoselmarServicesContext servicesContext) throws IOException { + DocumentsIndexationService documentsIndexationService = servicesContext.newService(DocumentsIndexationService.class); documentsIndexationService.cleanIndex(); // get All documents - List<Document> documents = getDocumentDao().findAll(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + List<Document> documents = persistenceContext.getDocumentDao().findAll(); for (Document document : documents) { - DocumentBean documentBean = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), document); + DocumentBean documentBean = BeanEntityConverter.toBean(persistenceContext.getTopiaIdFactory(), document); if (document.isWithFile()) { // Refresh file information String fileContent = TikaUtils.getFileContent(document.getFilePath()); documentsIndexationService.indexDocument(documentBean, fileContent); // Refresh database content document.setFileContent(fileContent); - getDocumentDao().update(document); + persistenceContext.getDocumentDao().update(document); } } - commit(); + persistenceContext.commit(); } } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java index bac184f..b84ad05 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java @@ -41,12 +41,14 @@ import fr.ifremer.coselmar.beans.UserBean; import fr.ifremer.coselmar.beans.UserWebToken; import fr.ifremer.coselmar.converter.BeanEntityConverter; import fr.ifremer.coselmar.exceptions.CoselmarTechnicalException; +import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; import fr.ifremer.coselmar.persistence.entity.CoselmarUserGroup; import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; import fr.ifremer.coselmar.persistence.entity.Document; import fr.ifremer.coselmar.persistence.entity.Privacy; import fr.ifremer.coselmar.persistence.entity.Question; +import fr.ifremer.coselmar.services.CoselmarServicesContext; import fr.ifremer.coselmar.services.CoselmarWebServiceSupport; import fr.ifremer.coselmar.services.errors.InvalidCredentialException; import fr.ifremer.coselmar.services.errors.NoResultException; @@ -58,21 +60,30 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.lucene.queryparser.classic.ParseException; -import org.debux.webmotion.server.call.UploadFile; -import org.debux.webmotion.server.render.Render; +import org.jboss.resteasy.plugins.providers.multipart.InputPart; +import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; import org.nuiton.csv.Import; import org.nuiton.csv.ImportRuntimeException; import org.nuiton.topia.persistence.TopiaNoResultException; import org.nuiton.util.DateUtil; import org.nuiton.util.pagination.PaginationResult; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +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 java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; @@ -81,6 +92,8 @@ import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -89,6 +102,9 @@ import static org.apache.commons.logging.LogFactory.getLog; /** * @author ymartel <martel@codelutin.com> */ +@Path("") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) public class DocumentsWebService extends CoselmarWebServiceSupport { private static final Log log = getLog(DocumentsWebService.class); @@ -96,7 +112,9 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { public static final List<String> DOCUMENT_CREATE_ALLOWED_USER_ROLES = Lists.newArrayList(CoselmarUserRole.ADMIN.name(), CoselmarUserRole.SUPERVISOR.name(), CoselmarUserRole.EXPERT.name()); - /** Kind of admin role **/ + /** + * Kind of admin role + **/ public static final List<String> DOCUMENT_SUPER_USER_ROLES = Lists.newArrayList(CoselmarUserRole.ADMIN.name(), CoselmarUserRole.SUPERVISOR.name()); @@ -108,23 +126,27 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { ); protected static final String DESCRIPTION_CSV_FILE_NAME = "description.csv"; - public DocumentBean getDocument(String documentId) throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/documents/{documentId}") + public DocumentBean getDocument(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("documentId") String documentId) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); // reconstitute full id - String fullId = getDocumentFullId(documentId); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String fullId = getDocumentFullId(persistenceContext, documentId); - Document document = getDocumentDao().forTopiaIdEquals(fullId).findUnique(); + Document document = persistenceContext.getDocumentDao().forTopiaIdEquals(fullId).findUnique(); // If document if public, only admin, supervisor and expert could view it // If document if private, only admin, supervisor and owner could view it if (!isAllowedToAccessDocument(currentUser, document)) { String message = String.format("User %s %s ('%s') try to access to document '%s'", - currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId()), documentId); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(persistenceContext, currentUser.getTopiaId()), documentId); if (log.isWarnEnabled()) { log.warn(message); } @@ -132,18 +154,18 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { } - DocumentBean documentBean = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), document); + DocumentBean documentBean = BeanEntityConverter.toBean(persistenceContext.getTopiaIdFactory(), document); // Manage related Question - List<Question> relatedQuestions = getQuestionDao().forRelatedDocumentsContains(document).findAll(); + List<Question> relatedQuestions = persistenceContext.getQuestionDao().forRelatedDocumentsContains(document).findAll(); for (Question relatedQuestion : relatedQuestions) { - QuestionBean questionBean = BeanEntityConverter.toLightBean(getPersistenceContext().getTopiaIdFactory(), relatedQuestion); + QuestionBean questionBean = BeanEntityConverter.toLightBean(persistenceContext.getTopiaIdFactory(), relatedQuestion); documentBean.addRelatedQuestion(questionBean); } // Manage related Question - List<Question> relatedQuestionsAsClosing = getQuestionDao().forClosingDocumentsContains(document).findAll(); + List<Question> relatedQuestionsAsClosing = persistenceContext.getQuestionDao().forClosingDocumentsContains(document).findAll(); for (Question relatedQuestion : relatedQuestionsAsClosing) { - QuestionBean questionBean = BeanEntityConverter.toLightBean(getPersistenceContext().getTopiaIdFactory(), relatedQuestion); + QuestionBean questionBean = BeanEntityConverter.toLightBean(persistenceContext.getTopiaIdFactory(), relatedQuestion); documentBean.addRelatedQuestion(questionBean); } @@ -154,7 +176,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { if (StringUtils.equalsIgnoreCase(fullId, usersGroup.getName())) { Set<CoselmarUser> members = usersGroup.getMembers(); for (CoselmarUser member : members) { - String userLightId = getPersistenceContext().getTopiaIdFactory().getRandomPart(member.getTopiaId()); + String userLightId = getShortIdFromFull(persistenceContext, member.getTopiaId()); UserBean userBean = BeanEntityConverter.toBean(userLightId, member); documentBean.addAuthorizedUser(userBean); } @@ -166,18 +188,25 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { return documentBean; } - public List<DocumentBean> getDocuments(DocumentSearchBean searchBean) throws InvalidCredentialException { + @GET + @Path("/v1/documents") + public List<DocumentBean> getDocuments(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @QueryParam("searchBean") DocumentSearchBean searchBean) throws InvalidCredentialException { - PaginationResult<DocumentBean> paginatedDocuments = getPaginatedDocuments(searchBean); + PaginationResult<DocumentBean> paginatedDocuments = getPaginatedDocuments(servicesContext, authorization, searchBean); return paginatedDocuments.getElements(); } - public PaginationResult<DocumentBean> getPaginatedDocuments(DocumentSearchBean searchBean) throws InvalidCredentialException { + @GET + @Path("/v2/documents") + public PaginationResult<DocumentBean> getPaginatedDocuments(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @QueryParam("searchBean") DocumentSearchBean searchBean) throws InvalidCredentialException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); CoselmarUserRole currentUserRole = currentUser.getRole(); @@ -221,17 +250,18 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { PaginationResult<Document> paginatedDocuments; // Admin and Supervisor can see all documents (public, private and restricted) + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); if (Lists.newArrayList(CoselmarUserRole.ADMIN, CoselmarUserRole.SUPERVISOR).contains(currentUserRole)) { List<String> searchKeywords = searchBean.getFullTextSearch(); if (searchKeywords != null && !searchKeywords.isEmpty()) { - DocumentsIndexationService documentsIndexationService = getServicesContext().newService(DocumentsIndexationService.class); + DocumentsIndexationService documentsIndexationService = servicesContext.newService(DocumentsIndexationService.class); try { List<String> documentIds = documentsIndexationService.searchDocuments(searchKeywords); - List<String> documentFullIds = getDocumentsFullId(documentIds); + List<String> documentFullIds = getDocumentsFullId(persistenceContext, documentIds); - Long count = getDocumentDao().forTopiaIdIn(documentFullIds).count(); - List<Document> documentList = getDocumentDao().forTopiaIdIn(documentFullIds).find(searchExample.getPaginationParameter()); + Long count = persistenceContext.getDocumentDao().forTopiaIdIn(documentFullIds).count(); + List<Document> documentList = persistenceContext.getDocumentDao().forTopiaIdIn(documentFullIds).find(searchExample.getPaginationParameter()); paginatedDocuments = PaginationResult.of(documentList, count, searchExample.getPaginationParameter()); @@ -239,27 +269,27 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { if (log.isErrorEnabled()) { log.error("Unable to search by lucene, make search directly in database", e); } - paginatedDocuments = getDocumentDao().findPaginatedContainingAllKeywords(searchKeywords, searchExample.getPaginationParameter()); + paginatedDocuments = persistenceContext.getDocumentDao().findPaginatedContainingAllKeywords(searchKeywords, searchExample.getPaginationParameter()); } } else { - paginatedDocuments = getDocumentDao().findAllByExample(null, searchExample); + paginatedDocuments = persistenceContext.getDocumentDao().findAllByExample(null, searchExample); } } else { //Other can only see public, his own private and restricted for which he is allowed - paginatedDocuments = getDocumentDao().findAllByExample(currentUser, searchExample); + paginatedDocuments = persistenceContext.getDocumentDao().findAllByExample(currentUser, searchExample); } List<DocumentBean> documentBeans = new ArrayList<>(paginatedDocuments.getElements().size()); for (Document document : paginatedDocuments.getElements()) { - DocumentBean documentBean = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), document); + DocumentBean documentBean = BeanEntityConverter.toBean(persistenceContext.getTopiaIdFactory(), document); // Manage related Question - long relatedQuestions = getQuestionDao().forRelatedDocumentsContains(document).count(); + long relatedQuestions = persistenceContext.getQuestionDao().forRelatedDocumentsContains(document).count(); // Don't forget use as closing documents - long relatedQuestionsAsClosing = getQuestionDao().forClosingDocumentsContains(document).count(); + long relatedQuestionsAsClosing = persistenceContext.getQuestionDao().forClosingDocumentsContains(document).count(); documentBean.setNbRelatedQuestions((int) (relatedQuestions + relatedQuestionsAsClosing)); @@ -270,11 +300,14 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { return result; } - public List<String> getSearchBibliography(DocumentSearchBean searchBean) throws InvalidCredentialException { + @GET + @Path("/v2/documents/citations") + public List<String> getSearchBibliography(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @QueryParam("searchBean") DocumentSearchBean searchBean) throws InvalidCredentialException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); CoselmarUserRole currentUserRole = currentUser.getRole(); @@ -315,31 +348,32 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { List<String> citations; // Admin and Supervisor can see all documents (public, private and restricted) + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); if (Lists.newArrayList(CoselmarUserRole.ADMIN, CoselmarUserRole.SUPERVISOR).contains(currentUserRole)) { List<String> searchKeywords = searchBean.getFullTextSearch(); if (searchKeywords != null && !searchKeywords.isEmpty()) { - DocumentsIndexationService documentsIndexationService = getServicesContext().newService(DocumentsIndexationService.class); + DocumentsIndexationService documentsIndexationService = servicesContext.newService(DocumentsIndexationService.class); try { List<String> documentIds = documentsIndexationService.searchDocuments(searchKeywords); - List<String> documentFullIds = getDocumentsFullId(documentIds); + List<String> documentFullIds = getDocumentsFullId(persistenceContext, documentIds); - citations = getDocumentDao().findCitationsByDocumentIds(documentFullIds); + citations = persistenceContext.getDocumentDao().findCitationsByDocumentIds(documentFullIds); } catch (IOException | ParseException e) { if (log.isErrorEnabled()) { log.error("Unable to search by lucene, make search directly in database", e); } - citations = getDocumentDao().findCitationsByDocumentFromKeywords(searchKeywords); + citations = persistenceContext.getDocumentDao().findCitationsByDocumentFromKeywords(searchKeywords); } } else { - citations = getDocumentDao().findCitationsByDocumentExample(null, searchExample); + citations = persistenceContext.getDocumentDao().findCitationsByDocumentExample(null, searchExample); } } else { //Other can only see public, his own private and restricted for which he is allowed - citations = getDocumentDao().findCitationsByDocumentExample(currentUser, searchExample); + citations = persistenceContext.getDocumentDao().findCitationsByDocumentExample(currentUser, searchExample); } Iterables.removeIf(citations, new Predicate<String>() { @@ -352,10 +386,15 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { return citations; } - public DocumentBean addDocument(DocumentBean document, UploadFile uploadFile) throws InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/documents") + @Consumes(MediaType.MULTIPART_FORM_DATA) + public DocumentBean addDocument(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + DocumentBean document, + MultipartFormDataInput uploadFile) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); UserWebToken userWebToken = checkAuthentication(authorization); // Only Expert or Supervisor can add document @@ -372,11 +411,12 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { Preconditions.checkNotNull(document); // retrieve user who will be assigned as document owner - String fullId = getFullUserIdFromShort(userWebToken.getUserId()); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String fullId = getFullUserIdFromShort(persistenceContext, userWebToken.getUserId()); CoselmarUser owner; try { - owner = getCoselmarUserDao().forTopiaIdEquals(fullId).findUnique(); + owner = persistenceContext.getCoselmarUserDao().forTopiaIdEquals(fullId).findUnique(); } catch (TopiaNoResultException tnre) { // Should not happened, cause user are not really deleted String message = String.format("Seems that logged user ('%s') does not exist anymore.", fullId); @@ -388,34 +428,40 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { FileInfos fileInfos = null; if (uploadFile != null) { - fileInfos = manageDocumentFile(uploadFile, owner); + fileInfos = manageDocumentFile(servicesContext, uploadFile, owner); } - DocumentBean result = createDocument(document, fileInfos, owner); + DocumentBean result = createDocument(servicesContext, document, fileInfos, owner); - commit(); + persistenceContext.commit(); return result; } - public void addDocumentFile(String documentId, UploadFile uploadFile) throws InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/documents/{documentId}/file") + @Consumes(MediaType.MULTIPART_FORM_DATA) + public void addDocumentFile(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("documentId") String documentId, + MultipartFormDataInput uploadFile) throws InvalidCredentialException, UnauthorizedException { Preconditions.checkNotNull(documentId); Preconditions.checkNotNull(uploadFile); // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); - String documentFullId = getFullIdFromShort(Document.class, documentId); - Document documentEntity = getDocumentDao().forTopiaIdEquals(documentFullId).findAny(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String documentFullId = getFullIdFromShort(persistenceContext, Document.class, documentId); + Document documentEntity = persistenceContext.getDocumentDao().forTopiaIdEquals(documentFullId).findAny(); // Only Owner Expert or Supervisor/Admin can add document file if (!DOCUMENT_SUPER_USER_ROLES.contains(currentUser.getRole().name()) && documentEntity.getOwner() != currentUser) { String message = String.format("User %s %s ('%s') is not allowed to add document file", - currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId())); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(persistenceContext, currentUser.getTopiaId())); if (log.isWarnEnabled()) { log.warn(message); } @@ -424,7 +470,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Get owner to place correctly the file CoselmarUser owner = documentEntity.getOwner(); - FileInfos fileInfos = manageDocumentFile(uploadFile, owner); + FileInfos fileInfos = manageDocumentFile(servicesContext, uploadFile, owner); String filePath = fileInfos.getFinalFilePath(); String contentType = fileInfos.getMimeType(); // Read file content @@ -439,13 +485,13 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { documentEntity.setWithFile(true); documentEntity.setMimeType(contentType); documentEntity.setFilePath(filePath); - documentEntity.setFileName(uploadFile.getName()); + documentEntity.setFileName(fileInfos.getFileName()); documentEntity.setFileContent(fileContent); // Should update document index information to put the file - DocumentBean documentBean = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), documentEntity); + DocumentBean documentBean = BeanEntityConverter.toBean(persistenceContext.getTopiaIdFactory(), documentEntity); - DocumentsIndexationService documentsIndexationService = getServicesContext().newService(DocumentsIndexationService.class); + DocumentsIndexationService documentsIndexationService = servicesContext.newService(DocumentsIndexationService.class); try { documentsIndexationService.updateDocument(documentBean, fileContent); if (log.isDebugEnabled()) { @@ -458,17 +504,23 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { } } - commit(); + persistenceContext.commit(); } - public Render getDocumentFile(String documentId) throws NoResultException { + @GET + @Path("/v1/documents/{documentId}/file") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + public Response getDocumentFile(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("documentId") String documentId) throws NoResultException { // reconstitute full id - String fullId =getDocumentFullId(documentId); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String fullId = getDocumentFullId(persistenceContext, documentId); - Document document = getDocumentDao().forTopiaIdEquals(fullId).findUnique(); + Document document = persistenceContext.getDocumentDao().forTopiaIdEquals(fullId).findUnique(); //TODO ymartel 20141103 : manage file owner ? // Get file attached to document @@ -480,36 +532,32 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { String fileName = StringUtils.isNotBlank(document.getFileName()) ? document.getFileName() : document.getName(); String fileMimeType = document.getMimeType(); - try { - InputStream fileStream = new FileInputStream(documentFile); - return renderDownload(fileStream, fileName, fileMimeType); - - } catch (FileNotFoundException e) { - if (log.isErrorEnabled()) { - String message = String.format("Unable to retrieve file %s", fileName); - log.error(message, e); - } - throw new NoResultException("File does not exist"); - } + Response.ResponseBuilder ok = Response.ok(documentFile, fileMimeType); + ok.header("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + return ok.build(); } - public void saveDocument(DocumentBean document) throws InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/documents/{documentId}") + public void saveDocument(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + DocumentBean document) throws InvalidCredentialException, UnauthorizedException { Preconditions.checkNotNull(document); Preconditions.checkNotNull(document.getId()); // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); - String documentId = getDocumentFullId(document.getId()); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String documentId = getDocumentFullId(persistenceContext, document.getId()); - Document documentEntity = getDocumentDao().forTopiaIdEquals(documentId).findUnique(); + Document documentEntity = persistenceContext.getDocumentDao().forTopiaIdEquals(documentId).findUnique(); if (!isAllowedToAccessDocument(currentUser, documentEntity)) { String message = String.format("User %s %s ('%s') try to modify document '%s'", - currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId()), documentId); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(persistenceContext, currentUser.getTopiaId()), documentId); if (log.isWarnEnabled()) { log.warn(message); } @@ -517,7 +565,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { } - boolean isUsedByQuestions = getQuestionDao().forRelatedDocumentsContains(documentEntity).exists(); + boolean isUsedByQuestions = persistenceContext.getQuestionDao().forRelatedDocumentsContains(documentEntity).exists(); if (isUsedByQuestions && currentUser.getRole() != CoselmarUserRole.ADMIN) { String message = "Document is used by some questions, cannot be modified."; if (log.isWarnEnabled()) { @@ -534,7 +582,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Manage privacy : if restricted, create an UserGroup with authorized users if (StringUtils.equals(Privacy.RESTRICTED.name(), newPrivacy)) { Set<UserBean> authorizedUsers = document.getAuthorizedUsers(); - Set<CoselmarUser> coselmarUsers = retrieveUsers(authorizedUsers); + Set<CoselmarUser> coselmarUsers = retrieveUsers(persistenceContext, authorizedUsers); Set<CoselmarUserGroup> restrictedList = documentEntity.getRestrictedList(); CoselmarUserGroup restrictedUsers = null; @@ -550,7 +598,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { } } if (restrictedUsers == null) { - restrictedUsers = getCoselmarUserGroupDao().create(); + restrictedUsers = persistenceContext.getCoselmarUserGroupDao().create(); restrictedUsers.setName(documentEntity.getTopiaId()); } @@ -570,7 +618,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Remove group from document, and delete it ! if (restrictedUsers != null) { documentEntity.removeRestrictedList(restrictedUsers); - getCoselmarUserGroupDao().delete(restrictedUsers); + persistenceContext.getCoselmarUserGroupDao().delete(restrictedUsers); } } @@ -604,14 +652,14 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { documentEntity.setCitation(document.getCitation()); - commit(); + persistenceContext.commit(); // Update index information for this document - DocumentBean result = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), documentEntity); + DocumentBean result = BeanEntityConverter.toBean(persistenceContext.getTopiaIdFactory(), documentEntity); - DocumentsIndexationService documentsIndexationService = getServicesContext().newService(DocumentsIndexationService.class); + DocumentsIndexationService documentsIndexationService = servicesContext.newService(DocumentsIndexationService.class); try { - documentsIndexationService.indexDocument(result, null); // no document file for the moment here + documentsIndexationService.indexDocument(result, null); // no document file for the moment here if (log.isDebugEnabled()) { String message = String.format("Document '%s' was updated in index", document.getName()); log.debug(message); @@ -624,22 +672,26 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { } - public void deleteDocument(String documentId) throws InvalidCredentialException, UnauthorizedException { + @DELETE + @Path("/v1/documents/{documentId}") + public void deleteDocument(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("documentId") String documentId) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); // reconstitute full id - String fullId = getDocumentFullId(documentId); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String fullId = getDocumentFullId(persistenceContext, documentId); - Document document = getDocumentDao().forTopiaIdEquals(fullId).findUnique(); + Document document = persistenceContext.getDocumentDao().forTopiaIdEquals(fullId).findUnique(); if (!DOCUMENT_SUPER_USER_ROLES.contains(currentUser.getRole().name()) && document.getOwner() != currentUser) { String message = String.format("User %s %s ('%s') try to delete document '%s'", - currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId()), documentId); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(persistenceContext, currentUser.getTopiaId()), documentId); if (log.isWarnEnabled()) { log.warn(message); } @@ -650,12 +702,12 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Delete physical file deleteAttachedFile(document); - getDocumentDao().delete(document); + persistenceContext.getDocumentDao().delete(document); - commit(); + persistenceContext.commit(); //Remove index entry - DocumentsIndexationService documentsIndexationService = getServicesContext().newService(DocumentsIndexationService.class); + DocumentsIndexationService documentsIndexationService = servicesContext.newService(DocumentsIndexationService.class); try { documentsIndexationService.deleteDocument(documentId); if (log.isDebugEnabled()) { @@ -669,38 +721,49 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { } } - public List<String> getKeywords() throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/documents/keywords") + public List<String> getKeywords(@Context CoselmarServicesContext servicesContext) throws InvalidCredentialException, UnauthorizedException { - List<String> themes = getDocumentDao().findAllKeywords(); + List<String> themes = servicesContext.getPersistenceContext().getDocumentDao().findAllKeywords(); return themes; } - public List<String> getTypes() throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/documents/types") + public List<String> getTypes(@Context CoselmarServicesContext servicesContext) throws InvalidCredentialException, UnauthorizedException { - List<String> types = getDocumentDao().findAllTypes(); + List<String> types = servicesContext.getPersistenceContext().getDocumentDao().findAllTypes(); return types; } - public MassiveDocumentsImportResult uploadZipDocuments(UploadFile uploadFile) throws InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/admin/documents/zip") + @Consumes(MediaType.MULTIPART_FORM_DATA) + public MassiveDocumentsImportResult uploadZipDocuments(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + MultipartFormDataInput uploadFile) throws InvalidCredentialException, UnauthorizedException { Preconditions.checkNotNull(uploadFile); // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); // Only Supervisor/Admin can add Zip documents if (!DOCUMENT_SUPER_USER_ROLES.contains(currentUser.getRole().name())) { String message = String.format("User %s %s ('%s') is not allowed to upload mass document files", - currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId())); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(servicesContext.getPersistenceContext(), currentUser.getTopiaId())); if (log.isWarnEnabled()) { log.warn(message); } throw new UnauthorizedException(message); } - return importFromZip(uploadFile.getFile(), currentUser); + + File file = getFileFromMultipart(uploadFile, "uploadFile", "importFile_" + servicesContext.getNow().getTime() + ".zip"); + + return importFromZip(servicesContext, file, currentUser); } @@ -714,10 +777,10 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { * * @return a DocumentBean with all created {@link Document} data. */ - protected DocumentBean createDocument(DocumentBean documentBean, FileInfos fileInfos, CoselmarUser owner) { + protected DocumentBean createDocument(CoselmarServicesContext servicesContext, DocumentBean documentBean, FileInfos fileInfos, CoselmarUser owner) { // Document Metadata - Document documentEntity = createDocumentMetadataFromBean(documentBean, owner); + Document documentEntity = createDocumentMetadataFromBean(servicesContext, documentBean, owner); // If document has a file, manage it ! String fileContent = ""; @@ -739,10 +802,10 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { documentEntity.setWithFile(false); } - DocumentBean result = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), documentEntity); + DocumentBean result = BeanEntityConverter.toBean(servicesContext.getPersistenceContext().getTopiaIdFactory(), documentEntity); // Indexation job - DocumentsIndexationService documentsIndexationService = getServicesContext().newService(DocumentsIndexationService.class); + DocumentsIndexationService documentsIndexationService = servicesContext.newService(DocumentsIndexationService.class); try { documentsIndexationService.indexDocument(result, fileContent); if (log.isDebugEnabled()) { @@ -762,11 +825,12 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { * This also manage owner and restriction but <strong>does not manage</strong> attached file part. * No commit are done here. */ - public Document createDocumentMetadataFromBean(DocumentBean documentBean, CoselmarUser owner) { + public Document createDocumentMetadataFromBean(CoselmarServicesContext servicesContext, DocumentBean documentBean, CoselmarUser owner) { Preconditions.checkNotNull(documentBean); // Document Metadata - Document document = getDocumentDao().create(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + Document document = persistenceContext.getDocumentDao().create(); document.setOwner(owner); @@ -778,8 +842,8 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Manage privacy : if restricted, create an UserGroup with authorized users if (StringUtils.equals(Privacy.RESTRICTED.name(), privacy)) { Set<UserBean> authorizedUsers = documentBean.getAuthorizedUsers(); - Set<CoselmarUser> coselmarUsers = retrieveUsers(authorizedUsers); - CoselmarUserGroup restrictedUsers = getCoselmarUserGroupDao().create(); + Set<CoselmarUser> coselmarUsers = retrieveUsers(persistenceContext, authorizedUsers); + CoselmarUserGroup restrictedUsers = persistenceContext.getCoselmarUserGroupDao().create(); restrictedUsers.setName(document.getTopiaId()); restrictedUsers.addAllMembers(coselmarUsers); @@ -823,55 +887,64 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { * * @return the upload file Metadata */ - protected FileInfos manageDocumentFile(UploadFile uploadFile, CoselmarUser owner) { + protected FileInfos manageDocumentFile(CoselmarServicesContext servicesContext, MultipartFormDataInput uploadFile, CoselmarUser owner) { Preconditions.checkNotNull(uploadFile); - // Document File - String fileName = uploadFile.getName(); - File uploadedFile = uploadFile.getFile(); - String contentType = uploadFile.getContentType(); + try { + // Document File + InputStream in = uploadFile.getFormDataPart("uploadFile", InputStream.class, null); + InputPart inputPart = uploadFile.getFormDataMap().get("uploadFile").get(0); + + String contentDisposition = inputPart.getHeaders().get("Content-Disposition").get(0); + Pattern FilenamePattern = Pattern.compile("filename=\"(.*)\""); + Matcher matcher = FilenamePattern.matcher(contentDisposition); + String fileName = "no-name-" + servicesContext.getNow().getTime() + "-" + owner.getName() + "_" + owner.getFirstname(); + if (matcher.find()) { + fileName = matcher.group(1); + } - if (log.isDebugEnabled()) { - String message = String.format("File name : %s, content-type : %s", fileName, contentType); - log.debug(message); - } + String contentType = inputPart.getHeaders().get("Content-Type").get(0); + + if (log.isDebugEnabled()) { + String message = String.format("File name : %s, content-type : %s", fileName, contentType); + log.debug(message); + } - // put the document in the good directory - String filePath = getDocumentFileDestPath(owner, fileName); + // put the document in the good directory + String filePath = getDocumentFileDestPath(servicesContext, owner, fileName); + File uploadedFile = new File(filePath); + Files.copy(in, uploadedFile.toPath()); + + FileInfos fileInfos = new FileInfos(); + fileInfos.setFileName(fileName); + fileInfos.setMimeType(contentType); + fileInfos.setFinalFilePath(uploadedFile.getAbsolutePath()); + return fileInfos; - File destFile = new File(filePath); - try { - FileUtils.moveFile(uploadedFile, destFile); } catch (IOException e) { if (log.isErrorEnabled()) { log.error("error during File transfer", e); } throw new CoselmarTechnicalException("Internal error during file transfer", e); } - - FileInfos fileInfos = new FileInfos(); - fileInfos.setFileName(fileName); - fileInfos.setMimeType(contentType); - fileInfos.setFinalFilePath(destFile.getAbsolutePath()); - return fileInfos; } - protected String getDocumentFileDestPath(CoselmarUser owner, String fileName) { - String userPath = getUserDocumentPath(owner); + protected String getDocumentFileDestPath(CoselmarServicesContext servicesContext, CoselmarUser owner, String fileName) { + String userPath = getUserDocumentPath(servicesContext, owner); - String storageFileName = getFileStorageName(fileName); + String storageFileName = getFileStorageName(servicesContext, fileName); return userPath + File.separator + storageFileName; } - protected String getFileStorageName(String fileName) { - Date now = getNow(); + protected String getFileStorageName(CoselmarServicesContext servicesContext, String fileName) { + Date now = servicesContext.getNow(); String formattedDay = DateUtil.formatDate(now, "yyyyMMddHHmm"); String prefix = formattedDay + "-"; return prefix + fileName; } - protected String getUserDocumentPath(CoselmarUser user) { - File dataDirectory = getCoselmarServicesConfig().getDataDirectory(); + protected String getUserDocumentPath(CoselmarServicesContext servicesContext, CoselmarUser user) { + File dataDirectory = servicesContext.getCoselmarServicesConfig().getDataDirectory(); String absolutePath = dataDirectory.getAbsolutePath(); String userFolder = StringUtils.replaceChars(user.getFirstname() + "-" + user.getName(), " ", "_"); String userPath = absolutePath + File.separator + userFolder; @@ -882,10 +955,9 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { * Check if an user can access to a document metadata, according to its * privacy settings and the user grant. * - * @param user : Current User, - * containing {@link fr.ifremer.coselmar.persistence.entity.CoselmarUserRole} as String - * @param document : the document user trying to access. - * + * @param user : Current User, + * containing {@link fr.ifremer.coselmar.persistence.entity.CoselmarUserRole} as String + * @param document : the document user trying to access. * @return true is user can access, false else. */ protected boolean isAllowedToAccessDocument(CoselmarUser user, Document document) { @@ -901,7 +973,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { if (document.getPrivacy() == Privacy.PUBLIC) { isAuthorized = DOCUMENT_VIEW_ALLOWED_USER_ROLES.contains(viewerRole); - // For Private : only admin/supervisor/owner can access + // For Private : only admin/supervisor/owner can access } else if (document.getPrivacy() == Privacy.PRIVATE) { CoselmarUser documentOwner = document.getOwner(); boolean isOwner = StringUtils.equals(documentOwner.getTopiaId(), user.getTopiaId()); @@ -920,16 +992,16 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { return isAuthorized; } - protected String getDocumentFullId(String documentId) { - return Document.class.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + documentId; + protected String getDocumentFullId(CoselmarPersistenceContext persistenceContext, String documentId) { + return getFullIdFromShort(persistenceContext, Document.class, documentId); } - protected List<String> getDocumentsFullId(List<String> documentShortIds) { + protected List<String> getDocumentsFullId(CoselmarPersistenceContext persistenceContext, List<String> documentShortIds) { Function<String, String> getFullIds = new Function<String, String>() { @Override public String apply(String shortId) { - return getDocumentFullId(shortId); + return getDocumentFullId(persistenceContext, shortId); } }; @@ -952,21 +1024,21 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { } } - protected Set<CoselmarUser> retrieveUsers(Collection<UserBean> userBeans) { + protected Set<CoselmarUser> retrieveUsers(CoselmarPersistenceContext persistenceContext, Collection<UserBean> userBeans) { Function<UserBean, String> getIds = new Function<UserBean, String>() { @Override public String apply(UserBean userBean) { - return getFullIdFromShort(CoselmarUser.class, userBean.getId()); + return getFullIdFromShort(persistenceContext, CoselmarUser.class, userBean.getId()); } }; Collection<String> userIds = Collections2.transform(userBeans, getIds); - List<CoselmarUser> coselmarUsers = getCoselmarUserDao().forTopiaIdIn(userIds).findAll(); + List<CoselmarUser> coselmarUsers = persistenceContext.getCoselmarUserDao().forTopiaIdIn(userIds).findAll(); return new HashSet<>(coselmarUsers); } - protected MassiveDocumentsImportResult importFromZip(File file, CoselmarUser currentUser) { + protected MassiveDocumentsImportResult importFromZip(CoselmarServicesContext servicesContext, File file, CoselmarUser currentUser) { MassiveDocumentsImportResult importResult = new MassiveDocumentsImportResult(); // File should be a Zip ZipFile zipFile; @@ -1004,10 +1076,10 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { DocumentImportModel csvModel = new DocumentImportModel(); Import<DocumentBean> importer = Import.newImport(csvModel, descriptionInputStream); - File dataDirectory = getCoselmarServicesConfig().getDataDirectory(); + File dataDirectory = servicesContext.getCoselmarServicesConfig().getDataDirectory(); String dataPath = dataDirectory.getAbsolutePath(); - String zipTempPath = dataPath + File.separator + DateUtil.formatDate(getNow(), "yyyyMMddHHmm"); - Path dir = Paths.get(zipTempPath); + String zipTempPath = dataPath + File.separator + DateUtil.formatDate(servicesContext.getNow(), "yyyyMMddHHmm"); + java.nio.file.Path dir = Paths.get(zipTempPath); try { Files.createDirectories(dir); } catch (IOException e) { @@ -1044,9 +1116,9 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { try { InputStream zipFileInputStream = zipFile.getInputStream(zipFileEntry); String fileMimeType = TikaUtils.getFileMimeType(fileName); - String futureFilePath = getDocumentFileDestPath(currentUser, fileName); + String futureFilePath = getDocumentFileDestPath(servicesContext, currentUser, fileName); - String storageFileName = getFileStorageName(fileName); + String storageFileName = getFileStorageName(servicesContext, fileName); // Push file stream in temporary folder File zipEntryFile = new File(zipTempPath + File.separator + storageFileName); @@ -1080,7 +1152,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { // Create document if (documentOk) { - createDocument(documentBean, fileInfos, currentUser); + createDocument(servicesContext, documentBean, fileInfos, currentUser); } } @@ -1100,23 +1172,23 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { try { // Ok, let move all files from temp storage to real one ! File tmpDir = new File(zipTempPath); - FileUtils.copyDirectory(tmpDir, new File(getUserDocumentPath(currentUser))); + FileUtils.copyDirectory(tmpDir, new File(getUserDocumentPath(servicesContext, currentUser))); FileUtils.deleteDirectory(tmpDir); - commit(); + servicesContext.getPersistenceContext().commit(); } catch (IOException e) { // Big problem if we can't move files into real folder ! String message = String.format("Unable to move files from temp folder '%s' to final folder", zipTempPath); if (log.isErrorEnabled()) { log.error(message, e); } - refreshDocumentsIndex(); + refreshDocumentsIndex(servicesContext); throw new CoselmarTechnicalException(message, e); } } else { // Something wrong happened... rollback, and refresh lucene to avoid new data - rollback(); - refreshDocumentsIndex(); + servicesContext.getPersistenceContext().rollback(); + refreshDocumentsIndex(servicesContext); } } finally { @@ -1129,14 +1201,37 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { return importResult; } - protected void refreshDocumentsIndex() { - AdminWebService adminWebService = getServicesContext().newService(AdminWebService.class); + protected void refreshDocumentsIndex(CoselmarServicesContext servicesContext) { + AdminWebService adminWebService = servicesContext.newService(AdminWebService.class); try { - adminWebService.refreshDocumentsIndex(); + adminWebService.refreshDocumentsIndex(servicesContext); } catch (IOException e) { if (log.isErrorEnabled()) { log.error("Unable to refresh Lucene Documents index. Data should be corrupted", e); } } } + + protected File getFileFromMultipart(MultipartFormDataInput uploadInput, String uploadName, String fileName) { + + File file = null; + try { + // Get file from Multipart + InputStream in = uploadInput.getFormDataPart(uploadName, InputStream.class, null); + InputPart inputPart = uploadInput.getFormDataMap().get(uploadName).get(0); + + java.nio.file.Path tempPath = Files.createTempDirectory("coselmar"); + file = new File(tempPath.toFile(), fileName); + Files.copy(in, file.toPath()); + + } catch ( + IOException e) { + if (log.isErrorEnabled()) { + log.error("error during ZipFile transfer", e); + } + + } + return file; + } + } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/ErrorAction.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/ErrorAction.java deleted file mode 100644 index 45d1c49..0000000 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/ErrorAction.java +++ /dev/null @@ -1,83 +0,0 @@ -package fr.ifremer.coselmar.services.v1; - -/* - * #%L - * Coselmar :: Rest Services - * $Id:$ - * $HeadURL:$ - * %% - * Copyright (C) 2014 Ifremer, Code Lutin - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -import fr.ifremer.coselmar.services.CoselmarRestUtil; -import org.debux.webmotion.server.WebMotionController; -import org.debux.webmotion.server.call.HttpContext; -import org.debux.webmotion.server.render.Render; - -import javax.servlet.http.HttpServletResponse; - -/** - * @author ymartel <martel@codelutin.com> - */ -public class ErrorAction extends WebMotionController { - - public Render on500(HttpContext context, Exception e) { - - CoselmarRestUtil.prepareResponse(context); - - Render render = renderError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - return render; - - } - - public Render on404(HttpContext context, Exception e) { - - CoselmarRestUtil.prepareResponse(context); - - Render render = renderError(HttpServletResponse.SC_NOT_FOUND, e.getMessage()); - return render; - - } - - public Render on403(HttpContext context, Exception e) { - - CoselmarRestUtil.prepareResponse(context); - - Render render = renderError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); - return render; - - } - - public Render on401(HttpContext context, Exception e) { - - CoselmarRestUtil.prepareResponse(context); - - Render render = renderError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); - return render; - - } - - public Render on409(HttpContext context, Exception e) { - - CoselmarRestUtil.prepareResponse(context); - - Render render = renderError(HttpServletResponse.SC_CONFLICT, e.getMessage()); - return render; - - } -} diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/GeneralWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/GeneralWebService.java index cb9bb07..4debc07 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/GeneralWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/GeneralWebService.java @@ -28,12 +28,20 @@ import com.google.common.collect.Lists; import fr.ifremer.coselmar.beans.CloudWord; import fr.ifremer.coselmar.exceptions.CoselmarTechnicalException; import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; +import fr.ifremer.coselmar.services.CoselmarServicesContext; import fr.ifremer.coselmar.services.CoselmarWebServiceSupport; import fr.ifremer.coselmar.services.indexation.TransverseIndexationService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.queryparser.classic.ParseException; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -42,15 +50,19 @@ import java.util.Map; /** * @author ymartel <martel@codelutin.com> */ +@Path("") +@Produces(MediaType.APPLICATION_JSON) public class GeneralWebService extends CoselmarWebServiceSupport { private static final Log log = LogFactory.getLog(GeneralWebService.class); protected static final List<String> RESTRICTED_ACCESS_USERS = Lists.newArrayList(CoselmarUserRole.CLIENT.name(), CoselmarUserRole.MEMBER.name()); - public List<CloudWord> getTopWords() { + @GET + @Path("/v1/general/topwords") + public List<CloudWord> getTopWords(@Context CoselmarServicesContext servicesContext) { - TransverseIndexationService questionsIndexationService = getServicesContext().newService(TransverseIndexationService.class); + TransverseIndexationService questionsIndexationService = servicesContext.newService(TransverseIndexationService.class); Map<String, Long> topTerms = null; try { diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/HealthService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/HealthService.java index b3faad2..67422b2 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/HealthService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/HealthService.java @@ -27,25 +27,37 @@ package fr.ifremer.coselmar.services.v1; import fr.ifremer.coselmar.beans.HealthBean; import fr.ifremer.coselmar.beans.QuestionSearchBean; import fr.ifremer.coselmar.persistence.entity.Privacy; +import fr.ifremer.coselmar.services.CoselmarServicesContext; import fr.ifremer.coselmar.services.CoselmarWebServiceSupport; import fr.ifremer.coselmar.services.indexation.QuestionsIndexationService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + /** * @author ymartel (martel@codelutin.com) */ +@Path("") +@Produces(MediaType.APPLICATION_JSON) public class HealthService extends CoselmarWebServiceSupport { private static final Log log = LogFactory.getLog(HealthService.class); - public HealthBean getHealth() { - String version = getCoselmarServicesConfig().getVersion(); - boolean devMode = getCoselmarServicesConfig().isDevMode(); + @GET + @Path("/v1/health") + public HealthBean getHealth(@Context CoselmarServicesContext servicesContext) { + String version = servicesContext.getCoselmarServicesConfig().getVersion(); + boolean devMode = servicesContext.getCoselmarServicesConfig().isDevMode(); boolean dbUp = false; try { - getQuestionDao().count(); + servicesContext.getPersistenceContext().getQuestionDao().count(); dbUp = true; } catch (Exception e) { @@ -55,7 +67,7 @@ public class HealthService extends CoselmarWebServiceSupport { } boolean indexationUp = false; - QuestionsIndexationService questionsIndexationService = getServicesContext().newService(QuestionsIndexationService.class); + QuestionsIndexationService questionsIndexationService = servicesContext.newService(QuestionsIndexationService.class); try { QuestionSearchBean searchBean = new QuestionSearchBean(); searchBean.setPrivacy(Privacy.PUBLIC.name()); diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java index 8597f5a..60b5689 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java @@ -44,6 +44,7 @@ import fr.ifremer.coselmar.beans.UserBean; import fr.ifremer.coselmar.beans.UserWebToken; import fr.ifremer.coselmar.converter.BeanEntityConverter; import fr.ifremer.coselmar.exceptions.CoselmarTechnicalException; +import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; import fr.ifremer.coselmar.persistence.entity.CoselmarUserGroup; import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; @@ -54,6 +55,7 @@ import fr.ifremer.coselmar.persistence.entity.Privacy; import fr.ifremer.coselmar.persistence.entity.Question; import fr.ifremer.coselmar.persistence.entity.QuestionImpl; import fr.ifremer.coselmar.persistence.entity.Status; +import fr.ifremer.coselmar.services.CoselmarServicesContext; import fr.ifremer.coselmar.services.CoselmarWebServiceSupport; import fr.ifremer.coselmar.services.errors.InvalidCredentialException; import fr.ifremer.coselmar.services.errors.NoResultException; @@ -65,7 +67,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.queryparser.classic.ParseException; -import org.debux.webmotion.server.render.Render; import org.nuiton.csv.Export; import org.nuiton.topia.persistence.TopiaIdFactory; import org.nuiton.topia.persistence.TopiaNoResultException; @@ -73,6 +74,19 @@ import org.nuiton.util.DateUtil; import org.nuiton.util.pagination.PaginationParameter; import org.nuiton.util.pagination.PaginationResult; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +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 java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -86,16 +100,23 @@ import java.util.Set; /** * @author ymartel <martel@codelutin.com> */ +@Path("") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) public class QuestionsWebService extends CoselmarWebServiceSupport { private static final Log log = LogFactory.getLog(QuestionsWebService.class); protected static final List<String> RESTRICTED_ACCESS_USERS = Lists.newArrayList(CoselmarUserRole.CLIENT.name(), CoselmarUserRole.MEMBER.name()); - public void addQuestion(QuestionBean question) throws InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/questions") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public void addQuestion(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @FormParam("question") QuestionBean question) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); UserWebToken userWebToken = checkAuthentication(authorization); // Only Supervisor can add question @@ -111,12 +132,13 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); // retrieve user who will be assigned as question supervisor - String fullId = getFullUserIdFromShort(userWebToken.getUserId()); + String fullId = getFullUserIdFromShort(persistenceContext, userWebToken.getUserId()); CoselmarUser supervisor; try { - supervisor = getCoselmarUserDao().forTopiaIdEquals(fullId).findUnique(); + supervisor = persistenceContext.getCoselmarUserDao().forTopiaIdEquals(fullId).findUnique(); } catch (TopiaNoResultException tnre) { // Should not happened, cause user are not really deleted String message = String.format("Logged user ('%s') does not exist.", fullId); @@ -133,7 +155,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Preconditions.checkNotNull(question.getThemes()); // let's go - Question questionEntity = getQuestionDao().create(); + Question questionEntity = persistenceContext.getQuestionDao().create(); questionEntity.setUnavailable(false); @@ -182,18 +204,18 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Retrieve the clients Set<UserBean> clients = question.getClients(); if (clients != null && !clients.isEmpty()) { - Set<CoselmarUser> clientEntities = retrieveUsers(clients); + Set<CoselmarUser> clientEntities = retrieveUsers(servicesContext, clients); questionEntity.addAllClients(clientEntities); } // For participants, should create a dedicated group, // that could be used to restrict documents access Set<UserBean> participants = question.getParticipants(); - CoselmarUserGroup participantGroup = getCoselmarUserGroupDao().create(); + CoselmarUserGroup participantGroup = persistenceContext.getCoselmarUserGroupDao().create(); if (participants != null && !participants.isEmpty()) { participantGroup.setName(question.getTitle()); - Set<CoselmarUser> expertEntities = retrieveUsers(participants); + Set<CoselmarUser> expertEntities = retrieveUsers(servicesContext, participants); participantGroup.addAllMembers(expertEntities); } @@ -202,7 +224,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Retrieve the supervisor Set<UserBean> supervisors = question.getSupervisors(); if (supervisors != null && !supervisors.isEmpty()) { - Set<CoselmarUser> supervisorEntities = retrieveUsers(supervisors); + Set<CoselmarUser> supervisorEntities = retrieveUsers(servicesContext, supervisors); questionEntity.addAllSupervisors(supervisorEntities); } @@ -213,7 +235,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Hierarchy of questions Set<QuestionBean> parents = question.getParents(); if (parents != null && !parents.isEmpty()) { - Set<Question> questions = retrieveQuestions(parents); + Set<Question> questions = retrieveQuestions(servicesContext, parents); questionEntity.addAllParents(questions); } @@ -221,7 +243,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Documents on init Set<DocumentBean> relatedDocuments = question.getRelatedDocuments(); if (relatedDocuments != null && !relatedDocuments.isEmpty()) { - Set<Document> documents = retrieveDocuments(relatedDocuments); + Set<Document> documents = retrieveDocuments(servicesContext, relatedDocuments); // Manage restriction list for document with Privacy.RESTRICTED for (Document document : documents) { if (document.getPrivacy() == Privacy.RESTRICTED) { @@ -239,7 +261,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Link linkEntity = null; String linkId = link.getId(); if (StringUtils.isNotBlank(linkId)) { - linkEntity = getPersistenceContext().getLinkDao().forTopiaIdEquals(getFullIdFromShort(Link.class, linkId)).findUniqueOrNull(); + linkEntity = persistenceContext.getLinkDao().forTopiaIdEquals(getFullIdFromShort(persistenceContext, Link.class, linkId)).findUniqueOrNull(); } if (linkEntity == null) { linkEntity = new LinkImpl(); @@ -248,20 +270,20 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { linkEntity.setUrl(link.getUrl()); if (StringUtils.isNotBlank(linkEntity.getTopiaId())) { - getPersistenceContext().getLinkDao().update(linkEntity); + persistenceContext.getLinkDao().update(linkEntity); } else { - getPersistenceContext().getLinkDao().create(linkEntity); + persistenceContext.getLinkDao().create(linkEntity); } questionEntity.addLinks(linkEntity); } } - commit(); + persistenceContext.commit(); - QuestionBean result = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), questionEntity); + QuestionBean result = BeanEntityConverter.toBean(persistenceContext.getTopiaIdFactory(), questionEntity); - QuestionsIndexationService questionsIndexationService = getServicesContext().newService(QuestionsIndexationService.class); + QuestionsIndexationService questionsIndexationService = servicesContext.newService(QuestionsIndexationService.class); try { questionsIndexationService.indexQuestion(result); if (log.isDebugEnabled()) { @@ -275,48 +297,57 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } } - public List<QuestionBean> getQuestions(QuestionSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/questions") + public List<QuestionBean> getQuestions(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @QueryParam("searchOption") QuestionSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); List<Question> questionList; if (searchOption != null) { - questionList = getAllFilteredQuestions(currentUser, searchOption); + questionList = getAllFilteredQuestions(servicesContext, currentUser, searchOption); } else { - questionList = getAllQuestions(currentUser); + questionList = getAllQuestions(servicesContext, currentUser); } - List<QuestionBean> result = convert(currentUser, questionList); + List<QuestionBean> result = convert(servicesContext, currentUser, questionList); return result; } - public PaginationResult<QuestionBean> getPaginatedQuestions(QuestionSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { + + @GET + @Path("/v2/questions") + public PaginationResult<QuestionBean> getPaginatedQuestions(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @QueryParam("searchOption") QuestionSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); PaginationResult<Question> paginatedQuestions; if (searchOption != null) { - paginatedQuestions = getPaginatedFilteredQuestions(currentUser, searchOption); + paginatedQuestions = getPaginatedFilteredQuestions(servicesContext, currentUser, searchOption); } else { - List<Question> allQuestions = getAllQuestions(currentUser); + List<Question> allQuestions = getAllQuestions(servicesContext, currentUser); paginatedQuestions = PaginationResult.of(allQuestions, allQuestions.size(), PaginationParameter.of(0, -1)); } - PaginationResult<QuestionBean> result = convert(currentUser, paginatedQuestions); + PaginationResult<QuestionBean> result = convert(servicesContext, currentUser, paginatedQuestions); return result; } - public List<QuestionBean> getPublicQuestions() throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/questions/public") + public List<QuestionBean> getPublicQuestions(@Context CoselmarServicesContext servicesContext) throws InvalidCredentialException, UnauthorizedException { // No authentication needed, just filter to get last 5 public questions QuestionSearchExample searchOption = QuestionSearchExample.newDefaultSearchExample(); @@ -329,12 +360,13 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { PaginationParameter paginationParameter = PaginationParameter.of(0, 5, Question.PROPERTY_SUBMISSION_DATE, true); - PaginationResult<Question> questionList = getQuestionDao().findWithSearchExample(searchOption, paginationParameter); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + PaginationResult<Question> questionList = persistenceContext.getQuestionDao().findWithSearchExample(searchOption, paginationParameter); List<QuestionBean> result = new ArrayList<>(questionList.getElements().size()); for (Question question : questionList.getElements()) { - TopiaIdFactory topiaIdFactory = getPersistenceContext().getTopiaIdFactory(); + TopiaIdFactory topiaIdFactory = persistenceContext.getTopiaIdFactory(); QuestionBean questionBean = BeanEntityConverter.toLightBean(topiaIdFactory, question); @@ -344,10 +376,13 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return result; } - public void deleteQuestion(String questionId) throws InvalidCredentialException, UnauthorizedException { + @DELETE + @Path("/v1/questions/{questionId}") + public void deleteQuestion(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("questionId") String questionId) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); UserWebToken userWebToken = checkAuthentication(authorization); // Only Supervisor can delete question @@ -364,10 +399,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } - String fullUserId = getFullIdFromShort(CoselmarUser.class, userWebToken.getUserId()); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String fullUserId = getFullIdFromShort(persistenceContext, CoselmarUser.class, userWebToken.getUserId()); try { - getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); + persistenceContext.getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); } catch (TopiaNoResultException tnre) { // Should not happened, cause user are not really deleted String message = String.format("Logged user ('%s') does not exist.", fullUserId); @@ -378,18 +414,18 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } // Retrieve Question - String fullQuestionId = getFullIdFromShort(Question.class, questionId); - Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + String fullQuestionId = getFullIdFromShort(persistenceContext, Question.class, questionId); + Question question = persistenceContext.getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); // Participant group should be deleted, and so, we should remove it from Document using this group CoselmarUserGroup participantGroup = question.getParticipants(); if (participantGroup != null) { - List<Document> documents = getDocumentDao().forRestrictedListContains(participantGroup).findAll(); + List<Document> documents = persistenceContext.getDocumentDao().forRestrictedListContains(participantGroup).findAll(); for (Document document : documents) { document.removeRestrictedList(participantGroup); } question.addAllContributors(participantGroup.getMembers()); - getPersistenceContext().getCoselmarUserGroupDao().delete(participantGroup); + persistenceContext.getCoselmarUserGroupDao().delete(participantGroup); } // Question become unavailable @@ -397,10 +433,10 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { question.setStatus(Status.DELETED); - commit(); + persistenceContext.commit(); // Remove it from index too - QuestionsIndexationService questionsIndexationService = getServicesContext().newService(QuestionsIndexationService.class); + QuestionsIndexationService questionsIndexationService = servicesContext.newService(QuestionsIndexationService.class); try { questionsIndexationService.deleteQuestion(questionId); if (log.isDebugEnabled()) { @@ -414,20 +450,24 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } } - public QuestionBean getQuestion(String questionId) throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/questions/{questionId}") + public QuestionBean getQuestion(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("questionId") String questionId) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); // Supervisor can get all the question elements // Expert can get all the question elements if public or if he is participant of the question // Client can get the question (not the documents) if he is client of the question. + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); // Member cannot access to question if (CoselmarUserRole.MEMBER == currentUser.getRole()) { String message = String.format("User %s %s ('%s') is not allowed to view question", - currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId())); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(persistenceContext, currentUser.getTopiaId())); if (log.isWarnEnabled()) { log.warn(message); } @@ -437,8 +477,8 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Retrieve Question - String fullQuestionId = getFullIdFromShort(Question.class, questionId); - Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + String fullQuestionId = getFullIdFromShort(persistenceContext, Question.class, questionId); + Question question = persistenceContext.getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); if (CoselmarUserRole.CLIENT == currentUser.getRole()) { // Client User can access if it is client of question @@ -446,11 +486,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } - QuestionBean result = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), question); + QuestionBean result = BeanEntityConverter.toBean(persistenceContext.getTopiaIdFactory(), question); //manager child - List<Question> children = getQuestionDao().forParentsContains(question).findAll(); + List<Question> children = persistenceContext.getQuestionDao().forParentsContains(question).findAll(); for (Question child : children) { - QuestionBean childBean = BeanEntityConverter.toLightBean(getPersistenceContext().getTopiaIdFactory(), child); + QuestionBean childBean = BeanEntityConverter.toLightBean(persistenceContext.getTopiaIdFactory(), child); result.addChild(childBean); } @@ -473,10 +513,10 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { result.setPrivacy(question.getPrivacy().name()); result.setRestricted(true); for (Question parent : question.getParents()) { - result.addParent(BeanEntityConverter.toLightBean(getPersistenceContext().getTopiaIdFactory(), parent)); + result.addParent(BeanEntityConverter.toLightBean(persistenceContext.getTopiaIdFactory(), parent)); } for (Question child : children) { - QuestionBean childBean = BeanEntityConverter.toLightBean(getPersistenceContext().getTopiaIdFactory(), child); + QuestionBean childBean = BeanEntityConverter.toLightBean(persistenceContext.getTopiaIdFactory(), child); result.addChild(childBean); } } @@ -485,10 +525,14 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return result; } - public void addDocuments(String questionId, DocumentBean[] documents) throws InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/questions/{questionId}/documents") + public void addDocuments(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("questionId") String questionId, + DocumentBean[] documents) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); UserWebToken userWebToken = checkAuthentication(authorization); // Only Supervisor can add documents @@ -507,11 +551,12 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } - String fullUserId = getFullIdFromShort(CoselmarUser.class, userWebToken.getUserId()); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String fullUserId = getFullIdFromShort(persistenceContext, CoselmarUser.class, userWebToken.getUserId()); CoselmarUser currentUser; try { - currentUser = getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); + currentUser = persistenceContext.getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); } catch (TopiaNoResultException tnre) { // Should not happened, cause user are not really deleted String message = String.format("Logged user ('%s') does not exist.", fullUserId); @@ -522,8 +567,8 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } // Retrieve Question - String fullQuestionId = getFullIdFromShort(Question.class, questionId); - Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + String fullQuestionId = getFullIdFromShort(persistenceContext, Question.class, questionId); + Question question = persistenceContext.getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); // Check expert authorization on the document checkIsParticipant(question, currentUser); @@ -533,7 +578,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Collection<Document> questionDocuments = question.getRelatedDocuments(); if (documents != null && documents.length > 0) { - Set<Document> documentEntities = retrieveDocuments(Lists.newArrayList(documents)); + Set<Document> documentEntities = retrieveDocuments(servicesContext, Lists.newArrayList(documents)); // Manage restriction list for document with Privacy.RESTRICTED for (Document document : documentEntities) { if (document.getPrivacy() == Privacy.RESTRICTED) { @@ -545,14 +590,17 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } } - commit(); + persistenceContext.commit(); } - public void saveQuestion(QuestionBean question) throws InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/questions/{questionId}") + public void saveQuestion(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + QuestionBean question) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser supervisor = checkUserAuthentication(authorization); + CoselmarUser supervisor = checkUserAuthentication(servicesContext, authorization); // Only Supervisor can save question @@ -572,6 +620,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Preconditions.checkNotNull(question.getType()); Preconditions.checkNotNull(question.getThemes()); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); // let's go Question questionEntity; @@ -579,12 +628,12 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String questionId = question.getId(); boolean inEdition = StringUtils.isNotBlank(questionId); if (inEdition) { - String fullQuestionId = getFullIdFromShort(Question.class, questionId); - questionEntity = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + String fullQuestionId = getFullIdFromShort(persistenceContext, Question.class, questionId); + questionEntity = persistenceContext.getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); } else { // or a create - questionEntity = getQuestionDao().create(); + questionEntity = persistenceContext.getQuestionDao().create(); } // Question basics @@ -657,7 +706,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Set<UserBean> clients = question.getClients(); questionEntity.clearClients(); if (clients != null && !clients.isEmpty()) { - Set<CoselmarUser> clientEntities = retrieveUsers(clients); + Set<CoselmarUser> clientEntities = retrieveUsers(servicesContext, clients); questionEntity.addAllClients(clientEntities); } @@ -665,7 +714,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Set<UserBean> contributors = question.getContributors(); questionEntity.clearContributors(); if (contributors != null && !contributors.isEmpty()) { - Set<CoselmarUser> contributorEntities = retrieveUsers(contributors); + Set<CoselmarUser> contributorEntities = retrieveUsers(servicesContext, contributors); questionEntity.addAllContributors(contributorEntities); } @@ -680,7 +729,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { participantGroup.setName(question.getTitle()); if (participants != null && !participants.isEmpty()) { - Set<CoselmarUser> expertEntities = retrieveUsers(participants); + Set<CoselmarUser> expertEntities = retrieveUsers(servicesContext, participants); // For each already assigned participants, if not in new list, assign them as contributors for (CoselmarUser participantBefore : participantGroup.getMembers()) { @@ -698,11 +747,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } else { // If not update, create new group - participantGroup = getCoselmarUserGroupDao().create(); + participantGroup = persistenceContext.getCoselmarUserGroupDao().create(); participantGroup.setName(question.getTitle()); if (participants != null && !participants.isEmpty()) { - Set<CoselmarUser> expertEntities = retrieveUsers(participants); + Set<CoselmarUser> expertEntities = retrieveUsers(servicesContext, participants); participantGroup.addAllMembers(expertEntities); } } @@ -712,7 +761,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Set<UserBean> supervisors = question.getSupervisors(); questionEntity.clearSupervisors(); if (supervisors != null && !supervisors.isEmpty()) { - Set<CoselmarUser> supervisorEntities = retrieveUsers(supervisors); + Set<CoselmarUser> supervisorEntities = retrieveUsers(servicesContext, supervisors); questionEntity.addAllSupervisors(supervisorEntities); } @@ -723,7 +772,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Hierarchy of questions Set<QuestionBean> parents = question.getParents(); if (parents != null && !parents.isEmpty()) { - Set<Question> questions = retrieveQuestions(parents); + Set<Question> questions = retrieveQuestions(servicesContext, parents); questionEntity.clearParents(); questionEntity.addAllParents(questions); @@ -737,7 +786,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Set<Document> documents = null; if (relatedDocuments != null && !relatedDocuments.isEmpty()) { - documents = retrieveDocuments(relatedDocuments); + documents = retrieveDocuments(servicesContext, relatedDocuments); } // In edition : make a diff, remove restricted list for removed documents, clear documents @@ -779,7 +828,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Set<Document> closingDocuments = null; if (closingDocumentBeans != null && !closingDocumentBeans.isEmpty()) { - closingDocuments = retrieveDocuments(closingDocumentBeans); + closingDocuments = retrieveDocuments(servicesContext, closingDocumentBeans); } // In edition : make a diff, remove restricted list for removed closing documents, clear closing documents @@ -823,8 +872,8 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Link linkEntity = null; String linkId = link.getId(); if (StringUtils.isNotBlank(linkId)) { - String linkFullId = getFullIdFromShort(Link.class, linkId); - linkEntity = getPersistenceContext().getLinkDao().forTopiaIdEquals(linkFullId).findUniqueOrNull(); + String linkFullId = getFullIdFromShort(persistenceContext, Link.class, linkId); + linkEntity = persistenceContext.getLinkDao().forTopiaIdEquals(linkFullId).findUniqueOrNull(); } if (linkEntity == null) { linkEntity = new LinkImpl(); @@ -833,20 +882,20 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { linkEntity.setUrl(link.getUrl()); if (StringUtils.isNotBlank(linkEntity.getTopiaId())) { - getPersistenceContext().getLinkDao().update(linkEntity); + persistenceContext.getLinkDao().update(linkEntity); } else { - getPersistenceContext().getLinkDao().create(linkEntity); + persistenceContext.getLinkDao().create(linkEntity); } questionEntity.addLinks(linkEntity); } } - commit(); + persistenceContext.commit(); - QuestionBean result = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), questionEntity); + QuestionBean result = BeanEntityConverter.toBean(persistenceContext.getTopiaIdFactory(), questionEntity); - QuestionsIndexationService questionsIndexationService = getServicesContext().newService(QuestionsIndexationService.class); + QuestionsIndexationService questionsIndexationService = servicesContext.newService(QuestionsIndexationService.class); try { questionsIndexationService.indexQuestion(result); if (log.isDebugEnabled()) { @@ -860,7 +909,9 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } } - public List<String> getThemes() throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/questions/themes") + public List<String> getThemes(@Context CoselmarServicesContext servicesContext) throws InvalidCredentialException, UnauthorizedException { //XXX ymartel 20141211 : do we need authentication check for that ? // // Check authentication @@ -871,12 +922,14 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // String fullCurrentUserId = getFullUserIdFromShort(userWebToken.getUserId()); // getCoselmarUserDao().forTopiaIdEquals(fullCurrentUserId).findAny(); - List<String> themes = getQuestionDao().findAllThemes(); + List<String> themes = servicesContext.getPersistenceContext().getQuestionDao().findAllThemes(); return themes; } - public List<String> getTypes() throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/questions/types") + public List<String> getTypes(@Context CoselmarServicesContext servicesContext) throws InvalidCredentialException, UnauthorizedException { //XXX ymartel 20141211 : do we need authentication check for that ? // // Check authentication @@ -887,14 +940,16 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // String fullCurrentUserId = getFullUserIdFromShort(userWebToken.getUserId()); // getCoselmarUserDao().forTopiaIdEquals(fullCurrentUserId).findAny(); - List<String> types = getQuestionDao().findAllTypes(); + List<String> types = servicesContext.getPersistenceContext().getQuestionDao().findAllTypes(); return types; } - public Render exportQuestions(QuestionSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { + public Response exportQuestions(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @QueryParam("searchOption") QuestionSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { - List<QuestionBean> questions = getQuestions(searchOption); + List<QuestionBean> questions = getQuestions(servicesContext, authorization, searchOption); QuestionExportModel exportModel = new QuestionExportModel(); @@ -908,24 +963,33 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { throw new CoselmarTechnicalException("Unable to export datas"); } - return renderDownload(IOUtils.toInputStream(exportData), "export-projects", "text/csv"); + Response.ResponseBuilder responseBuilder = Response.ok(IOUtils.toInputStream(exportData)); + responseBuilder.header("Content-Disposition", "attachment; filename=export-projects.csv"); + return responseBuilder.build(); } - public Render exportSearchedQuestions(String token, QuestionSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/export/questions") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_OCTET_STREAM) + public Response exportSearchedQuestions(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @FormParam("token") String token, + @FormParam("searchOption") QuestionSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { - CoselmarUser currentUser = checkUserAuthentication(token); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, token); List<Question> questionList; if (searchOption != null) { - questionList = getAllFilteredQuestions(currentUser, searchOption); + questionList = getAllFilteredQuestions(servicesContext, currentUser, searchOption); } else { - questionList = getAllQuestions(currentUser); + questionList = getAllQuestions(servicesContext, currentUser); } - List<QuestionBean> questions = convert(currentUser, questionList); + List<QuestionBean> questions = convert(servicesContext, currentUser, questionList); QuestionExportModel exportModel = new QuestionExportModel(); @@ -939,24 +1003,30 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { throw new CoselmarTechnicalException("Unable to export datas"); } - return renderDownload(IOUtils.toInputStream(exportData), "export-projects-result.csv", "text/csv"); + Response.ResponseBuilder responseBuilder = Response.ok(IOUtils.toInputStream(exportData)); + responseBuilder.header("Content-Disposition", "attachment; filename=export-projects-result.csv"); + return responseBuilder.build(); } - public List<QuestionTreeNode> getAncestors(String questionId, int depth) throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/questions/{questionId}/ancestors") + public List<QuestionTreeNode> getAncestors(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("questionId") String questionId, int depth) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); // Supervisor can get all the question elements // Expert can get all the question elements if public or if he is participant of the question // Client can get the question (not the documents) if he is client of the question. + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); // Member cannot access to question if (CoselmarUserRole.MEMBER == currentUser.getRole()) { String message = String.format("User %s %s ('%s') is not allowed to view question", - currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId())); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(persistenceContext, currentUser.getTopiaId())); if (log.isWarnEnabled()) { log.warn(message); } @@ -965,8 +1035,8 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } // Retrieve Question - String fullQuestionId = getFullIdFromShort(Question.class, questionId); - Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + String fullQuestionId = getFullIdFromShort(persistenceContext, Question.class, questionId); + Question question = persistenceContext.getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); if (CoselmarUserRole.CLIENT == currentUser.getRole()) { // Client User can access if it is client of question @@ -976,7 +1046,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { List<QuestionTreeNode> result; if (depth > 0) { - result = buildAncestorsTree(fullQuestionId, depth); + result = buildAncestorsTree(servicesContext, fullQuestionId, depth); } else { result = Collections.emptyList(); } @@ -984,20 +1054,24 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return result; } - public List<QuestionTreeNode> getDescendants(String questionId, int depth) throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/questions/{questionId}/descendants") + public List<QuestionTreeNode> getDescendants(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("questionId") String questionId, int depth) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); // Supervisor can get all the question elements // Expert can get all the question elements if public or if he is participant of the question // Client can get the question (not the documents) if he is client of the question. + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); // Member cannot access to question if (CoselmarUserRole.MEMBER == currentUser.getRole()) { String message = String.format("User %s %s ('%s') is not allowed to view question", - currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId())); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(persistenceContext, currentUser.getTopiaId())); if (log.isWarnEnabled()) { log.warn(message); } @@ -1006,8 +1080,8 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } // Retrieve Question - String fullQuestionId = getFullIdFromShort(Question.class, questionId); - Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + String fullQuestionId = getFullIdFromShort(persistenceContext, Question.class, questionId); + Question question = persistenceContext.getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); if (CoselmarUserRole.CLIENT == currentUser.getRole()) { // Client User can access if it is client of question @@ -1017,7 +1091,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { List<QuestionTreeNode> result; if (depth > 0) { - result = buildDescendantsTree(fullQuestionId, depth); + result = buildDescendantsTree(servicesContext, fullQuestionId, depth); } else { result = Collections.emptyList(); } @@ -1025,16 +1099,21 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return result; } - public List<QuestionBean> getUserQuestions(String userId, QuestionUserRole userRole) throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/users/{userId}/projects") + public List<QuestionBean> getUserQuestions(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("userId") String userId, + @QueryParam("userRole") QuestionUserRole userRole) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getAuthorizationHeader(); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); // Member cannot access to list of questions if (CoselmarUserRole.MEMBER == currentUser.getRole()) { String message = String.format("User %s %s ('%s') is not allowed to view question", - currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(currentUser.getTopiaId())); + currentUser.getFirstname(), currentUser.getName(), getShortIdFromFull(persistenceContext, currentUser.getTopiaId())); if (log.isWarnEnabled()) { log.warn(message); } @@ -1043,7 +1122,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { QuestionSearchBean searchBean = new QuestionSearchBean(); - String userFullId = getFullUserIdFromShort(userId); + String userFullId = getFullUserIdFromShort(persistenceContext, userId); switch (userRole) { case CONTRIBUTOR: searchBean.setContributorId(userFullId); @@ -1061,28 +1140,32 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return Collections.emptyList(); } - List<Question> questionList = getAllFilteredQuestions(currentUser, searchBean); + List<Question> questionList = getAllFilteredQuestions(servicesContext, currentUser, searchBean); - List<QuestionBean> result = convert(currentUser, questionList); + List<QuestionBean> result = convert(servicesContext, currentUser, questionList); return result; } - public List<CloudWord> getTopWords(String questionId) throws InvalidCredentialException, UnauthorizedException, NoResultException { + @GET + @Path("/v1/questions/{questionId}/topwords") + public List<CloudWord> getTopWords(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("questionId") String questionId) throws InvalidCredentialException, UnauthorizedException, NoResultException { // Check authentication - String authorization = getAuthorizationHeader(); - checkUserAuthentication(authorization); + checkUserAuthentication(servicesContext, authorization); // Retrieve Question - String fullQuestionId = getFullIdFromShort(Question.class, questionId); - Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String fullQuestionId = getFullIdFromShort(persistenceContext, Question.class, questionId); + Question question = persistenceContext.getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); List<CloudWord> topWords = new ArrayList<>(); - if (getCoselmarServicesConfig().isPostgresqlDatabase()) { + if (servicesContext.getCoselmarServicesConfig().isPostgresqlDatabase()) { try { - topWords = getQuestionDao().findTopWords(getFullIdFromShort(Question.class, questionId)); + topWords = persistenceContext.getQuestionDao().findTopWords(getFullIdFromShort(persistenceContext, Question.class, questionId)); } catch (TopiaNoResultException e) { if (log.isErrorEnabled()) { log.error("Try to find top words for non existing questionId" + questionId, e); @@ -1091,11 +1174,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } } else { - QuestionsIndexationService questionsIndexationService = getServicesContext().newService(QuestionsIndexationService.class); - DocumentsIndexationService documentsIndexationService = getServicesContext().newService(DocumentsIndexationService.class); + QuestionsIndexationService questionsIndexationService = servicesContext.newService(QuestionsIndexationService.class); + DocumentsIndexationService documentsIndexationService = servicesContext.newService(DocumentsIndexationService.class); try { Map<String, Long> topQuestionsTerms = questionsIndexationService.getTopQuestionsTerms(Lists.newArrayList(questionId)); - List<String> shortDocumentIds = getShortDocumentIds(question); + List<String> shortDocumentIds = getShortDocumentIds(persistenceContext, question); Map<String, Long> topDocumentsTerms = documentsIndexationService.getTopDocumentsTerms(shortDocumentIds); for (Map.Entry<String, Long> documentTermFreq : topDocumentsTerms.entrySet()) { String term = documentTermFreq.getKey(); @@ -1170,44 +1253,47 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } } - protected Set<CoselmarUser> retrieveUsers(Collection<UserBean> userBeans) { + protected Set<CoselmarUser> retrieveUsers(CoselmarServicesContext servicesContext, Collection<UserBean> userBeans) { + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); Function<UserBean, String> getIds = new Function<UserBean, String>() { @Override public String apply(UserBean userBean) { - return getFullIdFromShort(CoselmarUser.class, userBean.getId()); + return getFullIdFromShort(persistenceContext, CoselmarUser.class, userBean.getId()); } }; Collection<String> userIds = Collections2.transform(userBeans, getIds); - List<CoselmarUser> coselmarUsers = getCoselmarUserDao().forTopiaIdIn(userIds).findAll(); + List<CoselmarUser> coselmarUsers = persistenceContext.getCoselmarUserDao().forTopiaIdIn(userIds).findAll(); return new HashSet<>(coselmarUsers); } - protected Set<Question> retrieveQuestions(Collection<QuestionBean> questionBeans) { + protected Set<Question> retrieveQuestions(CoselmarServicesContext servicesContext, Collection<QuestionBean> questionBeans) { + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); Function<QuestionBean, String> getIds = new Function<QuestionBean, String>() { @Override public String apply(QuestionBean questionBean) { - return getFullIdFromShort(Question.class, questionBean.getId()); + return getFullIdFromShort(persistenceContext, Question.class, questionBean.getId()); } }; Collection<String> questionIds = Collections2.transform(questionBeans, getIds); - List<Question> questions = getQuestionDao().forTopiaIdIn(questionIds).findAll(); + List<Question> questions = persistenceContext.getQuestionDao().forTopiaIdIn(questionIds).findAll(); return new HashSet<>(questions); } - protected Set<Document> retrieveDocuments(Collection<DocumentBean> documentBeans) { + protected Set<Document> retrieveDocuments(CoselmarServicesContext servicesContext, Collection<DocumentBean> documentBeans) { + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); Function<DocumentBean, String> getIds = new Function<DocumentBean, String>() { @Override public String apply(DocumentBean documentBean) { - return getFullIdFromShort(Document.class, documentBean.getId()); + return getFullIdFromShort(persistenceContext, Document.class, documentBean.getId()); } }; Collection<String> documentIds = Collections2.transform(documentBeans, getIds); - List<Document> documents = getDocumentDao().forTopiaIdIn(documentIds).findAll(); + List<Document> documents = persistenceContext.getDocumentDao().forTopiaIdIn(documentIds).findAll(); return new HashSet<>(documents); } @@ -1226,22 +1312,23 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { * @return list of all question user is authorized to access. * @throws UnauthorizedException */ - protected List<Question> getAllQuestions(CoselmarUser currentUser) throws UnauthorizedException { + protected List<Question> getAllQuestions(CoselmarServicesContext servicesContext, CoselmarUser currentUser) throws UnauthorizedException { String currentUserRole = currentUser.getRole().name(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); List<Question> questionList; if (StringUtils.equalsIgnoreCase(CoselmarUserRole.ADMIN.name(), currentUserRole) || StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), currentUserRole)) { - questionList = getQuestionDao().findAll(); + questionList = persistenceContext.getQuestionDao().findAll(); } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.MEMBER.name(), currentUserRole)) { - questionList = getQuestionDao().forPrivacyEquals(Privacy.PUBLIC).findAll(); + questionList = persistenceContext.getQuestionDao().forPrivacyEquals(Privacy.PUBLIC).findAll(); } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.EXPERT.name(), currentUserRole)) { - questionList = getQuestionDao().findForExpert(currentUser); + questionList = persistenceContext.getQuestionDao().findForExpert(currentUser); } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.CLIENT.name(), currentUserRole)) { - questionList = getQuestionDao().forClientsContains(currentUser).findAll(); + questionList = persistenceContext.getQuestionDao().forClientsContains(currentUser).findAll(); } else { String message = "Not allowed to access this page"; if (log.isWarnEnabled()) { @@ -1268,11 +1355,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { * @return list of all question user is authorized to access. * @throws UnauthorizedException * - * @deprecated use {@link #getPaginatedFilteredQuestions(CoselmarUser, QuestionSearchBean)} + * @deprecated use {@link #getPaginatedFilteredQuestions(CoselmarServicesContext, CoselmarUser, QuestionSearchBean)} */ - protected List<Question> getAllFilteredQuestions(CoselmarUser currentUser, QuestionSearchBean searchBean) throws UnauthorizedException { + protected List<Question> getAllFilteredQuestions(CoselmarServicesContext servicesContext, CoselmarUser currentUser, QuestionSearchBean searchBean) throws UnauthorizedException { - PaginationResult<Question> paginatedFilteredQuestions = getPaginatedFilteredQuestions(currentUser, searchBean); + PaginationResult<Question> paginatedFilteredQuestions = getPaginatedFilteredQuestions(servicesContext, currentUser, searchBean); return paginatedFilteredQuestions.getElements(); } @@ -1293,16 +1380,16 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { * @return {@link PaginationResult} of questions user is authorized to access. * @throws UnauthorizedException */ - protected PaginationResult<Question> getPaginatedFilteredQuestions(CoselmarUser currentUser, QuestionSearchBean searchBean) throws UnauthorizedException { + protected PaginationResult<Question> getPaginatedFilteredQuestions(CoselmarServicesContext servicesContext, CoselmarUser currentUser, QuestionSearchBean searchBean) throws UnauthorizedException { - QuestionsIndexationService questionsIndexationService = getServicesContext().newService(QuestionsIndexationService.class); + QuestionsIndexationService questionsIndexationService = servicesContext.newService(QuestionsIndexationService.class); //Try to retrieve corresponding questionIds from the index if searchBean given List<String> fromIndexQuestionIds = null; if (searchBean != null && searchBean.getFullTextSearch() != null && !searchBean.getFullTextSearch().isEmpty()) { try { List<String> questionIds = questionsIndexationService.searchQuestion(searchBean); - fromIndexQuestionIds = getQuestionsFullId(questionIds); + fromIndexQuestionIds = getQuestionsFullId(servicesContext.getPersistenceContext(), questionIds); if (log.isInfoEnabled()) { log.info("Found " + fromIndexQuestionIds.size() + " questions from index."); } @@ -1320,6 +1407,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String currentUserRole = currentUser.getRole().name(); QuestionSearchExample searchExample = QuestionSearchExample.newDefaultSearchExample(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); if (searchBean != null) { if (searchBean.getPage() != null) { searchExample.setPage(searchBean.getPage()); @@ -1353,26 +1441,26 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } if (StringUtils.isNotBlank(searchBean.getParticipantId())) { - CoselmarUser user = getCoselmarUserDao().forTopiaIdEquals(searchBean.getParticipantId()).findAnyOrNull(); + CoselmarUser user = persistenceContext.getCoselmarUserDao().forTopiaIdEquals(searchBean.getParticipantId()).findAnyOrNull(); if (user != null) { searchExample.setParticipant(user); } } if (StringUtils.isNotBlank(searchBean.getContributorId())) { - CoselmarUser user = getCoselmarUserDao().forTopiaIdEquals(searchBean.getContributorId()).findAnyOrNull(); + CoselmarUser user = persistenceContext.getCoselmarUserDao().forTopiaIdEquals(searchBean.getContributorId()).findAnyOrNull(); if (user != null) { example.addContributors(user); } } if (StringUtils.isNotBlank(searchBean.getSupervisorId())) { - CoselmarUser user = getCoselmarUserDao().forTopiaIdEquals(searchBean.getSupervisorId()).findAnyOrNull(); + CoselmarUser user = persistenceContext.getCoselmarUserDao().forTopiaIdEquals(searchBean.getSupervisorId()).findAnyOrNull(); if (user != null) { example.addSupervisors(user); } } if (StringUtils.isNotBlank(searchBean.getClientId())) { - CoselmarUser user = getCoselmarUserDao().forTopiaIdEquals(searchBean.getClientId()).findAnyOrNull(); + CoselmarUser user = persistenceContext.getCoselmarUserDao().forTopiaIdEquals(searchBean.getClientId()).findAnyOrNull(); if (user != null) { example.addClients(user); } @@ -1387,11 +1475,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { || StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), currentUserRole)) { if (fromIndexQuestionIds != null && !fromIndexQuestionIds.isEmpty()) { - paginatedQuestions = getQuestionDao().forTopiaIdIn(fromIndexQuestionIds).findPage(paginationParameter); + paginatedQuestions = persistenceContext.getQuestionDao().forTopiaIdIn(fromIndexQuestionIds).findPage(paginationParameter); // classical search with DAO } else { - paginatedQuestions = getQuestionDao().findWithSearchExample(searchExample, paginationParameter); + paginatedQuestions = persistenceContext.getQuestionDao().findWithSearchExample(searchExample, paginationParameter); } @@ -1405,30 +1493,30 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } else { if (fromIndexQuestionIds != null && !fromIndexQuestionIds.isEmpty()) { - paginatedQuestions = getQuestionDao().forTopiaIdIn(fromIndexQuestionIds).findPage(paginationParameter); + paginatedQuestions = persistenceContext.getQuestionDao().forTopiaIdIn(fromIndexQuestionIds).findPage(paginationParameter); // classical search with DAO } else { - paginatedQuestions = getQuestionDao().findWithSearchExample(searchExample, paginationParameter); + paginatedQuestions = persistenceContext.getQuestionDao().findWithSearchExample(searchExample, paginationParameter); } } // Expert : access to all public question and private question if he is participant or client } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.EXPERT.name(), currentUserRole)) { if (fromIndexQuestionIds != null && !fromIndexQuestionIds.isEmpty()) { - paginatedQuestions = getQuestionDao().findForExpert(currentUser, fromIndexQuestionIds, paginationParameter); + paginatedQuestions = persistenceContext.getQuestionDao().findForExpert(currentUser, fromIndexQuestionIds, paginationParameter); } else { - paginatedQuestions = getQuestionDao().findForExpert(currentUser, searchExample, paginationParameter); + paginatedQuestions = persistenceContext.getQuestionDao().findForExpert(currentUser, searchExample, paginationParameter); } // Client : access to question he is client } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.CLIENT.name(), currentUserRole)) { if (fromIndexQuestionIds != null && !fromIndexQuestionIds.isEmpty()) { - paginatedQuestions = getQuestionDao().findForClient(currentUser, fromIndexQuestionIds, paginationParameter); + paginatedQuestions = persistenceContext.getQuestionDao().findForClient(currentUser, fromIndexQuestionIds, paginationParameter); } else { - paginatedQuestions = getQuestionDao().findForClient(currentUser, searchExample, paginationParameter); + paginatedQuestions = persistenceContext.getQuestionDao().findForClient(currentUser, searchExample, paginationParameter); } } else { @@ -1441,12 +1529,12 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return paginatedQuestions; } - protected List<String> getQuestionsFullId(List<String> questionShortIds) { + protected List<String> getQuestionsFullId(CoselmarPersistenceContext persistenceContext, List<String> questionShortIds) { Function<String, String> getFullIds = new Function<String, String>() { @Override public String apply(String shortId) { - return Question.class.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + shortId; + return getFullIdFromShort(persistenceContext, Question.class, shortId); } }; @@ -1454,11 +1542,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return fullIds; } - protected List<QuestionBean> convert(CoselmarUser currentUser, List<Question> questionList) { + protected List<QuestionBean> convert(CoselmarServicesContext servicesContext, CoselmarUser currentUser, List<Question> questionList) { List<QuestionBean> questions = new ArrayList<>(questionList.size()); for (Question question : questionList) { - TopiaIdFactory topiaIdFactory = getPersistenceContext().getTopiaIdFactory(); + TopiaIdFactory topiaIdFactory = servicesContext.getPersistenceContext().getTopiaIdFactory(); QuestionBean questionBean; if (RESTRICTED_ACCESS_USERS.contains(currentUser.getRole().name())) { @@ -1473,11 +1561,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return questions; } - protected PaginationResult<QuestionBean> convert(CoselmarUser currentUser, PaginationResult<Question> paginatedQuestions) { + protected PaginationResult<QuestionBean> convert(CoselmarServicesContext servicesContext, CoselmarUser currentUser, PaginationResult<Question> paginatedQuestions) { List<QuestionBean> questions = new ArrayList<>(paginatedQuestions.getElements().size()); for (Question question : paginatedQuestions.getElements()) { - TopiaIdFactory topiaIdFactory = getPersistenceContext().getTopiaIdFactory(); + TopiaIdFactory topiaIdFactory = servicesContext.getPersistenceContext().getTopiaIdFactory(); QuestionBean questionBean; if (RESTRICTED_ACCESS_USERS.contains(currentUser.getRole().name())) { @@ -1494,13 +1582,14 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return paginatedQuestionBeans; } - protected List<QuestionTreeNode> buildAncestorsTree(String questionId, int depth) { + protected List<QuestionTreeNode> buildAncestorsTree(CoselmarServicesContext servicesContext, String questionId, int depth) { // Depth = 0 : it is the end of the tree ! if (depth == 0) { return Collections.emptyList(); } - Question question = getQuestionDao().forTopiaIdEquals(questionId).findUnique(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + Question question = persistenceContext.getQuestionDao().forTopiaIdEquals(questionId).findUnique(); Set<Question> parents = question.getParents(); List<QuestionTreeNode> result; @@ -1515,7 +1604,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { for (Question parent : parents) { QuestionTreeNode ancestor = new QuestionTreeNode(); String parentId = parent.getTopiaId(); - ancestor.setId(getShortIdFromFull(parentId)); + ancestor.setId(getShortIdFromFull(persistenceContext, parentId)); ancestor.setTitle(parent.getTitle()); ancestor.setThemes(Sets.newHashSet(parent.getTheme())); Date deadline = parent.getDeadline(); @@ -1528,7 +1617,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } ancestor.setType(parent.getType()); // Get ancestors of this parent - List<QuestionTreeNode> parentAncestors = buildAncestorsTree(parentId, depth - 1); + List<QuestionTreeNode> parentAncestors = buildAncestorsTree(servicesContext, parentId, depth - 1); ancestor.setAncestors(parentAncestors); result.add(ancestor); } @@ -1537,14 +1626,15 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return result; } - protected List<QuestionTreeNode> buildDescendantsTree(String questionId, int depth) { + protected List<QuestionTreeNode> buildDescendantsTree(CoselmarServicesContext servicesContext, String questionId, int depth) { // Depth = 0 : it is the end of the tree ! if (depth == 0) { return Collections.emptyList(); } - Question question = getQuestionDao().forTopiaIdEquals(questionId).findUnique(); - List<Question> children = getQuestionDao().forParentsContains(question).findAll(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + Question question = persistenceContext.getQuestionDao().forTopiaIdEquals(questionId).findUnique(); + List<Question> children = persistenceContext.getQuestionDao().forParentsContains(question).findAll(); List<QuestionTreeNode> result; @@ -1558,7 +1648,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { for (Question child : children) { QuestionTreeNode descendant = new QuestionTreeNode(); String parentId = child.getTopiaId(); - descendant.setId(getShortIdFromFull(parentId)); + descendant.setId(getShortIdFromFull(persistenceContext, parentId)); descendant.setTitle(child.getTitle()); descendant.setThemes(Sets.newHashSet(child.getTheme())); Date deadline = child.getDeadline(); @@ -1571,7 +1661,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } descendant.setType(child.getType()); // Get descendants of this child - List<QuestionTreeNode> childDescendants = buildDescendantsTree(parentId, depth - 1); + List<QuestionTreeNode> childDescendants = buildDescendantsTree(servicesContext, parentId, depth - 1); descendant.setDescendants(childDescendants); result.add(descendant); } @@ -1580,14 +1670,14 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return result; } - protected List<String> getShortDocumentIds(Question question) { + protected List<String> getShortDocumentIds(CoselmarPersistenceContext persistenceContext, Question question) { List<String> shortDocumentIds = new ArrayList<>(); for (String relatedDocumentId : question.getRelatedDocumentsTopiaIds()) { - String shortIdFromFull = getShortIdFromFull(relatedDocumentId); + String shortIdFromFull = getShortIdFromFull(persistenceContext, relatedDocumentId); shortDocumentIds.add(shortIdFromFull); } for (String closingDocumentId : question.getClosingDocumentsTopiaIds()) { - String shortIdFromFull = getShortIdFromFull(closingDocumentId); + String shortIdFromFull = getShortIdFromFull(persistenceContext, closingDocumentId); shortDocumentIds.add(shortIdFromFull); } return shortDocumentIds; diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java index 8b3fc00..1a8e2dd 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java @@ -31,6 +31,7 @@ import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheException; import com.github.mustachejava.MustacheFactory; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import fr.ifremer.coselmar.beans.AbstractMail; import fr.ifremer.coselmar.beans.LostPasswordMail; import fr.ifremer.coselmar.beans.UserAccountCreatedMail; @@ -45,6 +46,8 @@ import fr.ifremer.coselmar.exceptions.CoselmarTechnicalException; import fr.ifremer.coselmar.persistence.SearchRequestBean; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; +import fr.ifremer.coselmar.persistence.entity.CoselmarUserTopiaDao; +import fr.ifremer.coselmar.services.CoselmarServicesContext; import fr.ifremer.coselmar.services.CoselmarWebServiceSupport; import fr.ifremer.coselmar.services.errors.InvalidCredentialException; import fr.ifremer.coselmar.services.errors.MailAlreadyExistingException; @@ -56,13 +59,25 @@ import org.apache.commons.logging.Log; import org.apache.commons.mail.Email; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.SimpleEmail; -import org.debux.webmotion.server.render.Render; import org.nuiton.csv.Export; import org.nuiton.topia.persistence.TopiaNoResultException; import org.nuiton.util.StringUtil; import org.nuiton.util.pagination.PaginationParameter; import org.nuiton.util.pagination.PaginationResult; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +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 java.io.StringWriter; import java.security.InvalidParameterException; import java.util.ArrayList; @@ -75,18 +90,24 @@ import static org.apache.commons.logging.LogFactory.getLog; /** * @author ymartel <martel@codelutin.com> */ +@Path("") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) public class UsersWebService extends CoselmarWebServiceSupport { private static final Log log = getLog(UsersWebService.class); - public UserBean getUser(String userId) throws InvalidCredentialException, UnauthorizedException, TopiaNoResultException { + @GET + @Path("/v1/users/{userId}") + public UserBean getUser(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("userId") String userId) throws InvalidCredentialException, UnauthorizedException, TopiaNoResultException { // Check authentication - String authorization = getContext().getHeader("Authorization"); - CoselmarUser curentUser = checkUserAuthentication(authorization); + CoselmarUser curentUser = checkUserAuthentication(servicesContext, authorization); // Rebuild fullId - String fullId = getFullIdFromShort(CoselmarUser.class, userId); + String fullId = getFullIdFromShort(servicesContext.getPersistenceContext(), CoselmarUser.class, userId); UserBean userBean; if (fullId.equals(curentUser.getTopiaId())) { // Current user is asking for his info : ok @@ -95,7 +116,8 @@ public class UsersWebService extends CoselmarWebServiceSupport { // Current user wanna see an other profile ; only allowed to Admin or Supervisor (if it user is a client) // Get the asked user - CoselmarUser askedUser = getCoselmarUserDao().forTopiaIdEquals(fullId).findAny(); + CoselmarUserTopiaDao coselmarUserDao = servicesContext.getPersistenceContext().getCoselmarUserDao(); + CoselmarUser askedUser = coselmarUserDao.forTopiaIdEquals(fullId).findAny(); boolean isAdmin = curentUser.getRole() == CoselmarUserRole.ADMIN; boolean isSupervisor4Client = (curentUser.getRole() == CoselmarUserRole.SUPERVISOR && askedUser.getRole() == CoselmarUserRole.CLIENT); @@ -114,18 +136,25 @@ public class UsersWebService extends CoselmarWebServiceSupport { return userBean; } - public List<UserBean> getUsers(UserSearchBean search) throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/users") + public List<UserBean> getUsers(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @QueryParam("search") UserSearchBean search) throws InvalidCredentialException, UnauthorizedException { - PaginationResult<UserBean> paginatedUsers = getPaginatedUsers(search); + PaginationResult<UserBean> paginatedUsers = getPaginatedUsers(servicesContext, authorization, search); List<UserBean> result = paginatedUsers.getElements(); return result; } - public PaginationResult<UserBean> getPaginatedUsers(UserSearchBean search) throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v2/users") + public PaginationResult<UserBean> getPaginatedUsers(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @QueryParam("search") UserSearchBean search) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getContext().getHeader("Authorization"); UserWebToken userWebToken = checkAuthentication(authorization); // Who is allowed here ? Admin and user himself @@ -148,18 +177,19 @@ public class UsersWebService extends CoselmarWebServiceSupport { CoselmarUser example = BeanEntityConverter.fromBean(search); - usersPage = getCoselmarUserDao().findAllByExample(example, search.isActiveAndInactive(), requestBean); + usersPage = servicesContext.getPersistenceContext().getCoselmarUserDao().findAllByExample(example, search.isActiveAndInactive(), requestBean); } else { PaginationParameter paginationParameter = PaginationParameter.of(0, -1); - usersPage = getCoselmarUserDao().forAll().findPage(paginationParameter); + List<CoselmarUser> all = servicesContext.getPersistenceContext().getCoselmarUserDao().forAll().findAll(); + usersPage = PaginationResult.of(all, all.size(), paginationParameter); } List<UserBean> result = new ArrayList<>(usersPage.getElements().size()); for (CoselmarUser user : usersPage.getElements()) { - String userLightId = getShortIdFromFull(user.getTopiaId()); + String userLightId = getShortIdFromFull(servicesContext.getPersistenceContext(), user.getTopiaId()); UserBean userBean = BeanEntityConverter.toBean(userLightId, user); result.add(userBean); } @@ -169,11 +199,14 @@ public class UsersWebService extends CoselmarWebServiceSupport { return paginationResult; } - public List<UserBean> getExperts(UserSearchBean search) throws InvalidCredentialException, UnauthorizedException { + @GET + @Path("/v1/users/experts") + public List<UserBean> getExperts(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @QueryParam("search") UserSearchBean search) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getContext().getHeader("Authorization"); - CoselmarUser currentUser = checkUserAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); // Who is allowed here ? Admin, Supervisor, Expert if (currentUser.getRole() != CoselmarUserRole.ADMIN @@ -197,18 +230,18 @@ public class UsersWebService extends CoselmarWebServiceSupport { CoselmarUser example = BeanEntityConverter.fromBean(search); example.setRole(CoselmarUserRole.EXPERT);// we search experts here, force it ! - PaginationResult<CoselmarUser> userPaginationResult = getCoselmarUserDao().findAllByExample(example, search.isActiveAndInactive(), requestBean); + PaginationResult<CoselmarUser> userPaginationResult = servicesContext.getPersistenceContext().getCoselmarUserDao().findAllByExample(example, search.isActiveAndInactive(), requestBean); userList = userPaginationResult.getElements(); } else { - userList = getCoselmarUserDao().forRoleEquals(CoselmarUserRole.EXPERT).findAll(); + userList = servicesContext.getPersistenceContext().getCoselmarUserDao().forRoleEquals(CoselmarUserRole.EXPERT).findAll(); } List<UserBean> result = new ArrayList<>(userList.size()); for (CoselmarUser user : userList) { - String userLightId = getShortIdFromFull(user.getTopiaId()); + String userLightId = getShortIdFromFull(servicesContext.getPersistenceContext(), user.getTopiaId()); UserBean userBean = BeanEntityConverter.toBean(userLightId, user); result.add(userBean); } @@ -216,16 +249,20 @@ public class UsersWebService extends CoselmarWebServiceSupport { return result; } - public void addUser(UserBean user) throws MailAlreadyExistingException, InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/users") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public void addUser(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @FormParam("user") UserBean user) throws MailAlreadyExistingException, InvalidCredentialException, UnauthorizedException { Preconditions.checkNotNull(user); // Check authentication - String authorization = getAuthorizationHeader(); - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); // Who is allowed here ? Admin and Superviseur - if (!StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.ADMIN.name()) - && StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.SUPERVISOR.name()) + if (!StringUtils.equals(currentUser.getRole().name(), CoselmarUserRole.ADMIN.name()) + && StringUtils.equals(currentUser.getRole().name(), CoselmarUserRole.SUPERVISOR.name()) && !StringUtils.equals(user.getRole(), CoselmarUserRole.CLIENT.name()) ) { if (log.isDebugEnabled()) { @@ -235,14 +272,14 @@ public class UsersWebService extends CoselmarWebServiceSupport { throw new UnauthorizedException("Not allowed to add user"); } - CoselmarUser userEntity = getCoselmarUserDao().create(); + CoselmarUser userEntity = servicesContext.getPersistenceContext().getCoselmarUserDao().create(); userEntity.setFirstname(user.getFirstName()); userEntity.setName(user.getName()); - String mail = getCleanMail(user.getMail()); + String mail = servicesContext.getCleanMail(user.getMail()); if (StringUtils.isNotBlank(mail)) { - checkMailUniqueness(mail, null); + checkMailUniqueness(servicesContext, mail, null); userEntity.setMail(mail); } @@ -253,39 +290,43 @@ public class UsersWebService extends CoselmarWebServiceSupport { String password = user.getPassword(); if (StringUtils.isBlank(password)) { - password = getServicesContext().generatePassword(); + password = servicesContext.generatePassword(); } //generate a password & a salt - String salt = getServicesContext().generateSalt(); - String encodedPassword = getServicesContext().encodePassword(salt, password); + String salt = servicesContext.generateSalt(); + String encodedPassword = servicesContext.encodePassword(salt, password); userEntity.setPassword(encodedPassword); userEntity.setSalt(salt); - commit(); + servicesContext.getPersistenceContext().commit(); // send mail to user with password if (StringUtils.isNotBlank(mail) && StringUtil.isEmail(mail)) { - UserAccountCreatedMail userAccountCreatedMail = new UserAccountCreatedMail(getServicesContext().getLocale()); + UserAccountCreatedMail userAccountCreatedMail = new UserAccountCreatedMail(servicesContext.getLocale()); userAccountCreatedMail.setUser(user); userAccountCreatedMail.setPassword(password); userAccountCreatedMail.setTo(user.getMail()); - sendMail(userAccountCreatedMail); + sendMail(servicesContext.getCoselmarServicesConfig(), userAccountCreatedMail); } } - public void modifyUser(UserBean user) throws InvalidCredentialException, UnauthorizedException, InvalidParameterException, TopiaNoResultException, MailAlreadyExistingException { + @POST + @Path("/v1/users/{userId}") + public void modifyUser(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("userId") String userId, + UserBean user) throws InvalidCredentialException, UnauthorizedException, InvalidParameterException, TopiaNoResultException, MailAlreadyExistingException { // Check authentication - String authorization = getContext().getHeader("Authorization"); UserWebToken userWebToken = checkAuthentication(authorization); boolean isAdmin = StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.ADMIN.name()); boolean isSupervisor4Client = StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.SUPERVISOR.name()) && StringUtils.equals(user.getRole(), CoselmarUserRole.CLIENT.name()); - String userId = user.getId(); - if (StringUtils.isBlank(userId)) { + String askedUserId = user.getId(); + if (StringUtils.isBlank(askedUserId)) { throw new InvalidParameterException("User.id is mandatory"); } @@ -295,22 +336,21 @@ public class UsersWebService extends CoselmarWebServiceSupport { } // Who is allowed here ? Admin and user himself only and Supervisor if it is a "client" type user - if (!isAdmin && !StringUtils.equals(userWebToken.getUserId(), userId) && !isSupervisor4Client) { + if (!isAdmin && !StringUtils.equals(userWebToken.getUserId(), askedUserId) && !isSupervisor4Client) { if (log.isDebugEnabled()) { - String message = String.format("A non admin user try to modify account details with shortId '%s'", userId); + String message = String.format("A non admin user try to modify account details with shortId '%s'", askedUserId); log.debug(message); } throw new UnauthorizedException("Not allowed to modify user details"); } // Ok, now, retrieve this user - String fullId = CoselmarUser.class.getCanonicalName() + - getPersistenceContext().getTopiaIdFactory().getSeparator() + userId; - CoselmarUser coselmarUser = getCoselmarUserDao().forTopiaIdEquals(fullId).findAny(); + String fullId = getFullIdFromShort(servicesContext.getPersistenceContext(), CoselmarUser.class, askedUserId); + CoselmarUser coselmarUser = servicesContext.getPersistenceContext().getCoselmarUserDao().forTopiaIdEquals(fullId).findAny(); // Last check : the password if (!isAdmin && !isSupervisor4Client) { - checkPassword(coselmarUser.getPassword(), coselmarUser.getSalt(), user.getPassword()); + checkPassword(servicesContext, coselmarUser.getPassword(), coselmarUser.getSalt(), user.getPassword()); } // Ok, now let's start the user update ! @@ -318,7 +358,7 @@ public class UsersWebService extends CoselmarWebServiceSupport { // start with mail : should be unique String mail = user.getMail(); if (StringUtils.isNotBlank(mail)) { - checkMailUniqueness(mail, fullId); + checkMailUniqueness(servicesContext, mail, fullId); coselmarUser.setMail(mail); } else { coselmarUser.setMail(null); @@ -361,60 +401,68 @@ public class UsersWebService extends CoselmarWebServiceSupport { String newPassword = user.getNewPassword(); if (StringUtils.isNotBlank(newPassword)) { - String salt = getServicesContext().generateSalt(); - String encodedPassword = getServicesContext().encodePassword(salt, newPassword); + String salt = servicesContext.generateSalt(); + String encodedPassword = servicesContext.encodePassword(salt, newPassword); coselmarUser.setSalt(salt); coselmarUser.setPassword(encodedPassword); //if it is a modification by Admin, send new mail to user if ( (isAdmin || isSupervisor4Client) && StringUtil.isEmail(coselmarUser.getMail())) { - UserPasswordChangedMail userPasswordChangedMail = new UserPasswordChangedMail(getServicesContext().getLocale()); + UserPasswordChangedMail userPasswordChangedMail = new UserPasswordChangedMail(servicesContext.getLocale()); userPasswordChangedMail.setUser(user); userPasswordChangedMail.setPassword(newPassword); userPasswordChangedMail.setTo(coselmarUser.getMail()); - sendMail(userPasswordChangedMail); + sendMail(servicesContext.getCoselmarServicesConfig(), userPasswordChangedMail); } } boolean active = user.isActive(); coselmarUser.setActive(active); - commit(); + servicesContext.getPersistenceContext().commit(); } - public Render login(String mail, String password) throws InvalidCredentialException { + @POST + @Path("/v1/users/login") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public Map<String, String> login(@Context CoselmarServicesContext servicesContext, + @FormParam("mail") String mail, + @FormParam("password") String password) throws InvalidCredentialException { Preconditions.checkNotNull(mail); Preconditions.checkNotNull(password); - CoselmarUser user = getCoselmarUserDao().forMailEquals(getCleanMail(mail)).addEquals(CoselmarUser.PROPERTY_ACTIVE, true).findAnyOrNull(); + CoselmarUser user = servicesContext.getPersistenceContext().getCoselmarUserDao().forMailEquals(servicesContext.getCleanMail(mail)).addEquals(CoselmarUser.PROPERTY_ACTIVE, true).findAnyOrNull(); if (user == null) { throw new InvalidCredentialException("Invalid mail"); } String salt = user.getSalt(); - checkPassword(user.getPassword(), salt, password); + checkPassword(servicesContext, user.getPassword(), salt, password); // return a Json Web Token for authentication - JWTSigner jwtSigner = new JWTSigner(getCoselmarServicesConfig().getWebSecurityKey()); + JWTSigner jwtSigner = new JWTSigner(servicesContext.getCoselmarServicesConfig().getWebSecurityKey()); JWTSigner.Options signerOption = new JWTSigner.Options(); signerOption.setAlgorithm(Algorithm.HS384); - String shortId = getShortIdFromFull(user.getTopiaId()); + String shortId = getShortIdFromFull(servicesContext.getPersistenceContext(), user.getTopiaId()); Map<String, Object> claims = UserWebToken.toJwtClaims(shortId, user.getFirstname(), user.getName(), user.getRole().name()); String webToken = jwtSigner.sign(claims, signerOption); - return renderJSON("jwt", webToken); + return ImmutableMap.of("jwt", webToken); } - public void deleteUser(String userId) throws InvalidCredentialException, UnauthorizedException { + @DELETE + @Path("/v1/users/{userId}") + public void deleteUser(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @PathParam("userId") String userId) throws InvalidCredentialException, UnauthorizedException { // Check authentication - String authorization = getContext().getHeader("Authorization"); - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, authorization); - boolean isAdmin = StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.ADMIN.name()); + boolean isAdmin = currentUser.getRole() == CoselmarUserRole.ADMIN; // Only admin is authorized to do this if (!isAdmin) { @@ -428,43 +476,55 @@ public class UsersWebService extends CoselmarWebServiceSupport { // reconstitute full id String fullId = CoselmarUser.class.getCanonicalName() + "_" + userId; - CoselmarUser user = getCoselmarUserDao().forTopiaIdEquals(fullId).findUnique(); + CoselmarUserTopiaDao coselmarUserDao = servicesContext.getPersistenceContext().getCoselmarUserDao(); + CoselmarUser user = coselmarUserDao.forTopiaIdEquals(fullId).findUnique(); - getCoselmarUserDao().delete(user); + coselmarUserDao.delete(user); - commit(); + servicesContext.getPersistenceContext().commit(); } - public void generateNewPassword(String userMail) { + @POST + @Path("/v1/users/password") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public void generateNewPassword(@Context CoselmarServicesContext servicesContext, + @FormParam("userMail") String userMail) { // Retrieve user - CoselmarUser user = getCoselmarUserDao().forMailEquals(userMail).findUnique(); + CoselmarUser user = servicesContext.getPersistenceContext().getCoselmarUserDao().forMailEquals(userMail).findUnique(); // create new password - String password = getServicesContext().generatePassword(); + String password = servicesContext.generatePassword(); // Salt it, encode it ! - String salt = getServicesContext().generateSalt(); - String encodedPassword = getServicesContext().encodePassword(salt, password); + String salt = servicesContext.generateSalt(); + String encodedPassword = servicesContext.encodePassword(salt, password); user.setPassword(encodedPassword); user.setSalt(salt); // commit, and send mail ! - commit(); + servicesContext.getPersistenceContext().commit(); - LostPasswordMail lostPasswordMail = new LostPasswordMail(getServicesContext().getLocale()); - String shortId = getShortIdFromFull(user.getTopiaId()); + LostPasswordMail lostPasswordMail = new LostPasswordMail(servicesContext.getLocale()); + String shortId = getShortIdFromFull(servicesContext.getPersistenceContext(), user.getTopiaId()); UserBean userBean = BeanEntityConverter.toBean(shortId, user); lostPasswordMail.setUser(userBean); lostPasswordMail.setPassword(password); lostPasswordMail.setTo(user.getMail()); - sendMail(lostPasswordMail); + sendMail(servicesContext.getCoselmarServicesConfig(), lostPasswordMail); } - public Render exportSearchedUsers(String token, UserSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { + @POST + @Path("/v1/export/users") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_OCTET_STREAM) + public Response exportSearchedUsers(@Context CoselmarServicesContext servicesContext, + @HeaderParam(AUTHORIZATION_HEADER) String authorization, + @FormParam("token") String token, + @FormParam("searchOption") UserSearchBean searchOption) throws InvalidCredentialException, UnauthorizedException { - CoselmarUser currentUser = checkUserAuthentication(token); + CoselmarUser currentUser = checkUserAuthentication(servicesContext, token); // Who is allowed here ? Admin and user himself if (currentUser.getRole() != CoselmarUserRole.ADMIN @@ -486,18 +546,18 @@ public class UsersWebService extends CoselmarWebServiceSupport { CoselmarUser example = BeanEntityConverter.fromBean(searchOption); - PaginationResult<CoselmarUser> userPaginationResult = getCoselmarUserDao().findAllByExample(example, searchOption.isActiveAndInactive(), requestBean); + PaginationResult<CoselmarUser> userPaginationResult = servicesContext.getPersistenceContext().getCoselmarUserDao().findAllByExample(example, searchOption.isActiveAndInactive(), requestBean); userList = userPaginationResult.getElements(); } else { - userList = getCoselmarUserDao().findAll(); + userList = servicesContext.getPersistenceContext().getCoselmarUserDao().findAll(); } List<UserBean> users = new ArrayList<>(userList.size()); for (CoselmarUser user : userList) { - String userLightId = getShortIdFromFull(user.getTopiaId()); + String userLightId = getShortIdFromFull(servicesContext.getPersistenceContext(), user.getTopiaId()); UserBean userBean = BeanEntityConverter.toBean(userLightId, user); users.add(userBean); } @@ -514,7 +574,9 @@ public class UsersWebService extends CoselmarWebServiceSupport { throw new CoselmarTechnicalException("Unable to export datas", e); } - return renderDownload(IOUtils.toInputStream(exportData), "export-users-result.csv", "text/csv"); + Response.ResponseBuilder responseBuilder = Response.ok(IOUtils.toInputStream(exportData)); + responseBuilder.header("Content-Disposition", "attachment; filename=export-users-result.csv"); + return responseBuilder.build(); } @@ -528,14 +590,14 @@ public class UsersWebService extends CoselmarWebServiceSupport { * @param userId : the current user#id : this parameter is needed to exclude from the search this user, cause it could already have this mail * @throws InvalidParameterException if the mail is already used. */ - protected void checkMailUniqueness(String mail, String userId) throws MailAlreadyExistingException { + protected void checkMailUniqueness(CoselmarServicesContext servicesContext, String mail, String userId) throws MailAlreadyExistingException { boolean mailAlreadyUsed; if (StringUtils.isNotBlank(userId)) { - mailAlreadyUsed = getCoselmarUserDao().forMailEquals(mail).addNotEquals(CoselmarUser.PROPERTY_TOPIA_ID, userId).exists(); + mailAlreadyUsed = servicesContext.getPersistenceContext().getCoselmarUserDao().forMailEquals(mail).addNotEquals(CoselmarUser.PROPERTY_TOPIA_ID, userId).exists(); } else { - mailAlreadyUsed = getCoselmarUserDao().forMailEquals(mail).exists(); + mailAlreadyUsed = servicesContext.getPersistenceContext().getCoselmarUserDao().forMailEquals(mail).exists(); } if (mailAlreadyUsed) { @@ -553,8 +615,8 @@ public class UsersWebService extends CoselmarWebServiceSupport { * @param password : given password we want to check * @throws InvalidCredentialException if the given password is not the same as one from database */ - protected void checkPassword(String currentPassword, String salt, String password) throws InvalidCredentialException { - String encodedPassword = getServicesContext().encodePassword(salt, password); + protected void checkPassword(CoselmarServicesContext servicesContext, String currentPassword, String salt, String password) throws InvalidCredentialException { + String encodedPassword = servicesContext.encodePassword(salt, password); if (!encodedPassword.equals(currentPassword)){ throw new InvalidCredentialException("Invalid password given"); @@ -565,9 +627,9 @@ public class UsersWebService extends CoselmarWebServiceSupport { /////////////// MAIL PART /////////////// ///////////////////////////////////////////// - protected void sendMail(AbstractMail mail) { + protected void sendMail(CoselmarServicesConfig servicesConfig, AbstractMail mail) { - if (getCoselmarServicesConfig().isDevMode()) { + if (servicesConfig.isDevMode()) { if (log.isInfoEnabled()) { log.info("an email should have been sent if not in devMode: to = " + @@ -581,22 +643,20 @@ public class UsersWebService extends CoselmarWebServiceSupport { } else { - CoselmarServicesConfig applicationConfig = getCoselmarServicesConfig(); - - mail.setCoselmarUrl(applicationConfig.getApplicationUrl()); + mail.setCoselmarUrl(servicesConfig.getApplicationUrl()); String body = getBody(mail); if (mail.isRecipientProvided()) { Email newEmail = new SimpleEmail(); - newEmail.setHostName(applicationConfig.getSmtpHost()); - newEmail.setSmtpPort(applicationConfig.getSmtpPort()); + newEmail.setHostName(servicesConfig.getSmtpHost()); + newEmail.setSmtpPort(servicesConfig.getSmtpPort()); newEmail.setCharset(Charsets.UTF_8.name()); newEmail.setSubject(mail.getSubject()); try { - newEmail.setFrom(applicationConfig.getSmtpFrom()); + newEmail.setFrom(servicesConfig.getSmtpFrom()); String to = mail.getTo(); newEmail.addTo(to); newEmail.setMsg(body); diff --git a/coselmar-rest/src/main/resources/mapping b/coselmar-rest/src/main/resources/mapping index 154cae2..186ac6f 100644 --- a/coselmar-rest/src/main/resources/mapping +++ b/coselmar-rest/src/main/resources/mapping @@ -14,12 +14,12 @@ default.render=fr.ifremer.coselmar.services.CoselmarRender [errors] -fr.ifremer.coselmar.services.errors.InvalidCredentialException ErrorAction.on401 -fr.ifremer.coselmar.services.errors.UnauthorizedException ErrorAction.on403 -fr.ifremer.coselmar.services.errors.NoResultException ErrorAction.on404 -fr.ifremer.coselmar.services.errors.MailAlreadyExistingException ErrorAction.on409 -fr.ifremer.coselmar.exceptions.CoselmarTechnicalException ErrorAction.on500 -org.nuiton.topia.persistence.TopiaNoResultException ErrorAction.on404 +#fr.ifremer.coselmar.services.errors.InvalidCredentialException ErrorAction.on401 +#fr.ifremer.coselmar.services.errors.UnauthorizedException ErrorAction.on403 +#fr.ifremer.coselmar.services.errors.NoResultException ErrorAction.on404 +#fr.ifremer.coselmar.services.errors.MailAlreadyExistingException ErrorAction.on409 +#fr.ifremer.coselmar.exceptions.CoselmarTechnicalException ErrorAction.on500 +#org.nuiton.topia.persistence.TopiaNoResultException ErrorAction.on404 [actions] @@ -31,60 +31,60 @@ GET /v1/doc DocApi.showMapping # Documents Api -GET /v1/documents DocumentsWebService.getDocuments -GET /v2/documents DocumentsWebService.getPaginatedDocuments -GET /v2/documents/citations DocumentsWebService.getSearchBibliography -GET /v1/documents/keywords DocumentsWebService.getKeywords -GET /v1/documents/types DocumentsWebService.getTypes -GET /v1/documents/{documentId} DocumentsWebService.getDocument -GET /v1/documents/{documentId}/file DocumentsWebService.getDocumentFile -POST /v1/documents DocumentsWebService.addDocument -POST /v1/documents/{documentId} DocumentsWebService.saveDocument -POST /v1/documents/{documentId}/file DocumentsWebService.addDocumentFile -DELETE /v1/documents/{documentId} DocumentsWebService.deleteDocument +#GET /v1/documents DocumentsWebService.getDocuments +#GET /v2/documents DocumentsWebService.getPaginatedDocuments +#GET /v2/documents/citations DocumentsWebService.getSearchBibliography +#GET /v1/documents/keywords DocumentsWebService.getKeywords +#GET /v1/documents/types DocumentsWebService.getTypes +#GET /v1/documents/{documentId} DocumentsWebService.getDocument +#GET /v1/documents/{documentId}/file DocumentsWebService.getDocumentFile +#POST /v1/documents DocumentsWebService.addDocument +#POST /v1/documents/{documentId} DocumentsWebService.saveDocument +#POST /v1/documents/{documentId}/file DocumentsWebService.addDocumentFile +#DELETE /v1/documents/{documentId} DocumentsWebService.deleteDocument # Users Api -GET /v1/users UsersWebService.getUsers -GET /v2/users UsersWebService.getPaginatedUsers -GET /v1/users/experts UsersWebService.getExperts -GET /v1/users/{userId} UsersWebService.getUser -POST /v1/users/login UsersWebService.login -POST /v1/users/password UsersWebService.generateNewPassword -POST /v1/users/{userId} UsersWebService.modifyUser -POST /v1/users UsersWebService.addUser -DELETE /v1/users/{userId} UsersWebService.deleteUser -GET /v1/users/{userId}/projects QuestionsWebService.getUserQuestions +#GET /v1/users UsersWebService.getUsers +#GET /v2/users UsersWebService.getPaginatedUsers +#GET /v1/users/experts UsersWebService.getExperts +#GET /v1/users/{userId} UsersWebService.getUser +#POST /v1/users/login UsersWebService.login +#POST /v1/users/password UsersWebService.generateNewPassword +#POST /v1/users/{userId} UsersWebService.modifyUser +#POST /v1/users UsersWebService.addUser +#DELETE /v1/users/{userId} UsersWebService.deleteUser +#GET /v1/users/{userId}/projects QuestionsWebService.getUserQuestions # Questions Api -GET /v1/questions QuestionsWebService.getQuestions -GET /v2/questions QuestionsWebService.getPaginatedQuestions -GET /v1/questions/public QuestionsWebService.getPublicQuestions -GET /v1/questions/themes QuestionsWebService.getThemes -GET /v1/questions/types QuestionsWebService.getTypes -GET /v1/questions/{questionId} QuestionsWebService.getQuestion -POST /v1/questions/{questionId} QuestionsWebService.saveQuestion -POST /v1/questions/{questionId}/documents QuestionsWebService.addDocuments -POST /v1/questions QuestionsWebService.addQuestion -DELETE /v1/questions/{questionId} QuestionsWebService.deleteQuestion -GET /v1/questions/{questionId}/ancestors QuestionsWebService.getAncestors depth=2 -GET /v1/questions/{questionId}/descendants QuestionsWebService.getDescendants depth=2 -GET /v1/questions/{questionId}/topwords QuestionsWebService.getTopWords +#GET /v1/questions QuestionsWebService.getQuestions +#GET /v2/questions QuestionsWebService.getPaginatedQuestions +#GET /v1/questions/public QuestionsWebService.getPublicQuestions +#GET /v1/questions/themes QuestionsWebService.getThemes +#GET /v1/questions/types QuestionsWebService.getTypes +#GET /v1/questions/{questionId} QuestionsWebService.getQuestion +#POST /v1/questions/{questionId} QuestionsWebService.saveQuestion +#POST /v1/questions/{questionId}/documents QuestionsWebService.addDocuments +#POST /v1/questions QuestionsWebService.addQuestion +#DELETE /v1/questions/{questionId} QuestionsWebService.deleteQuestion +#GET /v1/questions/{questionId}/ancestors QuestionsWebService.getAncestors depth=2 +#GET /v1/questions/{questionId}/descendants QuestionsWebService.getDescendants depth=2 +#GET /v1/questions/{questionId}/topwords QuestionsWebService.getTopWords # Transverse Api -GET /v1/general/topwords GeneralWebService.getTopWords +#GET /v1/general/topwords GeneralWebService.getTopWords # Admin API -POST /v1/admin/lucene/index AdminWebService.refreshLuceneIndex -POST /v1/admin/documents/zip DocumentsWebService.uploadZipDocuments +#POST /v1/admin/lucene/index AdminWebService.refreshLuceneIndex +#POST /v1/admin/documents/zip DocumentsWebService.uploadZipDocuments # Export POST /v1/export/questions QuestionsWebService.exportSearchedQuestions -POST /v1/export/users UsersWebService.exportSearchedUsers +#POST /v1/export/users UsersWebService.exportSearchedUsers # Health -GET /v1/health HealthService.getHealth +#GET /v1/health HealthService.getHealth diff --git a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/AbstractCoselmarWebServiceTest.java b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/AbstractCoselmarWebServiceTest.java index 30c383c..1c4b9d5 100644 --- a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/AbstractCoselmarWebServiceTest.java +++ b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/AbstractCoselmarWebServiceTest.java @@ -25,14 +25,14 @@ package fr.ifremer.coselmar.services; */ import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; +import io.undertow.Undertow; import org.apache.commons.logging.Log; -import org.debux.webmotion.unittest.WebMotionTest; +import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.nuiton.util.DateUtil; -import java.io.File; import java.io.IOException; import java.util.Locale; @@ -41,17 +41,14 @@ import static org.apache.commons.logging.LogFactory.getLog; /** * @author ymartel <martel@codelutin.com> */ -public class AbstractCoselmarWebServiceTest extends WebMotionTest { +public class AbstractCoselmarWebServiceTest { private static final Log log = getLog(AbstractCoselmarWebServiceTest.class); @Rule public final FakeCoselmarApplicationContext application = new FakeCoselmarApplicationContext("coselmar-test.properties"); - @Override - protected int getPort() { - return application.getPort(); - } + private static UndertowJaxrsServer server; @Before public void startServer() throws Exception { @@ -78,17 +75,11 @@ public class AbstractCoselmarWebServiceTest extends WebMotionTest { applicationContext.init(); - CoselmarServicesApplicationContext.setApplicationContext(applicationContext); - - super.startServer(); - - } - - @Override - protected String getServerBaseDirectory() { + CoselmarServicesApplicationContext.setInstance(applicationContext); - return new File(application.getTestBasedir(), - "tomcat_" + application.getPort()).getAbsolutePath(); + server = new UndertowJaxrsServer().start(Undertow.builder().addHttpListener(application.getPort(), "localhost")); + server.start(); + server.deploy(CoselmarApplication.class); } @@ -101,7 +92,6 @@ public class AbstractCoselmarWebServiceTest extends WebMotionTest { application.close(); server.stop(); - server.destroy(); } diff --git a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/QuestionsWebServiceTest.java b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/QuestionsWebServiceTest.java index 87492b1..fdbb835 100644 --- a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/QuestionsWebServiceTest.java +++ b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/QuestionsWebServiceTest.java @@ -25,17 +25,34 @@ package fr.ifremer.coselmar.services; */ import com.auth0.jwt.JWTVerifier; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Sets; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import fr.ifremer.coselmar.beans.QuestionBean; import fr.ifremer.coselmar.beans.QuestionUserRole; +import fr.ifremer.coselmar.beans.UserBean; +import fr.ifremer.coselmar.converter.JacksonMapperUtil; import fr.ifremer.coselmar.converter.JsonHelper; -import org.apache.http.HttpResponse; -import org.apache.http.client.fluent.Request; -import org.apache.http.client.fluent.Response; +import fr.ifremer.coselmar.persistence.entity.Privacy; +import fr.ifremer.coselmar.providers.QuestionBeanParamConverter; +import org.jboss.resteasy.test.TestPortProvider; import org.junit.Assert; import org.junit.Test; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import java.awt.print.Book; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -63,13 +80,15 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { public void testSearchQuestions() throws Exception { //First : login ! - Request loginRequest = createRequest("/v1/users/login") - .addParameter("mail", "default.supervisor@temporary.coselmar") - .addParameter("password", "manager1234") - .Post(); + Client client = ClientBuilder.newClient(); + WebTarget loginTarget = client.target(TestPortProvider.generateURL("/v1/users/login")); - Response loginResponse = loginRequest.execute(); - String loginContent = loginResponse.returnContent().asString(); + Form loginForm = new Form() + .param("mail", "default.supervisor@temporary.coselmar") + .param("password", "manager1234"); + + Response loginResponse = loginTarget.request().post(Entity.form(loginForm)); + String loginContent = loginResponse.readEntity(String.class); Assert.assertNotNull(loginContent); showTestResult(loginContent); @@ -80,74 +99,79 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { // Create first document String documentOneTitle = "Ceci est le titre"; - Request addQuestionsRequest = createRequest("/v1/questions") - .addParameter("question", - "{title: '" + documentOneTitle + "', summary: 'ceci est le resume'," - + " submissionDate: '" + (new Date()).getTime() + "'," - + " themes: ['test', 'questionOne'], type: 'question'," - + " privacy : 'PUBLIC' }") - .Post() - .addHeader("Authorization", "Bearer " + supervisorToken); - - HttpResponse addQuestionResponse = addQuestionsRequest.execute().returnResponse(); - Assert.assertEquals(200, addQuestionResponse.getStatusLine().getStatusCode()); + + Form addQuestionForm = new Form() + .param("question", "{ \"title\": \"" + documentOneTitle + "\", \"summary\": \"ceci est le resume\"," + + " \"submissionDate\" : \"" + (new Date()).getTime() + "\"," + + " \"themes\": [\"test\", \"questionOne\"]," + + " \"type\": \"question\"," + + " \"privacy\": \"PUBLIC\" }"); + + WebTarget questionsTarget = client.target(TestPortProvider.generateURL("/v1/questions")); + + Response addQuestionResponse = questionsTarget.request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .post(Entity.form(addQuestionForm)); + + Assert.assertEquals(204, addQuestionResponse.getStatus()); // Create second document String documentTwoTitle = "There s someone missing. The question s Who?"; - addQuestionsRequest = createRequest("/v1/questions") - .addParameter("question", - "{title: '" + documentTwoTitle + "'," - + " summary: 'Something old, Something new, Something borrowed, Something blue.'," - + " submissionDate: '" + (new Date()).getTime() + "'," - + " themes: ['big bang two', 'Pandorica', 'River', 'Universe'], type: 'question'," - + " privacy : 'PUBLIC' }") - .Post() - .addHeader("Authorization", "Bearer " + supervisorToken); - - addQuestionResponse = addQuestionsRequest.execute().returnResponse(); - Assert.assertEquals(200, addQuestionResponse.getStatusLine().getStatusCode()); + addQuestionForm = new Form() + .param("question", + "{ \"title\": \"" + documentTwoTitle + "\"," + + " \"summary\": \"Something old, Something new, Something borrowed, Something blue.\"," + + " \"submissionDate\": \"" + (new Date()).getTime() + "\"," + + " \"themes\": [\"big bang two\", \"Pandorica\", \"River\", \"Universe\"]," + + " \"type\": \"question\"," + + " \"privacy\": \"PUBLIC\" }"); + + addQuestionResponse = questionsTarget.request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .post(Entity.form(addQuestionForm)); + Assert.assertEquals(204, addQuestionResponse.getStatus()); // And a third document ! String documentThreeTitle = "Private things"; - addQuestionsRequest = createRequest("/v1/questions") - .addParameter("question", - "{title: '" + documentThreeTitle + "'," - + " summary: 'This question is private, zero access for others'," - + " submissionDate: '" + (new Date()).getTime() + "'," - + " themes: ['Universe', 'test', 'zero'], type: 'question'," - + " privacy : 'PRIVATE' }") - .Post() - .addHeader("Authorization", "Bearer " + supervisorToken); - - addQuestionResponse = addQuestionsRequest.execute().returnResponse(); - Assert.assertEquals(200, addQuestionResponse.getStatusLine().getStatusCode()); + addQuestionForm = new Form() + .param("question", + "{\"title\": \"" + documentThreeTitle + "\"," + + " \"summary\": \"This question is private, zero access for others\"," + + " \"submissionDate\": \"" + (new Date()).getTime() + "\"," + + " \"themes\": [\"Universe\", \"test\", \"zero\"]," + + " \"type\": \"question\"," + + " \"privacy \": \"PRIVATE\" }"); + + addQuestionResponse = questionsTarget.request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .post(Entity.form(addQuestionForm)); + Assert.assertEquals(204, addQuestionResponse.getStatus()); // First search : as supervisor, no search bean : retrieve the three documents - Request searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{}") - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); + Response searchQuestionsResponse = questionsTarget.request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); + + ObjectMapper objectMapper = JacksonMapperUtil.getMapper(); - Response searchResponse = searchRequest.execute(); - String searchResultContent = searchResponse.returnContent().asString(); - JsonHelper jsonHelper = new JsonHelper(true); - List<QuestionBean> result = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + String searchResultContent = searchQuestionsResponse.readEntity(String.class); + List<QuestionBean> result = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(3, result.size()); Assert.assertNotNull(result.get(0)); + Assert.assertEquals(documentOneTitle, result.get(0).getTitle()); Assert.assertNotNull(result.get(1)); Assert.assertNotNull(result.get(2)); // Second search : as supervisor, searchBean only with public privacy : retrieve two documents (one and two) - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{privacy : 'PUBLIC'}") - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{\"privacy\":\"PUBLIC\"}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - List<QuestionBean> publicPrivacyResult = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + List<QuestionBean> publicPrivacyResult = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(2, publicPrivacyResult.size()); List<String> expectedTitles = Arrays.asList(documentOneTitle, documentTwoTitle); @@ -156,41 +180,38 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { // Third search : as supervisor, searchBean with public privacy and test keywords : retrieve documentOne - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{privacy : 'public', fullTextSearch : ['test']}") - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{\"privacy\":\"public\",\"fullTextSearch\":[\"test\"]}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - List<QuestionBean> publicTestResult = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + List<QuestionBean> publicTestResult = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(1, publicTestResult.size()); Assert.assertEquals(documentOneTitle, publicTestResult.get(0).getTitle()); // Fourth search : as supervisor, searchBean with test and pandorica keywords : retrieve nothing - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{privacy : 'public', fullTextSearch : ['test', 'pandorica']}") - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{\"privacy\":\"public\",\"fullTextSearch\":[\"test\",\"pandorica\"]}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - List<QuestionBean> shouldNoResult = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + List<QuestionBean> shouldNoResult = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertTrue(shouldNoResult.isEmpty()); // Fifth search : as supervisor, searchBean with test keyword : retrieve documentOne and documentThree - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{fullTextSearch : ['test']}") - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{\"fullTextSearch\":[\"test\"]}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - List<QuestionBean> testKeywordResult = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + List<QuestionBean> testKeywordResult = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(2, testKeywordResult.size()); expectedTitles = Arrays.asList(documentOneTitle, documentThreeTitle); @@ -199,14 +220,13 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { // Sixth search : as supervisor, searchBean with test and universe keywords : retrieve documentThree - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{fullTextSearch : ['test', 'Universe']}") - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{\"fullTextSearch\":[\"test\",\"Universe\"]}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - List<QuestionBean> testUniverseKeywordsResult = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + List<QuestionBean> testUniverseKeywordsResult = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(1, testUniverseKeywordsResult.size()); Assert.assertEquals(documentThreeTitle, testUniverseKeywordsResult.get(0).getTitle()); @@ -214,13 +234,12 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { //Have login for an expert ! - loginRequest = createRequest("/v1/users/login") - .addParameter("mail", "lambda.expert@temporary.coselmar") - .addParameter("password", "manager1234") - .Post(); + loginForm = new Form() + .param("mail", "lambda.expert@temporary.coselmar") + .param("password", "manager1234"); - loginResponse = loginRequest.execute(); - loginContent = loginResponse.returnContent().asString(); + loginResponse = loginTarget.request().post(Entity.form(loginForm)); + loginContent = loginResponse.readEntity(String.class); Assert.assertNotNull(loginContent); showTestResult(loginContent); @@ -229,14 +248,13 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { String expertToken = loginMap.get("jwt"); // Seventh search : as expert, no search bean : retrieve the two public documents - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{}") - .Get() - .addHeader("Authorization", "Bearer " + expertToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + expertToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - result = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + result = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(2, result.size()); expectedTitles = Arrays.asList(documentOneTitle, documentTwoTitle); @@ -244,14 +262,13 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { // Eighth search : as expert, searchBean only with public privacy : retrieve two documents (one and two) (same as seventh) - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{privacy : 'public'}") - .Get() - .addHeader("Authorization", "Bearer " + expertToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{\"privacy\":\"public\"}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + expertToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - publicPrivacyResult = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + publicPrivacyResult = publicTestResult = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(2, publicPrivacyResult.size()); expectedTitles = Arrays.asList(documentOneTitle, documentTwoTitle); @@ -260,43 +277,42 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { // Ninth search : as expert, searchBean with public privacy and test keywords : retrieve documentOne - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{privacy : 'public', fullTextSearch : ['test']}") - .Get() - .addHeader("Authorization", "Bearer " + expertToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{\"privacy\":\"public\",\"fullTextSearch\":[\"test\"]}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + expertToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - publicTestResult = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + publicTestResult = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(1, publicTestResult.size()); Assert.assertEquals(documentOneTitle, publicTestResult.get(0).getTitle()); // Tenth search : as supervisor, searchBean with test and pandorica keywords : retrieve nothing - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{privacy : 'public', fullTextSearch : ['test', 'pandorica']}") - .Get() - .addHeader("Authorization", "Bearer " + expertToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{\"privacy\":\"public\",\"fullTextSearch\":[\"test\",\"pandorica\"]}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + expertToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - shouldNoResult = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + shouldNoResult = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertTrue(shouldNoResult.isEmpty()); // Eleventh search : as expert, searchBean with pandorica keyword : retrieve nothing (documentThree is private) - searchRequest = createRequest("/v1/questions") - .addParameter("searchOption", "{fullTextSearch : ['zero']}") - .Get() - .addHeader("Authorization", "Bearer " + expertToken); + searchQuestionsResponse = questionsTarget.queryParam("searchOption", URLEncoder.encode("{\"fullTextSearch\":[\"zero\"]}", StandardCharsets.UTF_8.name())) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + expertToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - shouldNoResult = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + shouldNoResult = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertTrue(shouldNoResult.isEmpty()); + + client.close(); } @Test @@ -305,14 +321,17 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { String webSecurityKey = getServiceContext().getCoselmarServicesConfig().getWebSecurityKey(); JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); + Client client = ClientBuilder.newClient(); + //First : login ! - Request loginRequest = createRequest("/v1/users/login") - .addParameter("mail", "lambda.expert@temporary.coselmar") - .addParameter("password", "manager1234") - .Post(); + WebTarget loginTarget = client.target(TestPortProvider.generateURL("/v1/users/login")); + + Form loginForm = new Form() + .param("mail", "lambda.expert@temporary.coselmar") + .param("password", "manager1234"); - Response loginResponse = loginRequest.execute(); - String loginContent = loginResponse.returnContent().asString(); + Response loginResponse = loginTarget.request().post(Entity.form(loginForm)); + String loginContent = loginResponse.readEntity(String.class); Assert.assertNotNull(loginContent); showTestResult(loginContent); @@ -324,13 +343,12 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { String expertId = (String) expertMap.get("userId"); //First : login ! - loginRequest = createRequest("/v1/users/login") - .addParameter("mail", "default.supervisor@temporary.coselmar") - .addParameter("password", "manager1234") - .Post(); + loginForm = new Form() + .param("mail", "default.supervisor@temporary.coselmar") + .param("password", "manager1234"); - loginResponse = loginRequest.execute(); - loginContent = loginResponse.returnContent().asString(); + loginResponse = loginTarget.request().post(Entity.form(loginForm)); + loginContent = loginResponse.readEntity(String.class); Assert.assertNotNull(loginContent); showTestResult(loginContent); @@ -343,63 +361,68 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { // Create first document String documentOneTitle = "Ceci est le titre"; - Request addQuestionsRequest = createRequest("/v1/questions") - .addParameter("question", - "{title: '" + documentOneTitle + "', summary: 'ceci est le resume'," - + " submissionDate: '" + (new Date()).getTime() + "'," - + " themes: ['test', 'questionOne'], type: 'question'," - + " participants: [{'id': '" + expertId + "'}]," - + " supervisors: [{'id': '" + supervisorId + "'}]," - + " privacy : 'PUBLIC' }") - .Post() - .addHeader("Authorization", "Bearer " + supervisorToken); - - HttpResponse addQuestionResponse = addQuestionsRequest.execute().returnResponse(); - Assert.assertEquals(200, addQuestionResponse.getStatusLine().getStatusCode()); + Form addQuestionForm = new Form() + .param("question", "{ \"title\": \"" + documentOneTitle + "\", \"summary\": \"ceci est le resume\"," + + " \"submissionDate\" : \"" + (new Date()).getTime() + "\"," + + " \"themes\": [\"test\", \"questionOne\"]," + + " \"type\": \"question\"," + + " \"participants\": [{\"id\":\"" + expertId + "\"}]," + + " \"supervisors\": [{\"id\": \"" + supervisorId + "\"}]," + + " \"privacy\": \"PUBLIC\" }"); + + WebTarget questionsTarget = client.target(TestPortProvider.generateURL("/v1/questions")); + + Response addQuestionResponse = questionsTarget.request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .post(Entity.form(addQuestionForm)); + + Assert.assertEquals(204, addQuestionResponse.getStatus()); // Create second document String documentTwoTitle = "There s someone missing. The question s Who?"; - addQuestionsRequest = createRequest("/v1/questions") - .addParameter("question", - "{title: '" + documentTwoTitle + "'," - + " summary: 'Something old, Something new, Something borrowed, Something blue.'," - + " submissionDate: '" + (new Date()).getTime() + "'," - + " themes: ['big bang two', 'Pandorica', 'River', 'Universe'], type: 'question'," - + " participants: [{'id': '" + expertId + "'}, {'id': '" + supervisorId + "'}]," - + " privacy : 'PUBLIC' }") - .Post() - .addHeader("Authorization", "Bearer " + supervisorToken); - - addQuestionResponse = addQuestionsRequest.execute().returnResponse(); - Assert.assertEquals(200, addQuestionResponse.getStatusLine().getStatusCode()); + addQuestionForm = new Form() + .param("question", + "{ \"title\": \"" + documentTwoTitle + "\"," + + " \"summary\": \"Something old, Something new, Something borrowed, Something blue.\"," + + " \"submissionDate\": \"" + (new Date()).getTime() + "\"," + + " \"themes\": [\"big bang two\", \"Pandorica\", \"River\", \"Universe\"]," + + " \"type\": \"question\"," + + " \"participants\": [{\"id\":\"" + expertId + "\"}, {\"id\": \"" + supervisorId + "\"}]," + + " \"privacy\": \"PUBLIC\" }"); + + addQuestionResponse = questionsTarget.request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .post(Entity.form(addQuestionForm)); + Assert.assertEquals(204, addQuestionResponse.getStatus()); // And a third document ! String documentThreeTitle = "Private things"; - addQuestionsRequest = createRequest("/v1/questions") - .addParameter("question", - "{title: '" + documentThreeTitle + "'," - + " summary: 'This question is private, zero access for others'," - + " submissionDate: '" + (new Date()).getTime() + "'," - + " themes: ['Universe', 'test', 'zero'], type: 'question'," - + " participants: [{'id': '" + supervisorId + "'}]," - + " clients: [{'id': '" + expertId + "'}]," - + " privacy : 'PRIVATE' }") - .Post() - .addHeader("Authorization", "Bearer " + supervisorToken); - - addQuestionResponse = addQuestionsRequest.execute().returnResponse(); - Assert.assertEquals(200, addQuestionResponse.getStatusLine().getStatusCode()); + addQuestionForm = new Form() + .param("question", + "{\"title\": \"" + documentThreeTitle + "\"," + + " \"summary\": \"This question is private, zero access for others\"," + + " \"submissionDate\": \"" + (new Date()).getTime() + "\"," + + " \"themes\": [\"Universe\", \"test\", \"zero\"]," + + " \"type\": \"question\"," + + " \"participants\": [{\"id\": \"" + supervisorId + "\"}]," + + " \"clients\": [{\"id\": \"" + expertId + "\"}]," + + " \"privacy \": \"PRIVATE\" }"); + + addQuestionResponse = questionsTarget.request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .post(Entity.form(addQuestionForm)); + Assert.assertEquals(204, addQuestionResponse.getStatus()); // First search : projects where expert is participant : one and two - Request searchRequest = createRequest("/v1/users/" + expertId + "/projects") - .addParameter("userRole", QuestionUserRole.PARTICIPANT.name()) - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); + WebTarget expertQuestionsTarget = client.target(TestPortProvider.generateURL("/v1/users/" + expertId + "/projects")); + Response searchQuestionsResponse = expertQuestionsTarget.queryParam("userRole", QuestionUserRole.PARTICIPANT.name()) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); - Response searchResponse = searchRequest.execute(); - String searchResultContent = searchResponse.returnContent().asString(); - JsonHelper jsonHelper = new JsonHelper(true); - List<QuestionBean> result = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + String searchResultContent = searchQuestionsResponse.readEntity(String.class); + ObjectMapper objectMapper = JacksonMapperUtil.getMapper(); + List<QuestionBean> result = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(2, result.size()); Assert.assertNotNull(result.get(0)); @@ -410,14 +433,14 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { // Second search : project where supervisor is participant : two and three - searchRequest = createRequest("/v1/users/" + supervisorId + "/projects") - .addParameter("userRole", QuestionUserRole.PARTICIPANT.name()) - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); + WebTarget supervisorQuestionsTarget = client.target(TestPortProvider.generateURL("/v1/users/" + supervisorId + "/projects")); + searchQuestionsResponse = supervisorQuestionsTarget.queryParam("userRole", QuestionUserRole.PARTICIPANT.name()) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - result = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + result = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(2, result.size()); expectedTitles = Arrays.asList(documentThreeTitle, documentTwoTitle); @@ -426,28 +449,26 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { // Second search : project where expert is client : three - searchRequest = createRequest("/v1/users/" + expertId + "/projects") - .addParameter("userRole", QuestionUserRole.CLIENT.name()) - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); + searchQuestionsResponse = expertQuestionsTarget.queryParam("userRole", QuestionUserRole.CLIENT.name()) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - result = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchResultContent = searchQuestionsResponse.readEntity(String.class); + result = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(1, result.size()); Assert.assertEquals(documentThreeTitle, result.get(0).getTitle()); // Second search : project where supervisor is supervisor : all (cause he creates them) - searchRequest = createRequest("/v1/users/" + supervisorId + "/projects") - .addParameter("userRole", QuestionUserRole.SUPERVISOR.name()) - .Get() - .addHeader("Authorization", "Bearer " + supervisorToken); - - searchResponse = searchRequest.execute(); - searchResultContent = searchResponse.returnContent().asString(); - result = jsonHelper.fromJson(searchResultContent, new TypeToken<ArrayList<QuestionBean>>(){}.getType()); + searchQuestionsResponse = supervisorQuestionsTarget.queryParam("userRole", QuestionUserRole.SUPERVISOR.name()) + .request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + supervisorToken) + .get(); + + searchResultContent = searchQuestionsResponse.readEntity(String.class); + result = objectMapper.readValue(searchResultContent, objectMapper.getTypeFactory().constructCollectionType(List.class, QuestionBean.class)); Assert.assertEquals(3, result.size()); expectedTitles = Arrays.asList(documentOneTitle, documentTwoTitle, documentThreeTitle); diff --git a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/UsersWebServiceTest.java b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/UsersWebServiceTest.java index 39a84e4..8c6e71d 100644 --- a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/UsersWebServiceTest.java +++ b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/UsersWebServiceTest.java @@ -26,13 +26,18 @@ package fr.ifremer.coselmar.services; import com.auth0.jwt.JWTVerifier; import com.google.gson.Gson; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.fluent.Request; -import org.apache.http.client.fluent.Response; +import fr.ifremer.coselmar.beans.UserBean; +import org.apache.http.HttpHeaders; +import org.jboss.resteasy.test.TestPortProvider; import org.junit.Assert; import org.junit.Test; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.Response; import java.util.Locale; import java.util.Map; @@ -55,13 +60,16 @@ public class UsersWebServiceTest extends AbstractCoselmarWebServiceTest { @Test public void testAuthentication() throws Exception { - Request loginRequest = createRequest("/v1/users/login") - .addParameter("mail", "admin@temporary.coselmar") - .addParameter("password", "manager1234") - .Post(); + Client client = ClientBuilder.newClient(); + WebTarget target = client.target(TestPortProvider.generateURL("/v1/users/login")); - Response loginResponse = loginRequest.execute(); - String loginContent = loginResponse.returnContent().asString(); + Form form = new Form() + .param("mail", "admin@temporary.coselmar") + .param("password", "manager1234"); + + Response loginResponse = target.request().post(Entity.form(form)); + Assert.assertEquals(Response.Status.OK.getStatusCode(), loginResponse.getStatus()); + String loginContent = loginResponse.readEntity(String.class); Assert.assertNotNull(loginContent); showTestResult(loginContent); @@ -72,18 +80,21 @@ public class UsersWebServiceTest extends AbstractCoselmarWebServiceTest { JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); String token = map.get("jwt"); jwtVerifier.verify(token); + + client.close(); } @Test public void testNewUserAuthentication() throws Exception { - Request loginRequest = createRequest("/v1/users/login") - .addParameter("mail", "admin@temporary.coselmar") - .addParameter("password", "manager1234") - .Post(); + Client client = ClientBuilder.newClient(); + WebTarget loginTarget = client.target(TestPortProvider.generateURL("/v1/users/login")); + Form loginForm = new Form() + .param("mail", "admin@temporary.coselmar") + .param("password", "manager1234"); - Response loginResponse = loginRequest.execute(); - String loginContent = loginResponse.returnContent().asString(); + Response loginResponse = loginTarget.request().post(Entity.form(loginForm)); + String loginContent = loginResponse.readEntity(String.class); Assert.assertNotNull(loginContent); showTestResult(loginContent); @@ -92,22 +103,31 @@ public class UsersWebServiceTest extends AbstractCoselmarWebServiceTest { String adminToken = loginMap.get("jwt"); + UserBean userRegistration = new UserBean(null, "test", "her", "test@test.org", null, "supervisor", "unit tester", null, true); + userRegistration.setPassword("iamatester"); + + WebTarget newUserTarget = client.target(TestPortProvider.generateURL("/v1/users")); + Form addUserForm = new Form() + .param("user", "{ \"firstName\": \"test\"," + + "\"name\": \"her\"," + + "\"mail\": \"test@test.org\"," + + "\"role\": \"supervisor\"," + + "\"qualification\": \"unit tester\"," + + "\"password\": \"iamatester\"}"); - Request addUserRequest = createRequest("/v1/users") - .addParameter("user", "{firstName: 'test', name: 'her', mail: 'test@test.org', role: 'supervisor', qualification: 'unit tester', password : 'iamatester'}") - .Post() - .addHeader("Authorization", "Bearer " + adminToken); + Response newUSerResponse = newUserTarget.request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken) + .post(Entity.form(addUserForm)); - HttpResponse addUserResponse = addUserRequest.execute().returnResponse(); - Assert.assertEquals(200, addUserResponse.getStatusLine().getStatusCode()); + Assert.assertEquals(204, newUSerResponse.getStatus()); - loginRequest = createRequest("/v1/users/login") - .addParameter("mail", "test@test.org") - .addParameter("password", "iamatester") - .Post(); + loginTarget = client.target(TestPortProvider.generateURL("/v1/users/login")); + loginForm = new Form() + .param("mail", "test@test.org") + .param("password", "iamatester"); - loginResponse = loginRequest.execute(); - loginContent = loginResponse.returnContent().asString(); + loginResponse = loginTarget.request().post(Entity.form(loginForm)); + loginContent = loginResponse.readEntity(String.class); Assert.assertNotNull(loginContent); showTestResult(loginContent); @@ -117,27 +137,31 @@ public class UsersWebServiceTest extends AbstractCoselmarWebServiceTest { JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); String token = newUserLoginMap.get("jwt"); jwtVerifier.verify(token); + + client.close(); } @Test public void testGenerateNewPassword() throws Exception { - Request newPasswordRequest = createRequest("/v1/users/password") - .addParameter("userMail", "lambda.expert@temporary.coselmar") - .Post(); + Client client = ClientBuilder.newClient(); + WebTarget newPasswordTarget = client.target(TestPortProvider.generateURL("/v1/users/password")); + Form newPasswordForm = new Form() + .param("userMail", "lambda.expert@temporary.coselmar"); - Response newPasswordResponse = newPasswordRequest.execute(); - Assert.assertEquals(200, newPasswordResponse.returnResponse().getStatusLine().getStatusCode()); + Response newPasswordResponse = newPasswordTarget.request().post(Entity.form(newPasswordForm)); + Assert.assertEquals(204, newPasswordResponse.getStatus()); // Try log now - Request loginRequest = createRequest("/v1/users/login") - .addParameter("mail", "lambda.expert@temporary.coselmar") - .addParameter("password", "manager1234") - .Post(); - - Response loginResponse = loginRequest.execute(); - StatusLine loginStatusLine = loginResponse.returnResponse().getStatusLine(); - Assert.assertEquals(401, loginStatusLine.getStatusCode()); + WebTarget loginTarget = client.target(TestPortProvider.generateURL("/v1/users/login")); + Form loginForm = new Form() + .param("mail", "lambda.expert@temporary.coselmar") + .param("password", "manager1234"); + + Response loginResponse = loginTarget.request().post(Entity.form(loginForm)); + Assert.assertEquals(401, loginResponse.getStatus()); + + client.close(); } } diff --git a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/v1/DocumentsWebServiceTest.java b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/v1/DocumentsWebServiceTest.java index c3f8de8..f48536e 100644 --- a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/v1/DocumentsWebServiceTest.java +++ b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/v1/DocumentsWebServiceTest.java @@ -78,7 +78,7 @@ public class DocumentsWebServiceTest extends AbstractCoselmarServiceTest { coselmarUser.setFirstname("Jean"); getServiceContext().getPersistenceContext().commit(); - MassiveDocumentsImportResult importResult = documentsWebService.importFromZip(zipFile, coselmarUser); + MassiveDocumentsImportResult importResult = documentsWebService.importFromZip(getServiceContext(), zipFile, coselmarUser); Assert.assertNotNull(importResult); Assert.assertTrue(importResult.getMissingFiles().isEmpty()); DocumentTopiaDao documentDao = getServiceContext().getPersistenceContext().getDocumentDao(); @@ -108,7 +108,7 @@ public class DocumentsWebServiceTest extends AbstractCoselmarServiceTest { coselmarUser.setFirstname("Jean"); getServiceContext().getPersistenceContext().commit(); - MassiveDocumentsImportResult importResult = documentsWebService.importFromZip(zipFile, coselmarUser); + MassiveDocumentsImportResult importResult = documentsWebService.importFromZip(getServiceContext(), zipFile, coselmarUser); Assert.assertNotNull(importResult); Assert.assertFalse(importResult.getMissingFiles().isEmpty()); Assert.assertEquals(1, importResult.getMissingFiles().size()); diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 3f6536c..3eb64ff 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -2188,6 +2188,7 @@ coselmarControllers.controller("AdminCtrl", ['$scope', '$routeParams', '$locatio } else { $scope.status.massimport = "fail"; $scope.status.importResult = importResult; + console.log(importResult); } },function(error) { diff --git a/pom.xml b/pom.xml index 346b73b..b8488aa 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ <platform>codelutin.com</platform> <!-- Java version --> - <javaVersion>1.7</javaVersion> + <javaVersion>1.8</javaVersion> <!--TODO remove this when idea won't ask to change jdk level at each pom modification--> <maven.compiler.source>${javaVersion}</maven.compiler.source> <!--TODO remove this when idea won't ask to change jdk level at each pom modification--> @@ -115,9 +115,7 @@ <!-- Site configuration --> <locales>fr</locales> - <!-- customized versions --> - <!--<webmotionVersion>2.4.1-20140826-pollen2</webmotionVersion>--> - <webmotionVersion>2.5.3</webmotionVersion> + <resteasy.version>3.6.3.Final</resteasy.version> <nuitonI18nVersion>3.3</nuitonI18nVersion> <eugenePluginVersion>2.13</eugenePluginVersion> @@ -393,17 +391,75 @@ </dependency> <!-- web exposition --> + + <!-- REASTEasy, for webservices --> <dependency> - <groupId>org.debux.webmotion</groupId> - <artifactId>webmotion</artifactId> - <version>${webmotionVersion}</version> - <exclusions> - <exclusion> - <groupId>javassist</groupId> - <artifactId>javassist</artifactId> - </exclusion> - </exclusions> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-jaxrs</artifactId> + <version>${resteasy.version}</version> + </dependency> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-jaxb-provider</artifactId> + <version>${resteasy.version}</version> + <scope>runtime</scope> + </dependency> + <!-- Multipart support --> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-multipart-provider</artifactId> + <version>${resteasy.version}</version> </dependency> + <!-- to run test on API --> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-undertow</artifactId> + <version>${resteasy.version}</version> + <scope>test</scope> + </dependency> + + <!-- transitive dependency from resteasy --> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpcore</artifactId> + <version>4.4</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>javax.ws.rs-api</artifactId> + <version>2.1</version> + </dependency> + <!--<dependency>--> + <!--<groupId>org.jboss.spec.javax.ws.rs</groupId>--> + <!--<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>--> + <!--<version>1.0.0.Final</version>--> + <!--</dependency>--> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>2.8.9</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + <version>2.8.9</version> + </dependency> + + <!-- needed for Servlet 3 --> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-servlet-initializer</artifactId> + <version>${resteasy.version}</version> + <scope>runtime</scope> + </dependency> + <!-- needed for JSON response --> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-jackson2-provider</artifactId> + <version>${resteasy.version}</version> + </dependency> + <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> @@ -556,12 +612,6 @@ <version>${topiaVersion}</version> <scope>test</scope> </dependency> - <dependency> - <groupId>org.debux.webmotion</groupId> - <artifactId>webmotion-unittest</artifactId> - <version>${webmotionVersion}</version> - <scope>test</scope> - </dependency> <!-- for embedded test --> <dependency> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch refonte-rest in repository coselmar. See https://gitlab.nuiton.org/codelutin/coselmar.git commit 64e85dc349db28bbe792a3c086401dcf1391d3a7 Author: Yannick Martel <martel@©odelutin.com> Date: Tue Jun 4 16:23:16 2019 +0200 update dependencies list --- coselmar-rest/pom.xml | 32 ++++++++++++++++++-------------- pom.xml | 38 +++++++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/coselmar-rest/pom.xml b/coselmar-rest/pom.xml index e4e3ba4..ed16e91 100644 --- a/coselmar-rest/pom.xml +++ b/coselmar-rest/pom.xml @@ -136,17 +136,17 @@ <artifactId>resteasy-undertow</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>io.undertow</groupId> + <artifactId>undertow-core</artifactId> + <scope>test</scope> + </dependency> <!-- transitive dependency from resteasy --> - <dependency> - <groupId>javax.ws.rs</groupId> - <artifactId>javax.ws.rs-api</artifactId> - </dependency> - <!--<dependency>--> - <!--<groupId>org.jboss.spec.javax.ws.rs</groupId>--> - <!--<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>--> - <!--<version>1.0.0.Final</version>--> - <!--</dependency>--> + <dependency> + <groupId>org.jboss.spec.javax.ws.rs</groupId> + <artifactId>jboss-jaxrs-api_2.1_spec</artifactId> + </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> @@ -155,6 +155,15 @@ <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + </dependency> + <dependency> + <groupId>org.jboss.spec.javax.servlet</groupId> + <artifactId>jboss-servlet-api_4.0_spec</artifactId> + <scope>test</scope> + </dependency> <!-- needed for Servlet 3 --> <dependency> @@ -238,11 +247,6 @@ <groupId>org.nuiton.topia</groupId> <artifactId>topia-junit</artifactId> </dependency> - <dependency> - <groupId>org.apache.httpcomponents</groupId> - <artifactId>httpcore</artifactId> - <scope>test</scope> - </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>fluent-hc</artifactId> diff --git a/pom.xml b/pom.xml index b8488aa..e3f0315 100644 --- a/pom.xml +++ b/pom.xml @@ -161,10 +161,11 @@ <commons-beanutils.version>1.9.2</commons-beanutils.version> <!-- XXX ymartel 20151202 : cannot upgrade httpcomponents deps for the moment, still needed for webmotion test --> <fluent-hc.version>4.2.3</fluent-hc.version> - <httpcore.version>4.2.3</httpcore.version> + <httpcore.version>4.4</httpcore.version> <jqcloud2Version>2.0.1</jqcloud2Version> <angularJqcloudVersion>1.0.2</angularJqcloudVersion> <d3JsVersion>3.5.12</d3JsVersion> + <jackson.version>2.8.9</jackson.version> </properties> <repositories> @@ -417,34 +418,45 @@ <version>${resteasy.version}</version> <scope>test</scope> </dependency> - - <!-- transitive dependency from resteasy --> <dependency> - <groupId>org.apache.httpcomponents</groupId> - <artifactId>httpcore</artifactId> - <version>4.4</version> + <groupId>io.undertow</groupId> + <artifactId>undertow-core</artifactId> + <version>2.0.16.Final</version> <scope>test</scope> </dependency> + + <!-- transitive dependency from resteasy --> <dependency> <groupId>javax.ws.rs</groupId> <artifactId>javax.ws.rs-api</artifactId> <version>2.1</version> </dependency> - <!--<dependency>--> - <!--<groupId>org.jboss.spec.javax.ws.rs</groupId>--> - <!--<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>--> - <!--<version>1.0.0.Final</version>--> - <!--</dependency>--> + <dependency> + <groupId>org.jboss.spec.javax.ws.rs</groupId> + <artifactId>jboss-jaxrs-api_2.1_spec</artifactId> + <version>1.0.0.Final</version> + </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> - <version>2.8.9</version> + <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> - <version>2.8.9</version> + <version>${jackson.version}</version> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>org.jboss.spec.javax.servlet</groupId> + <artifactId>jboss-servlet-api_4.0_spec</artifactId> + <version>1.0.0.Final</version> + <scope>test</scope> + </dependency> <!-- needed for Servlet 3 --> <dependency> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch refonte-rest in repository coselmar. See https://gitlab.nuiton.org/codelutin/coselmar.git commit 6e0a1403e72d6a78441ee55e026a3b8b3f6dbfef Author: Yannick Martel <martel@©odelutin.com> Date: Tue Jun 4 18:10:13 2019 +0200 upgrade jwt lib --- .../fr/ifremer/coselmar/beans/UserWebToken.java | 21 +++++ .../services/CoselmarWebServiceSupport.java | 92 +++------------------- .../coselmar/services/v1/DocumentsWebService.java | 14 ++-- .../coselmar/services/v1/QuestionsWebService.java | 49 ++++++------ .../coselmar/services/v1/UsersWebService.java | 29 ++++--- .../services/AbstractCoselmarWebServiceTest.java | 5 ++ .../coselmar/services/QuestionsWebServiceTest.java | 13 +-- .../coselmar/services/UsersWebServiceTest.java | 7 +- pom.xml | 2 +- 9 files changed, 96 insertions(+), 136 deletions(-) diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserWebToken.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserWebToken.java index e39de60..aaed87a 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserWebToken.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserWebToken.java @@ -24,6 +24,10 @@ package fr.ifremer.coselmar.beans; * #L% */ +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTCreator; +import com.auth0.jwt.interfaces.DecodedJWT; + import java.io.Serializable; import java.util.HashMap; import java.util.Map; @@ -57,6 +61,13 @@ public class UserWebToken implements Serializable { this.role = ((String) claims.get(CLAIMS_ROLE)).toUpperCase(); } + public UserWebToken(DecodedJWT decodedJWT) { + this.userId = decodedJWT.getClaim(CLAIMS_USER_ID).asString(); + this.firstName = decodedJWT.getClaim(CLAIMS_FIRST_NAME).asString(); + this.lastName = decodedJWT.getClaim(CLAIMS_LAST_NAME).asString(); + this.role = decodedJWT.getClaim(CLAIMS_ROLE).asString().toUpperCase(); + } + public String getUserId() { return userId; } @@ -109,4 +120,14 @@ public class UserWebToken implements Serializable { return claims; } + public static JWTCreator.Builder toJwtClaimBuilder(String userId, String firstName, String lastName, String role) { + + JWTCreator.Builder jwtClaimBuilder = JWT.create() + .withClaim(CLAIMS_USER_ID, userId) + .withClaim(CLAIMS_FIRST_NAME, firstName) + .withClaim(CLAIMS_LAST_NAME, lastName) + .withClaim(CLAIMS_ROLE, role.toUpperCase()); + return jwtClaimBuilder; + } + } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java index 87f1d8d..0db64f5 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java @@ -24,10 +24,12 @@ package fr.ifremer.coselmar.services; * #L% */ +import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; -import com.auth0.jwt.JWTVerifyException; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; import fr.ifremer.coselmar.beans.UserWebToken; -import fr.ifremer.coselmar.exceptions.CoselmarTechnicalException; import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; import fr.ifremer.coselmar.services.errors.InvalidCredentialException; @@ -36,12 +38,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuiton.topia.persistence.TopiaNoResultException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.SignatureException; -import java.util.Map; - /** * @author ymartel <martel@codelutin.com> */ @@ -52,66 +48,15 @@ public abstract class CoselmarWebServiceSupport implements CoselmarService { private CoselmarServicesContext servicesContext; + protected Algorithm getJwtAlgorithm(CoselmarServicesContext servicesContext) { + return Algorithm.HMAC384(servicesContext.getCoselmarServicesConfig().getWebSecurityKey()); + } + @Override public void setServicesContext(CoselmarServicesContext servicesContext) { this.servicesContext = servicesContext; } - /** - * Check the authorization code. - * Get the token from the authorization and reconstitute JWT user token. - * - * @param authorization : authorization containing the encoding token - * @return corresponding {@link fr.ifremer.coselmar.beans.UserWebToken} - * - * @throws InvalidCredentialException if token is not valid. - * - * @Deprecated since 0.8 : prefer use {@link #checkUserAuthentication(CoselmarServicesContext, String)} that also check user validity. - */ - @Deprecated - protected UserWebToken checkAuthentication(String authorization) throws InvalidCredentialException { - try { - - String webSecurityKey = CoselmarServicesApplicationContext.getInstance().getApplicationConfig().getWebSecurityKey(); - JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); - - String token = StringUtils.replace(authorization, "Bearer ", ""); - Map<String, Object> claims = jwtVerifier.verify(token); - UserWebToken userWebToken = new UserWebToken(claims); - return userWebToken; - - } catch (NoSuchAlgorithmException|InvalidKeyException|IOException e) { - // This should not happened or this is really exceptional ! - if (log.isErrorEnabled()) { - log.error("Error during JWT verification : wrong Algorithm !", e); - } - throw new CoselmarTechnicalException(e); - - } catch (SignatureException e) { - // Invalid Signature ! It's a Fake ! - if (log.isErrorEnabled()) { - log.error("Error during JWT verification : bad signature!", e); - } - throw new InvalidCredentialException("Error with signature"); - - } catch (JWTVerifyException e) { - // Error during Payload verification - if (log.isErrorEnabled()) { - log.error("Error during JWT verification : bad claims!", e); - } - throw new InvalidCredentialException("Error with claims"); - - } catch (IllegalStateException e) { - // No token set - if (log.isErrorEnabled()) { - log.error("Error during JWT verification : no token!", e); - } - throw new InvalidCredentialException("Seems no user connected"); - - } - - } - /** * Check the authorization code. * Get the token from the authorization and reconstitute JWT user token. @@ -126,11 +71,10 @@ public abstract class CoselmarWebServiceSupport implements CoselmarService { protected CoselmarUser checkUserAuthentication(CoselmarServicesContext servicesContext, String authorization) throws InvalidCredentialException { try { - String webSecurityKey = CoselmarServicesApplicationContext.getInstance().getApplicationConfig().getWebSecurityKey(); - JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); + JWTVerifier jwtVerifier = JWT.require(getJwtAlgorithm(servicesContext)).build(); String token = StringUtils.replace(authorization, "Bearer ", ""); - Map<String, Object> claims = jwtVerifier.verify(token); + DecodedJWT claims = jwtVerifier.verify(token); UserWebToken userWebToken = new UserWebToken(claims); // check user still exist @@ -139,21 +83,7 @@ public abstract class CoselmarWebServiceSupport implements CoselmarService { return coselmarUser; - } catch (NoSuchAlgorithmException|InvalidKeyException|IOException e) { - // This should not happened or this is really exceptional ! - if (log.isErrorEnabled()) { - log.error("Error during JWT verification : wrong Algorithm !", e); - } - throw new CoselmarTechnicalException(e); - - } catch (SignatureException e) { - // Invalid Signature ! It's a Fake ! - if (log.isErrorEnabled()) { - log.error("Error during JWT verification : bad signature!", e); - } - throw new InvalidCredentialException("Error with signature"); - - } catch (JWTVerifyException e) { + } catch (JWTVerificationException e) { // Error during Payload verification if (log.isErrorEnabled()) { log.error("Error during JWT verification : bad claims!", e); diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java index b84ad05..4ea5141 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java @@ -395,13 +395,16 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { MultipartFormDataInput uploadFile) throws InvalidCredentialException, UnauthorizedException { // Check authentication - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser userWebToken = checkUserAuthentication(servicesContext, authorization); // Only Expert or Supervisor can add document - String userRole = userWebToken.getRole(); - if (!DOCUMENT_CREATE_ALLOWED_USER_ROLES.contains(userRole.toUpperCase())) { + CoselmarUserRole userRole = userWebToken.getRole(); + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String userShortId = getShortIdFromFull(persistenceContext, userWebToken.getTopiaId()); + + if (!DOCUMENT_CREATE_ALLOWED_USER_ROLES.contains(userRole.name().toUpperCase())) { String message = String.format("User %s %s ('%s') is not allowed to add document", - userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); + userWebToken.getFirstname(), userWebToken.getName(), userShortId); if (log.isWarnEnabled()) { log.warn(message); } @@ -411,8 +414,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { Preconditions.checkNotNull(document); // retrieve user who will be assigned as document owner - CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); - String fullId = getFullUserIdFromShort(persistenceContext, userWebToken.getUserId()); + String fullId = userWebToken.getTopiaId(); CoselmarUser owner; try { diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java index 60b5689..d039474 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java @@ -117,14 +117,16 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { @FormParam("question") QuestionBean question) throws InvalidCredentialException, UnauthorizedException { // Check authentication - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser userWebToken = checkUserAuthentication(servicesContext, authorization); // Only Supervisor can add question - String userRole = userWebToken.getRole(); + CoselmarUserRole userRole = userWebToken.getRole(); - if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole)) { + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String userShortId = getShortIdFromFull(persistenceContext, userWebToken.getTopiaId()); + if (!CoselmarUserRole.SUPERVISOR.equals(userRole)) { String message = String.format("User %s %s ('%s') is not allowed to add question", - userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); + userWebToken.getFirstname(), userWebToken.getName(), userShortId); if (log.isWarnEnabled()) { log.warn(message); } @@ -132,9 +134,8 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } - CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); // retrieve user who will be assigned as question supervisor - String fullId = getFullUserIdFromShort(persistenceContext, userWebToken.getUserId()); + String fullId = userWebToken.getTopiaId(); CoselmarUser supervisor; try { @@ -383,24 +384,25 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { @PathParam("questionId") String questionId) throws InvalidCredentialException, UnauthorizedException { // Check authentication - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser userWebToken = checkUserAuthentication(servicesContext, authorization); // Only Supervisor can delete question - String userRole = userWebToken.getRole(); + CoselmarUserRole userRole = userWebToken.getRole(); - if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole) - && !StringUtils.equalsIgnoreCase(CoselmarUserRole.ADMIN.name(), userRole)) { + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String userShortId = getShortIdFromFull(persistenceContext, userWebToken.getTopiaId()); + + if (!CoselmarUserRole.SUPERVISOR.equals(userRole) + && !CoselmarUserRole.ADMIN.equals(userRole)) { String message = String.format("User %s %s ('%s') is not allowed to delete question", - userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); + userWebToken.getFirstname(), userWebToken.getName(), userShortId); if (log.isWarnEnabled()) { log.warn(message); } throw new UnauthorizedException(message); } - - CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); - String fullUserId = getFullIdFromShort(persistenceContext, CoselmarUser.class, userWebToken.getUserId()); + String fullUserId = userWebToken.getTopiaId(); try { persistenceContext.getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); @@ -533,26 +535,27 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { DocumentBean[] documents) throws InvalidCredentialException, UnauthorizedException { // Check authentication - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser userWebToken = checkUserAuthentication(servicesContext, authorization); // Only Supervisor can add documents - String userRole = userWebToken.getRole(); + CoselmarUserRole userRole = userWebToken.getRole(); + + CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); + String userShortId = getShortIdFromFull(persistenceContext, userWebToken.getTopiaId()); - if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole) - && !StringUtils.equalsIgnoreCase(CoselmarUserRole.ADMIN.name(), userRole) - && !StringUtils.equalsIgnoreCase(CoselmarUserRole.EXPERT.name(), userRole)) { + if (!CoselmarUserRole.SUPERVISOR.equals(userRole) + && !CoselmarUserRole.ADMIN.equals(userRole) + && !CoselmarUserRole.EXPERT.equals(userRole)) { String message = String.format("User %s %s ('%s') is not allowed to add document", - userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); + userWebToken.getFirstname(), userWebToken.getName(), userShortId); if (log.isWarnEnabled()) { log.warn(message); } throw new UnauthorizedException(message); } - - CoselmarPersistenceContext persistenceContext = servicesContext.getPersistenceContext(); - String fullUserId = getFullIdFromShort(persistenceContext, CoselmarUser.class, userWebToken.getUserId()); + String fullUserId = userWebToken.getTopiaId(); CoselmarUser currentUser; try { diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java index 1a8e2dd..bfa26b3 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java @@ -24,8 +24,8 @@ package fr.ifremer.coselmar.services.v1; * #L% */ -import com.auth0.jwt.Algorithm; -import com.auth0.jwt.JWTSigner; +import com.auth0.jwt.JWTCreator; +import com.auth0.jwt.algorithms.Algorithm; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheException; @@ -155,11 +155,11 @@ public class UsersWebService extends CoselmarWebServiceSupport { @QueryParam("search") UserSearchBean search) throws InvalidCredentialException, UnauthorizedException { // Check authentication - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser userWebToken = checkUserAuthentication(servicesContext, authorization); // Who is allowed here ? Admin and user himself - if (!StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.ADMIN.name()) - && !StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.SUPERVISOR.name())) { + if (!CoselmarUserRole.ADMIN.equals(userWebToken.getRole()) + && !CoselmarUserRole.SUPERVISOR.equals(userWebToken.getRole())) { if (log.isDebugEnabled()) { String message = String.format("A non admin, non supervisor user is trying to access users list"); log.debug(message); @@ -320,10 +320,10 @@ public class UsersWebService extends CoselmarWebServiceSupport { UserBean user) throws InvalidCredentialException, UnauthorizedException, InvalidParameterException, TopiaNoResultException, MailAlreadyExistingException { // Check authentication - UserWebToken userWebToken = checkAuthentication(authorization); + CoselmarUser userWebToken = checkUserAuthentication(servicesContext, authorization); - boolean isAdmin = StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.ADMIN.name()); - boolean isSupervisor4Client = StringUtils.equals(userWebToken.getRole(), CoselmarUserRole.SUPERVISOR.name()) && StringUtils.equals(user.getRole(), CoselmarUserRole.CLIENT.name()); + boolean isAdmin = CoselmarUserRole.ADMIN.equals(userWebToken.getRole()); + boolean isSupervisor4Client = CoselmarUserRole.SUPERVISOR.equals(userWebToken.getRole()) && StringUtils.equals(CoselmarUserRole.CLIENT.name(), user.getRole()); String askedUserId = user.getId(); if (StringUtils.isBlank(askedUserId)) { @@ -336,7 +336,8 @@ public class UsersWebService extends CoselmarWebServiceSupport { } // Who is allowed here ? Admin and user himself only and Supervisor if it is a "client" type user - if (!isAdmin && !StringUtils.equals(userWebToken.getUserId(), askedUserId) && !isSupervisor4Client) { + String userShortId = getShortIdFromFull(servicesContext.getPersistenceContext(), userWebToken.getTopiaId()); + if (!isAdmin && !StringUtils.equals(userShortId, askedUserId) && !isSupervisor4Client) { if (log.isDebugEnabled()) { String message = String.format("A non admin user try to modify account details with shortId '%s'", askedUserId); log.debug(message); @@ -441,13 +442,11 @@ public class UsersWebService extends CoselmarWebServiceSupport { checkPassword(servicesContext, user.getPassword(), salt, password); // return a Json Web Token for authentication - JWTSigner jwtSigner = new JWTSigner(servicesContext.getCoselmarServicesConfig().getWebSecurityKey()); - JWTSigner.Options signerOption = new JWTSigner.Options(); - signerOption.setAlgorithm(Algorithm.HS384); - String shortId = getShortIdFromFull(servicesContext.getPersistenceContext(), user.getTopiaId()); - Map<String, Object> claims = UserWebToken.toJwtClaims(shortId, user.getFirstname(), user.getName(), user.getRole().name()); - String webToken = jwtSigner.sign(claims, signerOption); + JWTCreator.Builder jwtBuilder = UserWebToken.toJwtClaimBuilder(shortId, user.getFirstname(), user.getName(), user.getRole().name()); + + Algorithm jwtAlgorithm = getJwtAlgorithm(servicesContext); + String webToken = jwtBuilder.sign(jwtAlgorithm); return ImmutableMap.of("jwt", webToken); diff --git a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/AbstractCoselmarWebServiceTest.java b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/AbstractCoselmarWebServiceTest.java index 1c4b9d5..1becab0 100644 --- a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/AbstractCoselmarWebServiceTest.java +++ b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/AbstractCoselmarWebServiceTest.java @@ -24,6 +24,7 @@ package fr.ifremer.coselmar.services; * #L% */ +import com.auth0.jwt.algorithms.Algorithm; import fr.ifremer.coselmar.persistence.CoselmarPersistenceContext; import io.undertow.Undertow; import org.apache.commons.logging.Log; @@ -50,6 +51,10 @@ public class AbstractCoselmarWebServiceTest { private static UndertowJaxrsServer server; + protected Algorithm getJwtAlgorithm(CoselmarServicesContext servicesContext) { + return Algorithm.HMAC384(servicesContext.getCoselmarServicesConfig().getWebSecurityKey()); + } + @Before public void startServer() throws Exception { diff --git a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/QuestionsWebServiceTest.java b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/QuestionsWebServiceTest.java index fdbb835..e6c2369 100644 --- a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/QuestionsWebServiceTest.java +++ b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/QuestionsWebServiceTest.java @@ -24,7 +24,9 @@ package fr.ifremer.coselmar.services; * #L% */ +import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.interfaces.DecodedJWT; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Sets; @@ -318,8 +320,7 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { @Test public void testSearchUserQuestions() throws Exception { - String webSecurityKey = getServiceContext().getCoselmarServicesConfig().getWebSecurityKey(); - JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); + JWTVerifier jwtVerifier = JWT.require(getJwtAlgorithm(getServiceContext())).build(); Client client = ClientBuilder.newClient(); @@ -339,8 +340,8 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { Map<String, String> loginJson = gson.fromJson(loginContent, Map.class); String expertToken = loginJson.get("jwt"); - Map<String, Object> expertMap = jwtVerifier.verify(expertToken); - String expertId = (String) expertMap.get("userId"); + DecodedJWT expertJwt = jwtVerifier.verify(expertToken); + String expertId = expertJwt.getClaim("userId").asString(); //First : login ! loginForm = new Form() @@ -355,8 +356,8 @@ public class QuestionsWebServiceTest extends AbstractCoselmarWebServiceTest { loginJson = gson.fromJson(loginContent, Map.class); String supervisorToken = loginJson.get("jwt"); - Map<String, Object> supervisorMap = jwtVerifier.verify(supervisorToken); - String supervisorId = (String) supervisorMap.get("userId"); + DecodedJWT supervisorJwt = jwtVerifier.verify(supervisorToken); + String supervisorId = supervisorJwt.getClaim("userId").asString(); // Create first document diff --git a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/UsersWebServiceTest.java b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/UsersWebServiceTest.java index 8c6e71d..d9535dd 100644 --- a/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/UsersWebServiceTest.java +++ b/coselmar-rest/src/test/java/fr/ifremer/coselmar/services/UsersWebServiceTest.java @@ -24,6 +24,7 @@ package fr.ifremer.coselmar.services; * #L% */ +import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.google.gson.Gson; import fr.ifremer.coselmar.beans.UserBean; @@ -76,8 +77,7 @@ public class UsersWebServiceTest extends AbstractCoselmarWebServiceTest { Gson gson = new Gson(); Map<String, String> map = gson.fromJson(loginContent, Map.class); - String webSecurityKey = getServiceContext().getCoselmarServicesConfig().getWebSecurityKey(); - JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); + JWTVerifier jwtVerifier = JWT.require(getJwtAlgorithm(getServiceContext())).build(); String token = map.get("jwt"); jwtVerifier.verify(token); @@ -133,8 +133,7 @@ public class UsersWebServiceTest extends AbstractCoselmarWebServiceTest { Map<String, String> newUserLoginMap = gson.fromJson(loginContent, Map.class); - String webSecurityKey = getServiceContext().getCoselmarServicesConfig().getWebSecurityKey(); - JWTVerifier jwtVerifier = new JWTVerifier(webSecurityKey, "audience"); + JWTVerifier jwtVerifier = JWT.require(getJwtAlgorithm(getServiceContext())).build(); String token = newUserLoginMap.get("jwt"); jwtVerifier.verify(token); diff --git a/pom.xml b/pom.xml index e3f0315..2ac4389 100644 --- a/pom.xml +++ b/pom.xml @@ -148,7 +148,7 @@ <jqueryVersion>2.1.4</jqueryVersion> <fontAwesomeVersion>4.5.0</fontAwesomeVersion> - <java-jwt.version>2.1.0</java-jwt.version> + <java-jwt.version>3.8.1</java-jwt.version> <gson.version>2.5</gson.version> <guava.version>19.0-rc3</guava.version> <junit.version>4.12</junit.version> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
participants (1)
-
codelutin.com scm