Skip to content

Commit

Permalink
#88: fixes to pixm and pdqm implementing changes to final supplement
Browse files Browse the repository at this point in the history
  • Loading branch information
ohr authored and unixoid committed Jun 25, 2016
1 parent 0602361 commit 1adc717
Show file tree
Hide file tree
Showing 16 changed files with 79 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@

package org.openehealth.ipf.commons.ihe.fhir

import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException
import ca.uhn.fhir.rest.server.exceptions.*
import org.hl7.fhir.instance.model.OperationOutcome
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormatter
import org.joda.time.format.ISODateTimeFormat
import org.openehealth.ipf.commons.core.URN
import org.openehealth.ipf.commons.ihe.fhir.translation.UriMapper
import org.openehealth.ipf.modules.hl7.dsl.Repeatable

Expand Down Expand Up @@ -72,15 +69,37 @@ class Utils {

}

// PIXm, Error Case 3
static BaseServerResponseException unknownPatientId() {
OperationOutcome oo = new OperationOutcome()
oo.addIssue()
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
.setCode(OperationOutcome.IssueType.VALUE)
.setCode(OperationOutcome.IssueType.NOTFOUND)
.setDiagnostics('sourceIdentifier identity not found')
return new InvalidRequestException('Unknown Patient ID', oo)
return new ResourceNotFoundException('Unknown Patient ID', oo)
}

// PIXm, Error Case 4
static BaseServerResponseException unknownSourceDomainCode(String domain = null) {
OperationOutcome oo = new OperationOutcome()
oo.addIssue()
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
.setCode(OperationOutcome.IssueType.CODEINVALID)
.setDiagnostics("sourceIdentifier assigning authority ${domain ?: ''} not found")
return new InvalidRequestException("Unknown Identifier Domain ${domain ?: ''}", oo)
}

// PIXm, Error Case 5
static BaseServerResponseException unknownTargetDomainCode(String domain = null) {
OperationOutcome oo = new OperationOutcome()
oo.addIssue()
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
.setCode(OperationOutcome.IssueType.CODEINVALID)
.setDiagnostics("targetSystem ${domain ?: ''} not found")
return new ForbiddenOperationException("Unknown Target Domain ${domain ?: ''}", oo)
}

