From 7aa87aa120e2619cc2c6120a1c14e35c13e8d5a3 Mon Sep 17 00:00:00 2001 From: Quentin Ligier Date: Tue, 30 Jul 2024 07:52:37 +0200 Subject: [PATCH] Improve mapping between XDS XON and FHIR Organization Fixes #165 --- docs/changelog.md | 1 + .../mag/mhd/BaseQueryResponseConverter.java | 35 ++++++++++---- .../java/ch/bfh/ti/i4mi/mag/mhd/Utils.java | 48 +++++-------------- .../mag/mhd/iti65/Iti65RequestConverter.java | 28 +++++++---- .../mhd/BaseQueryResponseConverterTest.java | 40 ++++++++++++++++ .../mhd/iti65/Iti65RequestConverterTest.java | 35 ++++++++++++++ 6 files changed, 134 insertions(+), 53 deletions(-) create mode 100644 src/test/java/ch/bfh/ti/i4mi/mag/mhd/BaseQueryResponseConverterTest.java create mode 100644 src/test/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RequestConverterTest.java diff --git a/docs/changelog.md b/docs/changelog.md index 392916cd..69870156 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,7 @@ ## Unreleased - Fixed an NPE in the assertion route +- Improved mapping between XDS XON and FHIR Organization [165](https://github.com/i4mi/MobileAccessGateway/issues/165) ## 2024/05/15 v070 - support for multiple IDP's [128](https://github.com/i4mi/MobileAccessGateway/issues/128) diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/BaseQueryResponseConverter.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/BaseQueryResponseConverter.java index 74f24590..e4703621 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/BaseQueryResponseConverter.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/BaseQueryResponseConverter.java @@ -56,6 +56,9 @@ import ch.bfh.ti.i4mi.mag.Config; import ch.bfh.ti.i4mi.mag.pmir.PatientReferenceCreator; +import static ch.bfh.ti.i4mi.mag.mhd.Utils.isUnprefixedOid; +import static ch.bfh.ti.i4mi.mag.mhd.Utils.isUnprefixedUuid; + /** * base query response converter XDS to MHD * @author alexander kreutz @@ -236,21 +239,35 @@ public Patient transformPatient(Person person) { } /** - * XDS Organization -> FHIR Organization + * IPF Organization (XDS XON) -> FHIR Organization * @param org * @return */ public Organization transform(org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization org) { - if (org == null) return null; - Organization result = new Organization(); + if (org == null) { + return null; + } + final var result = new Organization(); result.setName(org.getOrganizationName()); - String id = org.getIdNumber(); - // TODO handle system not given + String id = org.getIdNumber(); + if (isUnprefixedOid(id)) { + id = "urn:oid:" + id; + } else if (isUnprefixedUuid(id)) { + id = "urn:uuid:" + id; + } - if (org.getAssigningAuthority()!=null) { - String system = org.getAssigningAuthority().getUniversalId(); - result.addIdentifier().setSystem("urn:oid:"+system).setValue(id); - } else result.addIdentifier().setValue(id); + if (org.getAssigningAuthority() != null) { + String system = org.getAssigningAuthority().getUniversalId(); + if (isUnprefixedOid(system)) { + system = "urn:oid:" + system; + } + result.addIdentifier().setSystem(system).setValue(id); + } else { + final var identifier = result.addIdentifier().setValue(id); + if (id.startsWith("urn:")) { + identifier.setSystem("urn:ietf:rfc:3986"); + } + } return result; } diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/Utils.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/Utils.java index ef218f42..256eab84 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/Utils.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/Utils.java @@ -15,48 +15,15 @@ * limitations under the License. */ -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; -import org.apache.camel.Body; import org.apache.camel.Headers; import org.apache.camel.Processor; import org.apache.camel.http.common.HttpMessage; -import org.hl7.fhir.r4.model.Patient; import org.openehealth.ipf.commons.ihe.fhir.Constants; import org.openehealth.ipf.commons.ihe.fhir.FhirSearchParameters; -import org.openehealth.ipf.commons.ihe.fhir.iti66.Iti66SearchParameters; -import org.openehealth.ipf.commons.ihe.fhir.iti67.Iti67SearchParameters; -import org.openehealth.ipf.commons.ihe.xds.core.ebxml.ebxml30.RetrieveDocumentSetResponseType; -import org.openehealth.ipf.commons.ihe.xds.core.ebxml.ebxml30.RetrieveDocumentSetResponseType.DocumentResponse; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.AssigningAuthority; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.AvailabilityStatus; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Code; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.DocumentEntry; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Identifiable; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.ReferenceId; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Timestamp; -import org.openehealth.ipf.commons.ihe.xds.core.requests.QueryRegistry; -import org.openehealth.ipf.commons.ihe.xds.core.requests.RetrieveDocumentSet; -import org.openehealth.ipf.commons.ihe.xds.core.requests.query.FindDocumentsByReferenceIdQuery; -import org.openehealth.ipf.commons.ihe.xds.core.requests.query.FindDocumentsQuery; -import org.openehealth.ipf.commons.ihe.xds.core.requests.query.FindSubmissionSetsQuery; -import org.openehealth.ipf.commons.ihe.xds.core.requests.query.QueryList; -import org.openehealth.ipf.commons.ihe.xds.core.requests.query.QueryReturnType; -import org.openehealth.ipf.commons.ihe.xds.core.responses.RetrievedDocumentSet; - -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.param.DateParam; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ch.bfh.ti.i4mi.mag.Config; /** * utility classes for Mobile Access Gateway @@ -65,7 +32,10 @@ */ public class Utils { - public static final String KEPT_BODY = "KeptBody"; + public static final String KEPT_BODY = "KeptBody"; + + public static final Pattern UNPREFIXED_OID_PATTERN = Pattern.compile("\\d+(\\.\\d+)+"); + public static final Pattern UNPREFIXED_UUID_PATTERN = Pattern.compile("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"); public static FhirSearchParameters searchParameterToBody(@Headers Map parameters) { FhirSearchParameters searchParameter = (FhirSearchParameters) parameters @@ -141,4 +111,12 @@ public static Processor retrievedDocumentSetToHttResponse() { }; } */ + + public static boolean isUnprefixedOid(final String oid) { + return oid != null && UNPREFIXED_OID_PATTERN.matcher(oid).matches(); + } + + public static boolean isUnprefixedUuid(final String uuid) { + return uuid != null && UNPREFIXED_UUID_PATTERN.matcher(uuid).matches(); + } } diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RequestConverter.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RequestConverter.java index 3e941f03..41ef91d9 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RequestConverter.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RequestConverter.java @@ -1000,20 +1000,30 @@ public Telecom transform(ContactPoint contactPoint) { } /** - * FHIR Organization -> XDS Organization + * FHIR Organization -> IPF Organization (XDS XON) * @param org * @return */ public org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization transform(Organization org) { - org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization result = new org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization(); + final var result = new org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization(); result.setOrganizationName(org.getName()); - Identifier identifier = org.getIdentifierFirstRep(); - if (identifier != null) { - result.setIdNumber(identifier.getValue()); - result.setAssigningAuthority(new AssigningAuthority(noPrefix(identifier.getSystem()))); - } - return result; - } + final Identifier identifier = org.getIdentifierFirstRep(); + if (identifier != null && identifier.hasValue()) { + if (identifier.hasSystem()) { + if (identifier.getSystem().equals("urn:ietf:rfc:3986")) { + // The value is a URI, remove the prefix and ignore the system + result.setIdNumber(noPrefix(identifier.getValue())); + } else { + // The value is an identifier, use the system as assigning authority + result.setIdNumber(identifier.getValue()); + result.setAssigningAuthority(new AssigningAuthority(noPrefix(identifier.getSystem()))); + } + } else { + result.setIdNumber(identifier.getValue()); + } + } + return result; + } /** * FHIR Reference to Author -> XDS Author diff --git a/src/test/java/ch/bfh/ti/i4mi/mag/mhd/BaseQueryResponseConverterTest.java b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/BaseQueryResponseConverterTest.java new file mode 100644 index 00000000..66652a06 --- /dev/null +++ b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/BaseQueryResponseConverterTest.java @@ -0,0 +1,40 @@ +package ch.bfh.ti.i4mi.mag.mhd; + +import ch.bfh.ti.i4mi.mag.Config; +import ch.bfh.ti.i4mi.mag.mhd.iti67.Iti67ResponseConverter; +import org.junit.jupiter.api.Test; +import org.openehealth.ipf.commons.ihe.xds.core.metadata.Hl7v2Based; +import org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for {@link BaseQueryResponseConverter}. + * + * @author Quentin Ligier + **/ +class BaseQueryResponseConverterTest { + + @Test + void testTransformOrganization() { + final var converter = new Iti67ResponseConverter(new Config()); + + var fhirOrg = converter.transform(Hl7v2Based.parse("Test^^^^^^^^^1234", Organization.class)); + assertEquals("Test", fhirOrg.getName()); + assertEquals(1, fhirOrg.getIdentifier().size()); + assertEquals("1234", fhirOrg.getIdentifierFirstRep().getValue()); + assertFalse(fhirOrg.getIdentifierFirstRep().hasSystem()); + + fhirOrg = converter.transform(Hl7v2Based.parse("Test^^^^^&1.2.3&ISO^^^^1234", Organization.class)); + assertEquals("Test", fhirOrg.getName()); + assertEquals(1, fhirOrg.getIdentifier().size()); + assertEquals("1234", fhirOrg.getIdentifierFirstRep().getValue()); + assertEquals("urn:oid:1.2.3", fhirOrg.getIdentifierFirstRep().getSystem()); + + fhirOrg = converter.transform(Hl7v2Based.parse("Test^^^^^^^^^1.2.3", Organization.class)); + assertEquals("Test", fhirOrg.getName()); + assertEquals(1, fhirOrg.getIdentifier().size()); + assertEquals("urn:oid:1.2.3", fhirOrg.getIdentifierFirstRep().getValue()); + assertEquals("urn:ietf:rfc:3986", fhirOrg.getIdentifierFirstRep().getSystem()); + } +} \ No newline at end of file diff --git a/src/test/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RequestConverterTest.java b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RequestConverterTest.java new file mode 100644 index 00000000..aed74095 --- /dev/null +++ b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RequestConverterTest.java @@ -0,0 +1,35 @@ +package ch.bfh.ti.i4mi.mag.mhd.iti65; + +import org.hl7.fhir.r4.model.Organization; +import org.junit.jupiter.api.Test; +import org.openehealth.ipf.commons.ihe.xds.core.metadata.Hl7v2Based; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for {@link Iti65RequestConverter}. + * + * @author Quentin Ligier + **/ +class Iti65RequestConverterTest { + + @Test + void testTransformOrganization() { + final var iti65RequestConverter = new Iti65RequestConverter(); + + final var org = new Organization(); + org.setName("Test"); + org.addIdentifier().setValue("1234"); + + var xon = iti65RequestConverter.transform(org); + assertEquals("Test^^^^^^^^^1234", Hl7v2Based.render(xon)); + + org.getIdentifierFirstRep().setSystem("urn:oid:1.2.3"); + xon = iti65RequestConverter.transform(org); + assertEquals("Test^^^^^&1.2.3&ISO^^^^1234", Hl7v2Based.render(xon)); + + org.getIdentifierFirstRep().setValue("urn:oid:1.2.3").setSystem("urn:ietf:rfc:3986"); + xon = iti65RequestConverter.transform(org); + assertEquals("Test^^^^^^^^^1.2.3", Hl7v2Based.render(xon)); + } +} \ No newline at end of file