Une nouvelle version du portail de gestion des comptes externes sera mise en production lundi 09 août. Elle permettra d'allonger la validité d'un compte externe jusqu'à 3 ans. Pour plus de détails sur cette version consulter : https://doc-si.inria.fr/x/FCeS

Commit 227d1a21 authored by Wylem Bars's avatar Wylem Bars
Browse files

Merge branch 'feature/PATREG-31' and 'feature/PATREG-38' into 'develop'

See merge request !2
parents 00758962 a5d61394
Pipeline #132970 passed with stages
in 8 minutes and 21 seconds
......@@ -74,3 +74,11 @@ fabric.properties
target/
.idea/
.settings/**
**/.settings/**
.classpath
.project
**/.classpath
**/.project
# Define templates
include:
- project: 'gazelle/private/gitlab-ci-templates'
file: 'global.yaml'
- project: 'gazelle/private/gitlab-ci-templates'
file: 'build.yaml'
- project: 'gazelle/private/gitlab-ci-templates'
file: 'predeploy.yaml'
- project: 'gazelle/private/gitlab-ci-templates'
file: 'deploy.yaml'
- project: 'gazelle/private/gitlab-ci-templates'
file: 'tests.yaml'
- project: 'gazelle/private/gitlab-ci-templates'
file: 'release.yaml'
- project: 'gazelle/private/gitlab-ci-templates'
file: 'upgrade.yaml'
- project: 'gazelle/private/gitlab-ci-templates'
file: 'cleanup.yaml'
file: 'extends.yaml'
ref: 'master'
# Define stages
stages:
- build
- predeploy
- postbuild
- deploy
- tests
- release
- upgrade
- cleanup
# Define global variables
variables:
P_NAME: "${projectName}"
P_NAME: "app.patient-registry"
P_APP_TYPE: "java"
P_CODE_SRC_PATH: "."
P_DB_DATASOURCE_NAME: "patientRegistry"
P_IMPORT_DB_SCHEMA: "true"
P_DB_SCHEMA_PATH: "patient-registry-service/src/main/resources/sql/schema.sql"
P_DB_NAME: "pam-simulator"
services:
- postgres:9.6.17-alpine
# Define jobs
code:
stage: build
extends:
- .codeForJava
- .codeForJavaWithWildflyAndPostgresql
services:
- postgres:9.6.17-alpine
variables:
P_CODE_SRC_PATH: "."
P_MAVEN_IMAGE_TAG: "3.6.3"
P_MAVEN_IMAGE_TAG: "wildfly-3.6.3-18.0.1.Final"
quality:
stage: tests
extends:
- .sonarqubeForJava
- .qualityForJavaWithSonarqubeAndPostgresql
variables:
P_MAVEN_IMAGE_TAG: "3.6.3"
P_MAVEN_IMAGE_TAG: "wildfly-3.6.3-18.0.1.Final"
P_CODE_BINARIES: "target/classes/"
P_CODE_JACOCO_REPORT_PATH: "target/jacoco.exec"
P_CODE_JUNIT_REPORTS_PATH: "target/surefire-reports"
......@@ -55,5 +52,4 @@ quality:
P_CODE_SOURCE_ENCODING: "UTF-8"
P_CODE_LANGUAGE: "java"
P_CODE_DEVELOPER_EDITION: "true"
P_CODE_SRC_PATH: "."
# Patient Registry
## Patient model API
Define Gazelle patient model to be used by the platform applications as well as the different api to implement to define a patient service.
### APIs
The current supported APIs are :
- Patient feed service :
```java
String feedPatient(Patient patient) throws PatientFeedException;
```
- Patient retrieve service
```java
Patient retrievePatient(String uuid) throws PatientRetrieveException ;
```
- Patient search service
```java
List<Patient> search(SearchCriteria searchCriteria) throws SearchException;
```
- Domain service
```java
boolean exist(String domainIdentifier);
void createDomain(String domainIdentifier, String domainName);
```
### Dependency
To use the model, add the dependency to the library in the `pom.xml` of the client application.
```xml
<dependency>
<groupId>net.ihe.gazelle</groupId>
<artifactId>patient-registry-model-api</artifactId>
<version>...</version>
</dependency>
```
## Patient service
The patient service implements the different APIs defined in the `Patient model API` with a database.
### Service Usage
The service can either be deployed in a container or as a java component and instantiated with constructors or injection.
The different services accessible through a `GITB processing service` (SOAP) are :
- patient feed
- patient search
### Dependency
To use the service, add the dependency to the library in the `pom.xml` of the client application.
```xml
<dependency>
<groupId>net.ihe.gazelle</groupId>
<artifactId>patient-registry-service</artifactId>
<version>...</version>
</dependency>
```
## Patient search client
The patient search client is a `GITB Processing Service Client` which will point to a `GITB Processing Service`.
It will be used to search Patients depending on specific characteristics
### Client API
- The client can point to a distant service
```java
/**
* Default constructor for the class. The Client will create the GITB Processing Service client based on the URL.
* @param processingServiceURL : URL of the remote Processing Service.
*/
public PatientSearchClient(URL processingServiceURL)
```
- The client can point to an instantiated Patient Search Service
```java
/**
* Constructor used for test purposes.
* @param processingService : processing service to be used by the PatientSearchClient.
*/
public PatientSearchClient(ProcessingService processingService)
```
### Dependency
To use the client, add the dependency to the library in the `pom.xml` of the client application.
```xml
<dependency>
<groupId>net.ihe.gazelle</groupId>
<artifactId>app.patient-registry-search-client</artifactId>
<version>...</version>
</dependency>
```
## Patient feed client
The patient search client is a `GITB Processing Service Client` which will point to a `GITB Processing Service`.
It will be used to order the creation of a patient.
### Client API
- The client can point to a distant service
```java
/**
* Default constructor for the class. The Client will create the GITB Processing Service client based on the URL.
* @param processingServiceURL : URL of the remote Processing Service.
*/
public PatientFeedClient(URL processingServiceURL)
```
- The client can point to an instantiated Patient Feed Service
```java
/**
* Constructor used for test purposes.
* @param processingService : processing service to be used by the PatientFeedClient.
*/
public PatientFeedClient(ProcessingService processingService)
```
### Dependency
To use the client, add the dependency to the library in the `pom.xml` of the client application.
```xml
<dependency>
<groupId>net.ihe.gazelle</groupId>
<artifactId>app.patient-registry-feed-client</artifactId>
<version>...</version>
</dependency>
```
\ No newline at end of file
......@@ -3,7 +3,6 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>app.patient-registry</artifactId>
<groupId>net.ihe.gazelle</groupId>
......@@ -15,4 +14,41 @@
<name>Patient Registry Feed Client</name>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<!-- Logger Model -->
<dependency>
<groupId>net.ihe.gazelle</groupId>
<artifactId>framework.logger-service</artifactId>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
</dependency>
<dependency>
<groupId>net.ihe.gazelle</groupId>
<artifactId>app.patient-registry-api</artifactId>
</dependency>
<dependency>
<groupId>net.ihe.gazelle</groupId>
<artifactId>lib.gitb-processing-client</artifactId>
</dependency>
<dependency>
<groupId>net.ihe.gazelle</groupId>
<artifactId>lib.gitb-utils</artifactId>
</dependency>
<dependency>
<groupId>net.ihe.gazelle</groupId>
<artifactId>lib.unit-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package net.ihe.gazelle.app.patientregistryfeedclient.adapter;
import com.gitb.core.AnyContent;
import com.gitb.ps.ProcessRequest;
import com.gitb.ps.ProcessResponse;
import com.gitb.ps.ProcessingService;
import com.gitb.tr.BAR;
import com.gitb.tr.TAR;
import com.gitb.tr.TestResultType;
import net.ihe.gazelle.app.patientregistryapi.adapter.ws.PatientRegistryGITBWebserviceConstants;
import net.ihe.gazelle.app.patientregistryapi.application.PatientFeedException;
import net.ihe.gazelle.app.patientregistryapi.application.PatientFeedService;
import net.ihe.gazelle.app.patientregistryapi.business.Patient;
import net.ihe.gazelle.lib.gitbprocessingclient.adapter.GITBClientProcessImpl;
import net.ihe.gazelle.lib.gitbutils.adapter.MapperAnyContentToObject;
import net.ihe.gazelle.lib.gitbutils.adapter.MapperObjectToAnyContent;
import net.ihe.gazelle.lib.gitbutils.adapter.MappingException;
import java.net.URL;
import java.util.List;
import static net.ihe.gazelle.app.patientregistryapi.adapter.ws.PatientRegistryGITBWebserviceConstants.*;
/**
* PatientFeed Client to be used by any application that wants to feed a patient to a remote Patient Registry.
* Given an url, this client will be able to feed the remote registry using GITB Process Service while exposing the
* PatientFeedService interface.
*
* @author wbars
*/
public class PatientFeedClient implements PatientFeedService {
private ProcessingService processingClient;
/**
* Constructor used for test purposes.
* @param processingService : processing service to be used by the PatientFeedClient.
*/
public PatientFeedClient(ProcessingService processingService){
this.processingClient = processingService;
}
/**
* Default constructor for the class. The Client will create the GITB Processing Service client based on the URL.
* @param processingServiceURL : URL of the remote Processing Service.
*/
public PatientFeedClient(URL processingServiceURL){
this.processingClient = new GITBClientProcessImpl(processingServiceURL, PATIENT_PROCESSING_SERVICE, PATIENT_PROCESSING_SERVICE_PORT);
}
/**
* {@inheritDoc}
*/
@Override
public String feedPatient(Patient patient) throws PatientFeedException {
ProcessRequest processRequest = new ProcessRequest();
processRequest.setOperation(PatientRegistryGITBWebserviceConstants.PATIENT_FEED_OPERATION);
AnyContent patientToFeedAnyContent = null;
try {
patientToFeedAnyContent = new MapperObjectToAnyContent().getAnyContent(PATIENT_INPUT_NAME, patient);
processRequest.getInput().add(patientToFeedAnyContent);
ProcessResponse processResponse = processingClient.process(processRequest);
return extractUUIDFromProcessResponse(processResponse);
} catch (MappingException e){
throw new PatientFeedException("Exception while Mapping with GITB elements !", e);
} catch (PatientFeedProcessResponseException e){
throw new PatientFeedException("Invalid Response from distant PatientFeedProcessingService !", e);
} catch (UnsupportedOperationException e){
throw new PatientFeedException("Invalid operation used on distant PatientFeedProcessingService !", e);
} catch (IllegalArgumentException e){
throw new PatientFeedException("Invalid Request sent to distant PatientFeedProcessingService !", e);
}
}
/**
* Extract the UUID from the ProcessResponse received from the remote ProcessingService.
* @param processResponse : response received.
* @return literal value of the UUID assigned to the fed patient by the remote Repository.
* @throws PatientFeedProcessResponseException : if the ProcesssResponse is not valid for a PatientFeed request.
* @throws PatientFeedException : if the Patient Feed raised an exception in the remote Repository.
*/
private String extractUUIDFromProcessResponse(ProcessResponse processResponse) throws PatientFeedProcessResponseException, PatientFeedException{
if (processResponse == null){
throw new PatientFeedProcessResponseException("Empty Response from the distant PatientFeedProcessingService !");
} else{
if(processResponse.getReport() != null){
if (TestResultType.SUCCESS.equals(processResponse.getReport().getResult())){
return extractUUIDFromOutputs(processResponse.getOutput());
} else if (TestResultType.FAILURE.equals(processResponse.getReport().getResult())){
throw createExceptionFromProcessResponseReport(processResponse.getReport());
} else {
throw new PatientFeedProcessResponseException(String.format("Processing response with unexpected type %s ! Expected type is " +
"either %s or %s.",processResponse.getReport().getResult(), TestResultType.SUCCESS, TestResultType.FAILURE));
}
} else {
throw new PatientFeedProcessResponseException("Response from the distant PatientFeedProcessingService shall contain a report with " +
"the success status of the operation !");
}
}
}
/**
* Extract the UUID from the ProcessResponse.output received from the remote ProcessingService.
* @param outputs : list of received outputs.
* @return literal value of the UUID assigned to the fed patient by the remote Repository.
* @throws PatientFeedProcessResponseException : if the ProcesssResponse.output is not valid for a PatientFeed request.
*/
private String extractUUIDFromOutputs(List<AnyContent> outputs) throws PatientFeedProcessResponseException{
if (outputs.size() == 1){
try {
return new MapperAnyContentToObject().getObject(outputs.get(0), String.class);
} catch (MappingException e){
throw new PatientFeedProcessResponseException("Error while mapping processing output from distant PatientFeedProcessingService " +
"to String !");
}
} else {
throw new PatientFeedProcessResponseException("Response from the distant PatientFeedProcessingService shall contain a single " +
"output for fed Patient UUID !");
}
}
/**
* Extract PatientFeedException from ProcessResponse report.
* @param report : report found in the received response.
* @return {@link PatientFeedException} to be thrown by the {@link PatientFeedClient}
* @throws PatientFeedProcessResponseException : if the report is not well formed.
*/
private PatientFeedException createExceptionFromProcessResponseReport(TAR report) throws PatientFeedProcessResponseException {
if (report.getReports() == null || report.getReports().getInfoOrWarningOrError().size() != 1){
throw new PatientFeedProcessResponseException("The report from the ProcessResponse shall not be null and shall contain a single error.");
} else {
try {
BAR error = (BAR) report.getReports().getInfoOrWarningOrError().get(0).getValue();
if (error.getDescription()!= null && !error.getDescription().isEmpty()){
return new PatientFeedException(error.getDescription());
} else {
throw new PatientFeedProcessResponseException("Error from ProcessResponse report must have a valid description !");
}
} catch (ClassCastException e){
throw new PatientFeedProcessResponseException("Cannot decode error from ProcessResponse report !");
}
}
}
}
package net.ihe.gazelle.app.patientregistryfeedclient.adapter;
/**
* Exception to be thrown when there is a problem with the response received from the distant ProcessingService.
*
* @author wbars
*/
public class PatientFeedProcessResponseException extends Exception {
/**
* Constructs a new exception with {@code null} as its detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*/
public PatientFeedProcessResponseException() {
super();
}
/**
* Constructs a new exception with the specified detail message. The
* cause is not initialized, and may subsequently be initialized by
* a call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public PatientFeedProcessResponseException(String message) {
super(message);
}
/**
* Constructs a new exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public PatientFeedProcessResponseException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new exception with the specified cause and a detail
* message of {@code (cause==null ? null : cause.toString())} (which
* typically contains the class and detail message of {@code cause}).
* This constructor is useful for exceptions that are little more than
* wrappers for other throwables (for example, {@link
* java.security.PrivilegedActionException}).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public PatientFeedProcessResponseException(Throwable cause) {
super(cause);
}
/**
* Constructs a new exception with the specified detail message,
* cause, suppression enabled or disabled, and writable stack
* trace enabled or disabled.
*
* @param message the detail message.
* @param cause the cause. (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.)
* @param enableSuppression whether or not suppression is enabled
* or disabled
* @param writableStackTrace whether or not the stack trace should
* be writable
* @since 1.7
*/
public PatientFeedProcessResponseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
Put here classes from adapter layer :
Data transformers, adapters, presenters or DAO. Abstraction of external libraries for
application or business use.
Web-services point, sockets, database connection and pool, GUI, file system, framework,
external libraries.
\ No newline at end of file
Put here classes from application layer :
Use cases. Business elements applied in an application context or scenario.
\ No newline at end of file
Put here classes from business layer :
Business model, rules and constraints. Always true. Independent from the application.
\ No newline at end of file
package net.ihe.gazelle.app.patientregistryfeedclient.adapter;
import net.ihe.gazelle.app.patientregistryapi.application.PatientFeedException;
import net.ihe.gazelle.app.patientregistryapi.business.Patient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* Test class for {@link PatientFeedClient}
*/
public class PatientFeedClientTest {
private PatientFeedClient patientFeedClient;
private TestProcessingService testProcessingService;
/**
* Initialization of the tested {@link PatientFeedClient}.
* It is configured to use the mocked remote service {@link TestProcessingService}
*/
@BeforeEach
public void init(){
testProcessingService = new TestProcessingService();
patientFeedClient = new PatientFeedClient(testProcessingService);
}
/**
* Test Feed client getting successful response
* @throws PatientFeedException
*/
@Test
public void feed_With_Success() throws PatientFeedException {
Patient patient = new Patient();
patient.setUuid("TEST-uuid1");
String uuid = patientFeedClient.feedPatient(patient);
assertEquals(TestProcessingService.UUID, uuid);
}
/**
* Test Feed client getting a response with no report
*/
@Test
public void feed_With_Success_missing_report() {
testProcessingService.setResponseToReturn(TestProcessingService.ResponseTypes.MISSING_REPORT);
Patient patient = new Patient();
patient.setUuid("TEST-uuid1");
PatientFeedException exception = assertThrows(PatientFeedException.class, () -> patientFeedClient.feedPatient(patient));
assertEquals("Response from the distant PatientFeedProcessingService shall contain a report with " +
"the success status of the operation !", exception.getCause().getMessage());
}
/**
* Test Feed client getting a response with failed report
*/