// PDQm, Error Case ?
static BaseServerResponseException unknownPatientDomain(String domain = null) {
OperationOutcome oo = new OperationOutcome()
oo.addIssue()
Expand All @@ -90,13 +109,14 @@ class Utils {
return new InvalidRequestException("Unknown Patient Domain ${domain ?: ''}", oo)
}

static BaseServerResponseException unknownTargetDomain(String domain = null) {
// PDQm, Error Case 4
static BaseServerResponseException unknownTargetDomainValue(String domain = null) {
OperationOutcome oo = new OperationOutcome()
oo.addIssue()
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
.setCode(OperationOutcome.IssueType.NOTFOUND)
.setCode(OperationOutcome.IssueType.VALUE)
.setDiagnostics("targetSystem ${domain ?: ''} not found")
return new InvalidRequestException("Unknown Target Domain ${domain ?: ''}", oo)
return new ResourceNotFoundException("Unknown Target Domain ${domain ?: ''}", oo)
}

static BaseServerResponseException unexpectedProblem() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ class PdqResponseToPdqmResponseTranslator implements TranslatorHL7v2ToFhir {
switch (ackCode) {
case 'OK': return returnBundle ?
handleRegularSearchResponse(message.QUERY_RESPONSE()) :
handleRegularResource(message.QUERY_RESPONSE()) // Case 1
case 'NF': return handleRegularSearchResponse(null) // Case 2 TODO check + handle non-existent resource ID
case 'AE': return handleErrorResponse(message) // Cases 3-5
handleRegularResource(message.QUERY_RESPONSE()) // Case 1,2
case 'NF': return handleRegularSearchResponse(null) // Case 3
case 'AE': return handleErrorResponse(message) // Cases 3-5
default: throw new InternalErrorException("Unexpected ack code " + ackCode)
}
}
Expand Down Expand Up @@ -230,7 +230,7 @@ class PdqResponseToPdqmResponseTranslator implements TranslatorHL7v2ToFhir {
// Check error locations
int errorField = message.ERR[2][3]?.value ? Integer.parseInt(message.ERR[2][3]?.value) : 0
if (errorField == 8) {
throw Utils.unknownTargetDomain()
throw Utils.unknownTargetDomainValue()
} else {
throw Utils.unexpectedProblem()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,19 +160,11 @@ class PdqmRequestToPdqQueryTranslator implements TranslatorFhirToHL7v2 {
// Patient identifier has identifier value
List<String> searchIdentifier = identifiers?.find { it.size() == 3 && !it[2]?.empty }
if (searchIdentifier) (identifierNamespace, identifierOid, identifierValue) = searchIdentifier
// The following is not present in the final spec version, but clients may still use this:
// Requested domains have no identifier value. If the resource identifier system is not included here,
// add it because otherwise we don't know the resource ID in the response.
requestedDomains = identifiers?.findAll { it.size() == 2 || (!it[2]) }.collect { it[1] }
}

// Last-minute change: use targetSystem parameter to request dedicated domains
UriAndListParam targetSystems = map.get(Constants.TARGET_SYSTEM_NAME)
if (targetSystems) {
List<List<String>> targets = searchUriList(targetSystems)
requestedDomains = targets.collect { it[1] }
}

// If requestedDomains is set but the pdqSupplierResourceIdentifierOid is not part of it, add it.
// Otherwise no MPI patient ID is returned and we cannot generate a resource ID from it!
if (requestedDomains && (!requestedDomains.contains(pdqSupplierResourceIdentifierOid)))
Expand Down Expand Up @@ -257,7 +249,7 @@ class PdqmRequestToPdqQueryTranslator implements TranslatorFhirToHL7v2 {
if (!(namespace || oid)) {
throw identifierParam.value ?
Utils.unknownPatientDomain(identifierParam.system) :
Utils.unknownTargetDomain(identifierParam.system)
Utils.unknownTargetDomainValue(identifierParam.system)
}
}
[namespace, oid, identifierParam?.value]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,18 @@ class PixQueryResponseToPixmResponseTranslator implements TranslatorHL7v2ToFhir
private Parameters handleErrorResponse(RSP_K23 message) {

// Check error locations
OperationOutcome oo = new OperationOutcome()
int errorField = message.ERR[2][3]?.value ? Integer.parseInt(message.ERR[2][3]?.value) : 0
int errorComponent = message.ERR[2][5]?.value ? Integer.parseInt(message.ERR[2][5]?.value) : 0

if (errorField == 3 && errorComponent == 1) {
// Case 3: Patient ID not found, maybe return empty response instead
return handleEmptyResponse()
// Case 3: Patient ID not found
throw Utils.unknownPatientId()
} else if (errorField == 3 && errorComponent == 4) {
// Case 4: Unknown Patient Domain
throw Utils.unknownPatientDomain()
throw Utils.unknownSourceDomainCode()
} else if (errorField == 4) {
// Case 5: Unknown Target Domain
throw Utils.unknownTargetDomain()
throw Utils.unknownTargetDomainCode()
} else {
throw Utils.unexpectedProblem();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,19 @@ class PixmRequestToPixQueryTranslator implements TranslatorFhirToHL7v2 {

Identifier sourceIdentifier = map[Constants.SOURCE_IDENTIFIER_NAME]
if (!sourceIdentifier.value) {
// No value provided? Patient cannot be found
// No value provided? Patient cannot be found, Error Case 3
throw Utils.unknownPatientId();
}
if (!Utils.populateIdentifier(qry.QPD[3], uriMapper, sourceIdentifier.system, sourceIdentifier.value)) {
// UriMapper is not able to derive a PIX OID/Namespace for the patient domain URI
throw Utils.unknownPatientDomain(sourceIdentifier.system);
// UriMapper is not able to derive a PIX OID/Namespace for the patient domain URI, Error Case 4
throw Utils.unknownSourceDomainCode(sourceIdentifier.system);
}

UriType requestedDomain = map[Constants.TARGET_SYSTEM_NAME]
if (requestedDomain) {
if (!Utils.populateIdentifier(Utils.nextRepetition(qry.QPD[4]), uriMapper, requestedDomain.value)) {
// UriMapper is not able to derive a PIX OID/Namespace for the target domain URI
throw Utils.unknownTargetDomain(requestedDomain.value);
// UriMapper is not able to derive a PIX OID/Namespace for the target domain URI, Error Case 5
throw Utils.unknownTargetDomainCode(requestedDomain.value);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public class Iti78ResourceProvider extends AbstractPlainProvider {
@Search(type = PdqPatient.class)
public IBundleProvider pdqmSearch(
@OptionalParam(name = Patient.SP_IDENTIFIER) TokenAndListParam identifiers,
@OptionalParam(name = Constants.TARGET_SYSTEM_NAME) UriAndListParam targetSystems,
@OptionalParam(name = Patient.SP_FAMILY) StringAndListParam family,
@OptionalParam(name = Patient.SP_GIVEN) StringAndListParam given,
@OptionalParam(name = Patient.SP_BIRTHDATE) DateParam birthDate,
Expand All @@ -82,7 +81,6 @@ public IBundleProvider pdqmSearch(

Map<String, Object> searchParameters = new HashMap<>();
addParameter(searchParameters, Patient.SP_IDENTIFIER, identifiers);
addParameter(searchParameters, Constants.TARGET_SYSTEM_NAME, targetSystems);
addParameter(searchParameters, Patient.SP_FAMILY, family);
addParameter(searchParameters, Patient.SP_GIVEN, given);
addParameter(searchParameters, Patient.SP_BIRTHDATE, birthDate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
*/
public class Iti81ClientRequestFactory extends QueryClientRequestFactory {

public Iti81ClientRequestFactory(Class<? extends IBaseResource> type) {
public Iti81ClientRequestFactory() {
super(AuditEvent.class);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.UriParam;
import org.hl7.fhir.instance.model.*;
import org.openehealth.ipf.commons.ihe.fhir.AbstractPlainProvider;

Expand All @@ -38,28 +40,27 @@ public class Iti83ResourceProvider extends AbstractPlainProvider {
/**
* Handles the PIXm Query
*
* @param sourceIdentifierString Identifier to search for. Should be an {@link Identifier}, but obviously
* non-primitive types are forbidden in GET operations
* @param targetSystem target system URI
* @param sourceIdentifierToken Identifier to search for. Should be an {@link Identifier}, but obviously
* non-primitive types are forbidden in GET operations
* @param targetSystem target system URI
* @return {@link Parameters} containing found identifiers
*/
@SuppressWarnings("unused")
@Operation(name = PIXM_OPERATION_NAME, type = Patient.class, idempotent = true, returnParameters = { @OperationParam(name = "return", type = Identifier.class, max = 100)})
@Operation(name = PIXM_OPERATION_NAME, type = Patient.class, idempotent = true, returnParameters = {@OperationParam(name = "return", type = Identifier.class, max = 100)})
public Parameters pixmQuery(
@OperationParam(name = SOURCE_IDENTIFIER_NAME) StringType sourceIdentifierString,
@OperationParam(name = TARGET_SYSTEM_NAME) UriType targetSystem,
@OperationParam(name = SOURCE_IDENTIFIER_NAME) TokenParam sourceIdentifierToken,
@OperationParam(name = TARGET_SYSTEM_NAME) UriParam targetSystem,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {

// Split up the primitive String into an {@link Identifier}
String[] parts = sourceIdentifierString.getValueAsString().split("\\|");
Identifier sourceIdentifier = new Identifier()
.setSystem(parts.length > 0 ? parts[0] : null)
.setValue(parts.length > 1 ? parts[1] : null);
.setSystem(sourceIdentifierToken.getSystem())
.setValue(sourceIdentifierToken.getValue());

Parameters inParams = new Parameters();
inParams.addParameter().setName(SOURCE_IDENTIFIER_NAME).setValue(sourceIdentifier);
inParams.addParameter().setName(TARGET_SYSTEM_NAME).setValue(targetSystem);
inParams.addParameter().setName(TARGET_SYSTEM_NAME).setValue(new UriType(targetSystem.getValue()));

// Run down the route
return requestResource(inParams, Parameters.class, httpServletRequest, httpServletResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.openehealth.ipf.commons.ihe.fhir.iti83

import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException
import ca.uhn.hl7v2.HapiContext
import org.apache.commons.io.IOUtils
import org.hl7.fhir.instance.model.Identifier
Expand Down Expand Up @@ -82,11 +83,10 @@ class PixQueryResponseToPixmResponseTranslatorTest extends Assert {
assertEquals(0, parameters.parameter.size())
}

@Test
@Test(expected = ResourceNotFoundException)
public void testTranslateErrorResponseCase3() {
RSP_K23 message = loadMessage('err-1_Response')
Parameters parameters = translator.translateHL7v2ToFhir(message, new HashMap<String, Object>())
assertEquals(0, parameters.parameter.size())
translator.translateHL7v2ToFhir(message, new HashMap<String, Object>())
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import org.hl7.fhir.instance.model.OperationOutcome;
import org.openehealth.ipf.commons.ihe.core.atna.MockedSender;
import org.openehealth.ipf.platform.camel.ihe.ws.StandardTestContainer;
Expand All @@ -42,8 +42,7 @@ protected static IGenericClient startClient(String base) {
return client;
}

protected void assertAndRethrowException(InvalidRequestException e, OperationOutcome.IssueType expectedIssue) {
assertEquals(400, e.getStatusCode());
protected void assertAndRethrowException(BaseServerResponseException e, OperationOutcome.IssueType expectedIssue) {
// Hmm, I wonder if this could not be done automatically...
OperationOutcome oo = context.newXmlParser().parseResource(OperationOutcome.class, e.getResponseBody());
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void testSendManualPdqm() {

PdqPatient p = (PdqPatient)result.getEntry().get(0).getResource();
assertEquals("Test", p.getName().get(0).getFamily().get(0).getValue());
assertEquals("Patient/4711", p.getId());
assertEquals("http://localhost:8999/Patient/4711", p.getId());


// Check ATNA Audit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.openehealth.ipf.platform.camel.ihe.fhir.iti78;

import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.instance.model.OperationOutcome;
import org.junit.BeforeClass;
import org.junit.Test;
Expand All @@ -35,12 +36,12 @@ public static void setUpClass() throws ServletException {
startServer(CONTEXT_DESCRIPTOR);
}

@Test(expected = InvalidRequestException.class)
@Test(expected = ResourceNotFoundException.class)
public void testSendManualPixm() {
try {
sendManually(familyParameters());
} catch (InvalidRequestException e) {
assertAndRethrowException(e, OperationOutcome.IssueType.NOTFOUND);
} catch (ResourceNotFoundException e) {
assertAndRethrowException(e, OperationOutcome.IssueType.VALUE);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@

package org.openehealth.ipf.platform.camel.ihe.fhir.iti83;

import org.hl7.fhir.instance.model.Parameters;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.instance.model.OperationOutcome;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.servlet.ServletException;

import static org.junit.Assert.assertTrue;

/**
*
*/
Expand All @@ -36,10 +35,13 @@ public static void setUpClass() throws ServletException {
startServer(CONTEXT_DESCRIPTOR);
}

@Test
@Test(expected = ResourceNotFoundException.class)
public void testSendManualPixm() {
Parameters result = sendManually(validQueryParameters());
assertTrue(result.getParameter().isEmpty());
try {
sendManually(validQueryParameters());
} catch (ResourceNotFoundException e) {
assertAndRethrowException(e, OperationOutcome.IssueType.NOTFOUND);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void testSendManualPixm() {
try {
sendManually(validQueryParameters());
} catch (InvalidRequestException e) {
assertAndRethrowException(e, OperationOutcome.IssueType.NOTFOUND);
assertAndRethrowException(e, OperationOutcome.IssueType.CODEINVALID);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package org.openehealth.ipf.platform.camel.ihe.fhir.iti83;

import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import org.hl7.fhir.instance.model.OperationOutcome;
import org.junit.BeforeClass;
import org.junit.Test;
Expand All @@ -35,12 +35,12 @@ public static void setUpClass() throws ServletException {
startServer(CONTEXT_DESCRIPTOR);
}

@Test(expected = InvalidRequestException.class)
@Test(expected = ForbiddenOperationException.class)
public void testSendManualPixm() {
try {
sendManually(validQueryParameters());
} catch (InvalidRequestException e) {
assertAndRethrowException(e, OperationOutcome.IssueType.NOTFOUND);
} catch (ForbiddenOperationException e) {
assertAndRethrowException(e, OperationOutcome.IssueType.CODEINVALID);
}

}
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
<groovy-version>2.4.6</groovy-version>
<guava-version>19.0</guava-version>
<hapi-version>2.2</hapi-version>
<hapi-fhir-version>1.4</hapi-fhir-version>
<hapi-fhir-version>1.5</hapi-fhir-version>
<hibernate-version>4.3.9.Final</hibernate-version>
<hibernate-commons-annotations-version>4.0.5.Final</hibernate-commons-annotations-version>
<hibernate-jpa-2.1-api-version>1.0.0.Final</hibernate-jpa-2.1-api-version>
Expand Down

0 comments on commit 1adc717

Please sign in to comment.