From 2290a0df76bb98002fa3296ac3ce96c3ab4a1980 Mon Sep 17 00:00:00 2001 From: pm <pmvanhouteghem@gmail.com> Date: Mon, 12 Jul 2021 09:27:53 +0200 Subject: [PATCH] Documentation & few sonar fixes --- README.md | 72 ++++- .../adapter/servlet/ChHapiFhirServer.java | 3 - .../PatientRegistryXRefSearchClient.java | 2 +- .../interceptor/LegacyLoggingInterceptor.java | 2 + .../provider/ChPatientResourceProvider.java | 280 +----------------- 5 files changed, 70 insertions(+), 289 deletions(-) diff --git a/README.md b/README.md index c5859f8..274d2bf 100644 --- a/README.md +++ b/README.md @@ -33,22 +33,82 @@ After building the project through Maven, the artifact created just has to be ad Once the project deployed on your WildFly, you can now call it to request a cross Referenced Patient in the Patient Registry. +Parameters allowed are : + +- The Patient Identifier and the Target System attributed to this identifier +- The Target System you want the cross reference from. +- The format returned. + +Cardinality for these parameters will be described in each profile since it's the main difference between each. + +#### IHE Profile + The URL to call is ```http {wildfly18.address}/pixm_fhir_server/fhir_ihe/Patient/$ihe-pix ``` -Parameters allowed are : -- The Patient Identifier and the Target System attributed to this identifier -- The Target System you want the cross reference from. + +|Parameter name|Cardinality|Parameter Type|Description| +|--------------|-----------|--------------|-----------| +|sourceIdentifier|1..1|Token|The Patient identifier search parameter that will be used by the Patient Identifier Cross-reference Manager to find cross matching identifiers associated with the Patient Resource +|targetSystem|0..*|uri|The target Patient Identifier Assigning Authority from which the returned identifiers should be selected.| +|_format|0..1|mime-type|The requested format of the response. Accepted values : JSON and XML| + +#### CH Profile + +The URL to call is +```http + {wildfly18.address}/pixm_fhir_server/fhir_ch/Patient/$ihe-pix +``` + + +|Parameter name|Cardinality|Parameter Type|Description| +|--------------|-----------|--------------|-----------| +|sourceIdentifier|1..1|Token|The Patient identifier search parameter that will be used by the Patient Identifier Cross-reference Manager to find cross matching identifiers associated with the Patient Resource +|targetSystem|1..2|uri|The target Patient Identifier Assigning Authority from which the returned identifiers should be selected.| +|_format|0..1|mime-type|The requested format of the response. Accepted values : JSON and XML| For example : -Given the Patient with the ID 69420 in the Target System 1:2:3:4:5023:31520 -And you want the cross referenced patient in the target system 5:6:7:8:0587:79085 +Given the Patient with the ID 69420 in the Target System 1.3.6.1.4.1.21367.13.20.3000 +And you want the cross referenced patient in the target system 1.3.6.1.4.1.21367.13.20.1000 The corresponding url will be : +```http + {wildfly18.address}/pixm_fhir_server/fhir_ihe/Patient/$ihe-pix?sourceIdentifier=urn:oid:1.3.6.1.4.1.21367.13.20.3000|69420&targetSystem=urn:oid:1.3.6.1.4.1.21367.13.20.1000 +``` + +--- +### Error returned +Malformed requests can cause different types of error, here is a quick overview of how to troubleshoot them : + + +An error 400 Bad Request is returned when the source domain given within the source identifier parameter is not recognized by the Patient Registry as an asigning authority. +In the case of our request above, the value "urn:oid:1.3.6.1.4.1.21367.13.20.3000" is not a valid source domain able to register Patients + +Common mistakes with the source domain include : +- Forgetting the namespace in front of the adress (urn:oid:) +- Malformed source domain. +The source domain can have the form of an url ```http - {wildfly18.address}/pixm_fhir_server/fhir_ihe/Patient/$ihe-pix?sourceIdentifier=urn:oid:1:2:3:4:5023:31520|69420&targetSystem=5:6:7:8:0587:79085 +sourceIdentifier=https://your.domain|id ``` +or of an adress, which must follows the pattern x.x.x.x.x.x.x.x.x.x +```http +sourceIdentifier=urn:oid:x.x.x.x.x.x.x.x.x.x|id +``` + +--- + + +An error 403 Forbidden is returned when a target domain given in the target system parameter is not recognized by the Patient Registry. +In the case of our request above, the value "urn:oid:1.3.6.1.4.1.21367.13.20.1000" is not recognized as a valid target domain containing Patients. + +Common mistakes with the target system are the same as the aformentioned error 400 since the target system and the source domain have the same representation. + +--- + +An error 404 Not Found is returned when the patient identifier given within the source identifier parameter is not recognized by the Patient registry. +In the case of our request above, the value "69420" is not a valid Identifier linked to an existing Patient. diff --git a/src/main/java/net/ihe/gazelle/adapter/servlet/ChHapiFhirServer.java b/src/main/java/net/ihe/gazelle/adapter/servlet/ChHapiFhirServer.java index 70ce408..95ecb2e 100644 --- a/src/main/java/net/ihe/gazelle/adapter/servlet/ChHapiFhirServer.java +++ b/src/main/java/net/ihe/gazelle/adapter/servlet/ChHapiFhirServer.java @@ -14,10 +14,7 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor; import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; -import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; -import net.ihe.gazelle.business.interceptor.LegacyLoggingInterceptor; import net.ihe.gazelle.business.provider.ChPatientResourceProvider; -import net.ihe.gazelle.business.provider.IhePatientResourceProvider; /** * This servlet is the actual FHIR server itself diff --git a/src/main/java/net/ihe/gazelle/application/PatientRegistryXRefSearchClient.java b/src/main/java/net/ihe/gazelle/application/PatientRegistryXRefSearchClient.java index eaac73d..dcafd02 100644 --- a/src/main/java/net/ihe/gazelle/application/PatientRegistryXRefSearchClient.java +++ b/src/main/java/net/ihe/gazelle/application/PatientRegistryXRefSearchClient.java @@ -225,7 +225,7 @@ public class PatientRegistryXRefSearchClient { try { ip = InetAddress.getLocalHost(); serverUrl = ip.getCanonicalHostName(); - System.out.println("Your current Hostname : " + serverUrl); + logger.info(String.format("Your current Hostname : %s", serverUrl)); } catch (UnknownHostException exception) { logger.error("Unable to find serverUrl"); diff --git a/src/main/java/net/ihe/gazelle/business/interceptor/LegacyLoggingInterceptor.java b/src/main/java/net/ihe/gazelle/business/interceptor/LegacyLoggingInterceptor.java index 3bf84bc..6bfc937 100644 --- a/src/main/java/net/ihe/gazelle/business/interceptor/LegacyLoggingInterceptor.java +++ b/src/main/java/net/ihe/gazelle/business/interceptor/LegacyLoggingInterceptor.java @@ -28,6 +28,7 @@ public class LegacyLoggingInterceptor extends InterceptorAdapter { return true; // Processing should continue } + @Override @Hook(Pointcut.SERVER_HANDLE_EXCEPTION) public boolean handleException( RequestDetails theRequestDetails, @@ -48,6 +49,7 @@ public class LegacyLoggingInterceptor extends InterceptorAdapter { return false; } + @Override @Hook(Pointcut.SERVER_OUTGOING_RESPONSE) public boolean outgoingResponse(RequestDetails theRequestDetails) { ServletRequestDetails details = (ServletRequestDetails) theRequestDetails; diff --git a/src/main/java/net/ihe/gazelle/business/provider/ChPatientResourceProvider.java b/src/main/java/net/ihe/gazelle/business/provider/ChPatientResourceProvider.java index 391036c..609ec60 100644 --- a/src/main/java/net/ihe/gazelle/business/provider/ChPatientResourceProvider.java +++ b/src/main/java/net/ihe/gazelle/business/provider/ChPatientResourceProvider.java @@ -41,114 +41,6 @@ public class ChPatientResourceProvider implements IResourceProvider { private PatientRegistryXRefSearchClient patientRegistryXRefSearchClient; private static final Logger patientLogger = LoggerFactory.getLogger(ChPatientResourceProvider.class); - /** - * This map has a resource ID as a key, and each key maps to a Deque list containing all versions of the resource with that ID. - */ - private Map<Long, Deque<Patient>> myIdToPatientVersions = new HashMap<Long, Deque<Patient>>(); - - /** - * This is used to generate new IDs - */ - private long myNextId = 1; - - /** - * Constructor, which pre-populates the provider with one resource instance. - */ - public ChPatientResourceProvider() { - long resourceId = myNextId++; - - Patient patient = new Patient(); - patient.setId(Long.toString(resourceId)); - patient.addIdentifier(); - patient.getIdentifier().get(0).setSystem("urn:oid"); - patient.getIdentifier().get(0).setValue("00002"); - patient.addName().setFamily("Test"); - patient.getName().get(0).addGiven("PatientOne"); - patient.setGender(AdministrativeGender.FEMALE); - - LinkedList<Patient> list = new LinkedList<Patient>(); - list.add(patient); - - - myIdToPatientVersions.put(resourceId, list); - - } - - /** - * Stores a new version of the patient in memory so that it can be retrieved later. - * - * @param thePatient The patient resource to store - * @param theId The ID of the patient to retrieve - */ - private void addNewVersion(Patient thePatient, Long theId) { - if (!myIdToPatientVersions.containsKey(theId)) { - myIdToPatientVersions.put(theId, new LinkedList<Patient>()); - } - - thePatient.getMeta().setLastUpdatedElement(InstantType.withCurrentTime()); - - Deque<Patient> existingVersions = myIdToPatientVersions.get(theId); - - // We just use the current number of versions as the next version number - String newVersion = Integer.toString(existingVersions.size()); - - // Create an ID with the new version and assign it back to the resource - IdType newId = new IdType("Patient", Long.toString(theId), newVersion); - thePatient.setId(newId); - - existingVersions.add(thePatient); - } - - /** - * The "@Create" annotation indicates that this method implements "create=type", which adds a - * new instance of a resource to the server. - */ - @Create() - public MethodOutcome createPatient(@ResourceParam Patient thePatient) { - validateResource(thePatient); - - // Here we are just generating IDs sequentially - long id = myNextId++; - - addNewVersion(thePatient, id); - - // Let the caller know the ID of the newly created resource - return new MethodOutcome(new IdType(id)); - } - - /** - * The "@Search" annotation indicates that this method supports the search operation. You may have many different method annotated with this - * annotation, to support many different search criteria. - * This example searches by family name. - * - * @param theFamilyName This operation takes one parameter which is the search criteria. It is annotated with the "@Required" annotation. This - * annotation takes one argument, a string containing the name of - * the search criteria. The datatype here is StringDt, but there are other possible parameter types depending on the - * specific search criteria. - * @return This method returns a list of Patients. This list may contain multiple matching resources, or it may also be empty. - */ - @Search() - public List<Patient> findPatientsByName(@RequiredParam(name = Patient.SP_FAMILY) StringType theFamilyName) { - LinkedList<Patient> retVal = new LinkedList<Patient>(); - - /* - * Look for all patients matching the name - */ - for (Deque<Patient> nextPatientList : myIdToPatientVersions.values()) { - Patient nextPatient = nextPatientList.getLast(); - NAMELOOP: - for (HumanName nextName : nextPatient.getName()) { - String nextFamily = nextName.getFamily(); - if (theFamilyName.equals(nextFamily)) { - retVal.add(nextPatient); - break NAMELOOP; - } - } - } - - return retVal; - } - /** * Search method for a Patient using the source identifier required parameter * and an optional list of target system @@ -174,18 +66,6 @@ public class ChPatientResourceProvider implements IResourceProvider { return parametersResults; } - @Search - public List<Patient> findPatientsUsingArbitraryCriteria() { - LinkedList<Patient> retVal = new LinkedList<Patient>(); - - for (Deque<Patient> nextPatientList : myIdToPatientVersions.values()) { - Patient nextPatient = nextPatientList.getLast(); - retVal.add(nextPatient); - } - - return retVal; - } - /** * The getResourceType method comes from IResourceProvider, and must be overridden to indicate what type of resource this provider supplies. @@ -193,165 +73,7 @@ public class ChPatientResourceProvider implements IResourceProvider { @Override public Class<? extends IBaseResource> getResourceType() { return Patient.class; - } - - /** - * This is the "read" operation. The "@Read" annotation indicates that this method supports the read and/or vread operation. - * <p> - * Read operations take a single parameter annotated with the {@link IdParam} paramater, and should return a single resource instance. - * </p> - * - * @param theId The read operation takes one parameter, which must be of type IdDt and must be annotated with the "@Read.IdParam" annotation. - * @return Returns a resource matching this identifier, or null if none exists. - */ - @Read(version = true) - public Patient readPatient(@IdParam IdType theId) { - - Deque<Patient> retVal; - patientLogger.warn("ReadingPatient " + theId.getIdPart()); - try { - retVal = myIdToPatientVersions.get(theId.getIdPartAsLong()); - } catch (NumberFormatException e) { - /* - * If we can't parse the ID as a long, it's not valid so this is an unknown resource - */ - throw new ResourceNotFoundException(theId); - } - - if (retVal == null) { - throw new ResourceNotFoundException(theId); - } - if (theId.hasVersionIdPart() == false) { - return retVal.getLast(); - } else { - patientLogger.info("Patient found"); - for (Patient nextVersion : retVal) { - String nextVersionId = nextVersion.getIdElement().getVersionIdPart(); - if (theId.getVersionIdPart().equals(nextVersionId)) { - return nextVersion; - } - } - patientLogger.warn("Could not find ressource"); - // No matching version - throw new ResourceNotFoundException("Unknown version: " + theId.getValue()); - } - - } - - /** - * The "@Update" annotation indicates that this method supports replacing an existing - * resource (by ID) with a new instance of that resource. - * - * @param theId This is the ID of the patient to update - * @param thePatient This is the actual resource to save - * @return This method returns a "MethodOutcome" - */ - @Update() - public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient) { - validateResource(thePatient); - - Long id; - try { - id = theId.getIdPartAsLong(); - } catch (DataFormatException e) { - throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric"); - } - - /* - * Throw an exception (HTTP 404) if the ID is not known - */ - if (!myIdToPatientVersions.containsKey(id)) { - throw new ResourceNotFoundException(theId); - } - - addNewVersion(thePatient, id); - - return new MethodOutcome(); - } - - /** - * This method just provides simple business validation for resources we are storing. - * - * @param thePatient The patient to validate - */ - private void validateResource(Patient thePatient) { - /* - * Our server will have a rule that patients must have a family name or we will reject them - */ - if (thePatient.getNameFirstRep().getFamily().isEmpty()) { - OperationOutcome outcome = new OperationOutcome(); - outcome.addIssue().setSeverity(IssueSeverity.FATAL).setDiagnostics("No family name provided, Patient resources must have at least one " + - "family name."); - throw new UnprocessableEntityException(FhirContext.forDstu3(), outcome); - } - } - - /** - * Private Method called to translate a Patient between the model in Patient Registry to the fhir model - * We need to translate a Patient in order to have it in the list of Patient contained in the fhir server - * To later have access to it (through an URL given when searched) - * - * @param patReg : the Patient in the Patient Registry Model - * @return the same Patient translated in the FHIR r4 model. - */ - private Patient translatePatientToR4(net.ihe.gazelle.app.patientregistryapi.business.Patient patReg) { - - Patient translatedPatient = new Patient(); - - translatedPatient.setActive(patReg.isActive()); - List<HumanName> nameList = new ArrayList<>(); - for (PersonName pName : patReg.getNames()) { - HumanName name = new HumanName(); - name.setFamily(pName.getFamily()); - List<StringType> givenNames = new ArrayList<>(); - for (String given : pName.getGivens()) { - givenNames.add(new StringType(given)); - } - name.setGiven(givenNames); - name.addPrefix(pName.getPrefix()); - name.addSuffix(pName.getSuffix()); - name.setUse(NameUse.fromCode(pName.getUse())); - nameList.add(name); - } - translatedPatient.setName(nameList); - translatedPatient.setGender(AdministrativeGender.fromCode(patReg.getGender().toString().toLowerCase())); - List<Address> addresses = new ArrayList<>(); - for (net.ihe.gazelle.app.patientregistryapi.business.Address pAddress : patReg.getAddresses()) { - Address address = new Address(); - address.setUse(AddressUse.fromCode(pAddress.getUse().toString().toLowerCase())); - address.setCity(pAddress.getCity()); - address.setCountry(pAddress.getCountryIso3()); - address.setPostalCode(pAddress.getPostalCode()); - address.setState(pAddress.getState()); - for (String addressLine : pAddress.getLines()) - address.addLine(addressLine); - addresses.add(address); - } - translatedPatient.setAddress(addresses); - translatedPatient.setBirthDate(patReg.getDateOfBirth()); - - return translatedPatient; - - } - - /** - * Method used to generate a Parameter Component representing an error in the the Fhir Resource Model - * Permits to display the error. - * - * @param errorType : the code of the error as a String. - * Will be translated as an OperationOutcome.IssueType - * @return A ParametersParameterComponent representing the http error as a Fhir resource - */ - private ParametersParameterComponent generateErrorReturn(String errorCode) { - OperationOutcome error = new OperationOutcome(); - OperationOutcomeIssueComponent issue = new OperationOutcomeIssueComponent(); - issue.setCode(OperationOutcome.IssueType.fromCode(errorCode)); - error.addIssue(issue); - ParametersParameterComponent errorReturned = new ParametersParameterComponent(); - errorReturned.setResource(error); - return errorReturned; - } - + } private EntityIdentifier createEntityIdentifierFromSourceIdentifier(TokenParam sourceIdentifier) { if (sourceIdentifier == null) { -- GitLab