diff --git a/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/AbstractFhirToXdsMapper.java b/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/AbstractFhirToXdsMapper.java index 1d9bd0d..252ff0a 100644 --- a/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/AbstractFhirToXdsMapper.java +++ b/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/AbstractFhirToXdsMapper.java @@ -1,34 +1,19 @@ package org.openehealth.app.xdstofhir.registry.common.mapper; -import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.urnDecodedScheme; +import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem; +import org.openehealth.app.xdstofhir.registry.common.MappingSupport; +import org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization; +import org.openehealth.ipf.commons.ihe.xds.core.metadata.Person; +import org.openehealth.ipf.commons.ihe.xds.core.metadata.*; +import org.openehealth.ipf.commons.ihe.xds.core.validate.OIDValidator; import java.time.ZoneId; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import org.hl7.fhir.r4.model.BaseDateTimeType; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.ContactPoint; -import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem; -import org.hl7.fhir.r4.model.HumanName; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Practitioner; -import org.hl7.fhir.r4.model.PractitionerRole; -import org.hl7.fhir.r4.model.Reference; -import org.openehealth.app.xdstofhir.registry.common.MappingSupport; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.AssigningAuthority; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Author; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Code; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Identifiable; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.LocalizedString; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Person; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Telecom; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Timestamp; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.XcnName; +import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.urnDecodedScheme; public abstract class AbstractFhirToXdsMapper { @@ -71,7 +56,7 @@ protected Author fromAuthor(Reference author) { doc = (Practitioner) fhirPractRole.getPractitioner().getResource(); xdsAuthor.getAuthorRole().addAll(fhirPractRole.getCode().stream().map(this::fromCodeableConcept).toList()); xdsAuthor.getAuthorSpecialty().addAll(fhirPractRole.getSpecialty().stream().map(this::fromCodeableConcept).toList()); - fromOrganization((org.hl7.fhir.r4.model.Organization)fhirPractRole.getOrganization().getResource()).ifPresent(org -> xdsAuthor.getAuthorInstitution().add(org)); + fromOrganization((org.hl7.fhir.r4.model.Organization) fhirPractRole.getOrganization().getResource()).ifPresent(org -> xdsAuthor.getAuthorInstitution().add(org)); } else if (resource instanceof Practitioner docResource) { doc = docResource; } @@ -105,13 +90,19 @@ protected XcnName mapName(HumanName name) { return xdsName; } - private Optional fromOrganization(org.hl7.fhir.r4.model.Organization fhirAuthOrg){ + private Optional fromOrganization(org.hl7.fhir.r4.model.Organization fhirAuthOrg) { if (fhirAuthOrg != null) { var xdsOrg = new Organization(); var identifierFirstRep = fhirAuthOrg.getIdentifierFirstRep(); if (identifierFirstRep != null && !identifierFirstRep.isEmpty()) { xdsOrg.setIdNumber(identifierFirstRep.getValue()); - xdsOrg.setAssigningAuthority(new AssigningAuthority(urnDecodedScheme(identifierFirstRep.getSystem()))); + + if (identifierFirstRep.getSystem() != null) { + xdsOrg.setAssigningAuthority(new AssigningAuthority(urnDecodedScheme(identifierFirstRep.getSystem()))); + } else { + // without assigning authority, the ID needs to be a valid OID. + new OIDValidator().validate(identifierFirstRep.getValue()); + } } xdsOrg.setOrganizationName(fhirAuthOrg.getName()); return Optional.of(xdsOrg); @@ -120,7 +111,7 @@ private Optional fromOrganization(org.hl7.fhir.r4.model.Organizati } } - private Identifiable fromCodeableConcept(CodeableConcept codeConcept ) { + private Identifiable fromCodeableConcept(CodeableConcept codeConcept) { var id = new Identifiable(); var code = codeConcept.getCodingFirstRep(); if (code.getSystem() != null) { diff --git a/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/AbstractXdsToFhirMapper.java b/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/AbstractXdsToFhirMapper.java index a4b9b90..7c8faad 100644 --- a/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/AbstractXdsToFhirMapper.java +++ b/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/AbstractXdsToFhirMapper.java @@ -22,13 +22,7 @@ import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.StringType; import org.openehealth.app.xdstofhir.registry.common.MappingSupport; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Author; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Code; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Identifiable; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Name; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Telecom; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.Timestamp; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.XDSMetaClass; +import org.openehealth.ipf.commons.ihe.xds.core.metadata.*; public abstract class AbstractXdsToFhirMapper { @@ -51,7 +45,7 @@ protected Identifier fromIdentifier(final String urnIdValue, final Identifier.Id protected Identifier fromIdentifier(final Identifiable id) { var identifier = new Identifier(); - identifier.setSystem(OID_URN + id.getAssigningAuthority().getUniversalId()); + identifier.setSystem(nullSafeConvertToUrn(id.getAssigningAuthority())); identifier.setValue(id.getId()); return identifier; } @@ -77,7 +71,7 @@ protected Coding map(final Code code) { protected Reference fromAuthor(final Author author) { var role = new PractitionerRole(); var doc = new Practitioner(); - if((author.getAuthorPerson() != null) && !author.getAuthorPerson().isEmpty()) { + if (author.getAuthorPerson() != null && !author.getAuthorPerson().isEmpty()) { if (!author.getAuthorPerson().getName().isEmpty()) doc.setName(singletonList(fromName(author.getAuthorPerson().getName()))); if (!author.getAuthorPerson().getId().isEmpty()) @@ -95,7 +89,7 @@ protected Reference fromAuthor(final Author author) { org.setName(xdsAuthorOrg.getOrganizationName()); if (xdsAuthorOrg.getIdNumber() != null) { var identifier = new Identifier(); - identifier.setSystem(OID_URN + xdsAuthorOrg.getAssigningAuthority().getUniversalId()); + identifier.setSystem(nullSafeConvertToUrn(xdsAuthorOrg.getAssigningAuthority())); identifier.setValue(xdsAuthorOrg.getIdNumber()); org.addIdentifier(identifier); } @@ -105,6 +99,10 @@ protected Reference fromAuthor(final Author author) { return reference; } + private static String nullSafeConvertToUrn(AssigningAuthority xdsAuthorOrg) { + return xdsAuthorOrg == null ? null : OID_URN + xdsAuthorOrg.getUniversalId(); + } + protected CodeableConcept convertToCode(final Identifiable id) { var fhirConcept = new CodeableConcept(); Coding codeing; @@ -117,7 +115,7 @@ protected CodeableConcept convertToCode(final Identifiable id) { return fhirConcept; } - protected ContactPoint fromTelecom (final Telecom xdsTelecom) { + protected ContactPoint fromTelecom(final Telecom xdsTelecom) { var cp = new ContactPoint(); if (xdsTelecom.getEmail() != null) { cp.setSystem(ContactPointSystem.EMAIL); diff --git a/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/XdsToFhirDocumentMapper.java b/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/XdsToFhirDocumentMapper.java index a72acb2..8dc4c69 100644 --- a/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/XdsToFhirDocumentMapper.java +++ b/src/main/java/org/openehealth/app/xdstofhir/registry/common/mapper/XdsToFhirDocumentMapper.java @@ -1,37 +1,27 @@ package org.openehealth.app.xdstofhir.registry.common.mapper; -import static java.util.Collections.singletonList; -import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.MHD_COMPREHENSIVE_PROFILE; -import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.toUrnCoded; -import static org.openehealth.ipf.commons.ihe.xds.core.metadata.AvailabilityStatus.APPROVED; - -import java.util.List; -import java.util.function.BiFunction; - +import ca.uhn.hl7v2.model.v25.datatype.XCN; import lombok.RequiredArgsConstructor; import org.hl7.fhir.r4.model.Address; -import org.hl7.fhir.r4.model.Base64BinaryType; -import org.hl7.fhir.r4.model.CanonicalType; -import org.hl7.fhir.r4.model.DateTimeType; -import org.hl7.fhir.r4.model.DateType; -import org.hl7.fhir.r4.model.DocumentReference; +import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.DocumentReference.DocumentReferenceRelatesToComponent; import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; import org.hl7.fhir.r4.model.Enumerations.DocumentReferenceStatus; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Period; -import org.hl7.fhir.r4.model.Practitioner; -import org.hl7.fhir.r4.model.Reference; import org.openehealth.app.xdstofhir.registry.common.MappingSupport; import org.openehealth.app.xdstofhir.registry.common.RegistryConfiguration; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.DocumentEntry; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.PatientInfo; import org.openehealth.ipf.commons.ihe.xds.core.metadata.Person; -import org.openehealth.ipf.commons.ihe.xds.core.metadata.ReferenceId; +import org.openehealth.ipf.commons.ihe.xds.core.metadata.*; import org.openehealth.ipf.commons.map.BidiMappingService; import org.springframework.stereotype.Component; +import java.util.List; +import java.util.function.BiFunction; + +import static java.util.Collections.singletonList; +import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.MHD_COMPREHENSIVE_PROFILE; +import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.toUrnCoded; +import static org.openehealth.ipf.commons.ihe.xds.core.metadata.AvailabilityStatus.APPROVED; + @Component @RequiredArgsConstructor public class XdsToFhirDocumentMapper extends AbstractXdsToFhirMapper @@ -98,10 +88,19 @@ public DocumentReference apply(final DocumentEntry xdsDoc, List name = xdsAuthenticator.getName(); + if (!name.isEmpty()) { + practitioner.setName(singletonList(fromName(name))); + } + Identifiable id = xdsAuthenticator.getId(); + if (!id.isEmpty()) { + practitioner.addIdentifier(fromIdentifier(id)); + } + + authenticator.setResource(practitioner); return authenticator; } diff --git a/src/main/java/org/openehealth/app/xdstofhir/registry/register/RegisterDocumentsProcessor.java b/src/main/java/org/openehealth/app/xdstofhir/registry/register/RegisterDocumentsProcessor.java index ee3f891..62b9479 100644 --- a/src/main/java/org/openehealth/app/xdstofhir/registry/register/RegisterDocumentsProcessor.java +++ b/src/main/java/org/openehealth/app/xdstofhir/registry/register/RegisterDocumentsProcessor.java @@ -394,9 +394,9 @@ private void assignRegistryValues(XDSMetaClass xdsObject, List asso */ private void assignRegistryValues(List associations) { for (var assoc : associations) { - if (!assoc.getEntryUuid().startsWith(MappingSupport.UUID_URN)) { - var previousIdentifier = assoc.getEntryUuid(); + if (assoc.getEntryUuid() == null || !assoc.getEntryUuid().startsWith(MappingSupport.UUID_URN)) { assoc.assignEntryUuid(); + var previousIdentifier = assoc.getEntryUuid(); associations.stream().forEach(as -> { as.setSourceUuid(as.getSourceUuid().replace(previousIdentifier, assoc.getEntryUuid())); as.setTargetUuid(as.getTargetUuid().replace(previousIdentifier, assoc.getEntryUuid())); diff --git a/src/test/java/org/openehealth/app/xdstofhir/registry/common/mapper/DocumentMappingImplTest.java b/src/test/java/org/openehealth/app/xdstofhir/registry/common/mapper/DocumentMappingImplTest.java index 6f2be20..4060a5f 100644 --- a/src/test/java/org/openehealth/app/xdstofhir/registry/common/mapper/DocumentMappingImplTest.java +++ b/src/test/java/org/openehealth/app/xdstofhir/registry/common/mapper/DocumentMappingImplTest.java @@ -1,6 +1,7 @@ package org.openehealth.app.xdstofhir.registry.common.mapper; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; @@ -21,6 +22,8 @@ import org.openehealth.ipf.commons.ihe.xds.core.metadata.AssigningAuthority; 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.Organization; +import org.openehealth.ipf.commons.ihe.xds.core.validate.XDSMetaDataException; import org.openehealth.ipf.commons.spring.map.SpringBidiMappingService; import org.openehealth.ipf.commons.xml.XmlUtils; import org.springframework.core.io.ClassPathResource; @@ -49,6 +52,49 @@ void verifyBirectionalMapping() throws JAXBException { verifyFhirXdsMapping(testDocument); } + @Test + void verifyXcnWithNoIdentifierShallBeMapped() throws JAXBException { + DocumentEntry testDocument = SampleData + .createDocumentEntry(new Identifiable("123", new AssigningAuthority("2.999.3.4"))); + testDocument.setUri(null); + testDocument.getLegalAuthenticator().setId(null); + + verifyFhirXdsMapping(testDocument); + } + + @Test + void verifyXcnWithNoNameShallBeMapped() throws JAXBException { + DocumentEntry testDocument = SampleData + .createDocumentEntry(new Identifiable("123", new AssigningAuthority("2.999.3.4"))); + testDocument.setUri(null); + testDocument.getLegalAuthenticator().setName(null); + + verifyFhirXdsMapping(testDocument); + } + + @Test + void verifyXonWithNoAssigningAuthorityShallBeMapped() throws JAXBException { + DocumentEntry testDocument = SampleData + .createDocumentEntry(new Identifiable("123", new AssigningAuthority("2.999.3.4"))); + testDocument.setUri(null); + Organization organization = testDocument.getAuthors().getFirst().getAuthorInstitution().getFirst(); + organization.setIdNumber("1.2.3"); + organization.setAssigningAuthority(null); + + verifyFhirXdsMapping(testDocument); + } + + @Test + void verifyXonWithNonOidIdentifierAndNoAssigningAuthorityShallNotBeAccepted() { + DocumentEntry testDocument = SampleData + .createDocumentEntry(new Identifiable("123", new AssigningAuthority("2.999.3.4"))); + testDocument.setUri(null); + Organization organization = testDocument.getAuthors().getFirst().getAuthorInstitution().getFirst(); + organization.setIdNumber("112"); + organization.setAssigningAuthority(null); + + assertThrowsExactly(XDSMetaDataException.class, () -> verifyFhirXdsMapping(testDocument)); + } @Test void minimalDocumentMapping() throws JAXBException, IOException {