Commit a5d61394 authored by luc chatty's avatar luc chatty Committed by Wylem Bars

Include Patient Feed (Service and Client) and Patient Search on UUID (Search...

Include Patient Feed (Service and Client) and Patient Search on UUID (Search Client and Service with one known Criterion).
parent 00758962
......@@ -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;
/**