From c57747e9fd92a0008c0b3f0eef51d868ac38a754 Mon Sep 17 00:00:00 2001 From: Dmytro Rud Date: Thu, 11 Jul 2024 12:33:40 +0200 Subject: [PATCH] deletion of single DocumentReferences over HTTP method DELETE --- pom.xml | 7 + .../java/ch/bfh/ti/i4mi/mag/MagConstants.java | 8 + .../mag/mhd/iti67/IdRequestConverter.java | 28 +-- .../Iti67FromIti57ResponseConverter.java | 4 + .../iti67/Iti67RequestUpdateConverter.java | 199 +++++++++--------- .../mag/mhd/iti67/Iti67ResponseConverter.java | 4 + .../i4mi/mag/mhd/iti67/Iti67RouteBuilder.java | 108 +++++++--- .../iti67_v401/Iti67ResourceProvider.java | 22 +- .../mag/mhd/Iti57RequestTranslatorTest.java | 2 +- .../java/ch/bfh/ti/i4mi/mag/mhd/MhdTest.java | 26 ++- .../ti/i4mi/mag/mhd/MhdTestRouteBuilder.java | 70 +++++- 11 files changed, 318 insertions(+), 160 deletions(-) diff --git a/pom.xml b/pom.xml index 963665b2..a0a71868 100644 --- a/pom.xml +++ b/pom.xml @@ -314,6 +314,13 @@ ${camel-version} test + + org.openehealth.ipf.commons + ipf-commons-ihe-xds + ${ipf-version} + test-jar + test + diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/MagConstants.java b/src/main/java/ch/bfh/ti/i4mi/mag/MagConstants.java index 82b94341..7eb5e2e4 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/MagConstants.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/MagConstants.java @@ -34,4 +34,12 @@ public static class FhirCodingSystemIds { public static final String MHD_DOCUMENT_ID_TYPE = "https://profiles.ihe.net/ITI/MHD/CodeSystem/DocumentIdentifierTypes"; } + @UtilityClass + public static class DeletionStatuses { + private static final String PREFIX = "urn:e-health-suisse:2019:deletionStatus:"; + public static final String NOT_REQUESTED = PREFIX + "deletionNotRequested"; + public static final String REQUESTED = PREFIX + "deletionRequested"; + public static final String PROHIBITED = PREFIX + "deletionProhibited"; + } + } diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/IdRequestConverter.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/IdRequestConverter.java index 51f60417..6ad9bc83 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/IdRequestConverter.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/IdRequestConverter.java @@ -16,47 +16,41 @@ package ch.bfh.ti.i4mi.mag.mhd.iti67; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - +import ch.bfh.ti.i4mi.mag.BaseRequestConverter; import org.apache.camel.Header; import org.openehealth.ipf.commons.ihe.xds.core.requests.QueryRegistry; import org.openehealth.ipf.commons.ihe.xds.core.requests.query.GetDocumentsQuery; import org.openehealth.ipf.commons.ihe.xds.core.requests.query.QueryReturnType; -import ch.bfh.ti.i4mi.mag.BaseRequestConverter; +import java.util.Collections; /** * ITI-67 to ITI-18 request converter - * - * @author oliver egger * + * @author oliver egger */ public class IdRequestConverter extends BaseRequestConverter { /** * convert ITI-67 request to ITI-18 request - * - * @param searchParameter - * @return */ public QueryRegistry idToGetDocumentsQuery(@Header(value = "FhirHttpUri") String fhirHttpUri) { if (fhirHttpUri != null && fhirHttpUri.contains("/")) { boolean getLeafClass = true; - String uuid = fhirHttpUri.substring(fhirHttpUri.lastIndexOf("/") + 1); - if (!uuid.startsWith("urn:uuid:")) { - uuid = "urn:uuid:"+uuid; - } - + GetDocumentsQuery query = new GetDocumentsQuery(); - final QueryRegistry queryRegistry = new QueryRegistry(query); - query.setUuids(Collections.singletonList(uuid)); + final QueryRegistry queryRegistry = new QueryRegistry(query); + query.setLogicalUuid(Collections.singletonList(extractId(fhirHttpUri))); queryRegistry.setReturnType((getLeafClass) ? QueryReturnType.LEAF_CLASS : QueryReturnType.OBJECT_REF); return queryRegistry; } return null; + } + public static String extractId(String fhirHttpUri) { + String uuid = fhirHttpUri.substring(fhirHttpUri.lastIndexOf("/") + 1); + return uuid.startsWith("urn:uuid:") ? uuid : "urn:uuid:" + uuid; } + } diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67FromIti57ResponseConverter.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67FromIti57ResponseConverter.java index 31e463ab..1d2f5f1f 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67FromIti57ResponseConverter.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67FromIti57ResponseConverter.java @@ -22,13 +22,17 @@ import org.openehealth.ipf.commons.ihe.fhir.translation.ToFhirTranslator; import org.openehealth.ipf.commons.ihe.xds.core.responses.Response; import org.openehealth.ipf.commons.ihe.xds.core.responses.Status; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import java.util.Map; +@Component public class Iti67FromIti57ResponseConverter extends BaseResponseConverter implements ToFhirTranslator { private Config config; + @Autowired public Iti67FromIti57ResponseConverter(final Config config) { this.config = config; } diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RequestUpdateConverter.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RequestUpdateConverter.java index b5223acd..0c16f34a 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RequestUpdateConverter.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RequestUpdateConverter.java @@ -1,4 +1,3 @@ -package ch.bfh.ti.i4mi.mag.mhd.iti67; /* * Copyright 2020 the original author or authors. * @@ -14,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package ch.bfh.ti.i4mi.mag.mhd.iti67; import java.time.ZonedDateTime; import java.util.HashMap; @@ -21,7 +21,6 @@ import ch.bfh.ti.i4mi.mag.Config; import ch.bfh.ti.i4mi.mag.MagConstants; -import org.apache.camel.Body; import org.hl7.fhir.r4.model.*; import org.openehealth.ipf.commons.core.URN; import org.openehealth.ipf.commons.ihe.xds.core.metadata.*; @@ -44,112 +43,124 @@ @Component public class Iti67RequestUpdateConverter extends Iti65RequestConverter { - private final Config config; - - @Autowired - public Iti67RequestUpdateConverter(Config config) { - this.config = config; - } - - /** - * ITI-67 Response to ITI-57 request converter - * - * @param searchParameter - * @return - */ - public SubmitObjectsRequest convertDocumentReferenceToDocumentEntry(@Body DocumentReference documentReference) { - - SubmissionSet submissionSet = new SubmissionSet(); - submissionSet.setSubmissionTime(new Timestamp(ZonedDateTime.now(), Timestamp.Precision.SECOND)); - - Extension source = - getExtensionByUrl(documentReference, "https://profiles.ihe.net/ITI/MHD/StructureDefinition/ihe-sourceId"); - if (source != null && source.getValue() instanceof Identifier) { - submissionSet.setSourceId(noPrefix(((Identifier) source.getValue()).getValue())); - } else { - submissionSet.setSourceId(noPrefix(config.getDocumentSourceId())); - } + private final Config config; - Extension designationType = - getExtensionByUrl(documentReference, "https://profiles.ihe.net/ITI/MHD/StructureDefinition/ihe-designationType"); - if (designationType != null && designationType.getValue() instanceof CodeableConcept) { - submissionSet.setContentTypeCode(transformCodeableConcept((CodeableConcept) designationType.getValue())); - } else { - submissionSet.setContentTypeCode(new Code("71388002", new LocalizedString("Procedure"), "2.16.840.1.113883.6.96")); + @Autowired + public Iti67RequestUpdateConverter(Config config) { + this.config = config; } - Extension authorRoleExt = - getExtensionByUrl(documentReference, MagConstants.FhirExtensionUrls.CH_AUTHOR_ROLE); - if (authorRoleExt != null) { - Identifiable identifiable = null; - if (authorRoleExt != null) { - Coding coding = authorRoleExt.castToCoding(authorRoleExt.getValue()); - if (coding != null) { - identifiable = new Identifiable(coding.getCode(), new AssigningAuthority(noPrefix(coding.getSystem()))); - } - } - submissionSet.setAuthor(transformAuthor(null, null, identifiable)); + /** + * ITI-67 Response to ITI-57 request converter + */ + public SubmitObjectsRequest createMetadataUpdateRequest(DocumentReference documentReference) { + DocumentEntry documentEntry = translateDocumentMetadata(documentReference); + SubmissionSet submissionSet = createSubmissionSet(); + enrichSubmissionSet(submissionSet, documentReference); + return createMetadataUpdateRequest(submissionSet, documentEntry); } - RegisterDocumentSetBuilder builder = new RegisterDocumentSetBuilder(true, submissionSet); // TODO should be - // true? - DocumentEntry entry = new DocumentEntry(); - entry.setExtraMetadata(new HashMap<>()); - processDocumentReference(documentReference, entry); - - Extension repositoryUniqueIdExtension = documentReference - .getExtensionByUrl(MagConstants.FhirExtensionUrls.REPOSITORY_UNIQUE_ID); - if (repositoryUniqueIdExtension != null && repositoryUniqueIdExtension.getValue() instanceof Identifier) { - Identifier identifier = (Identifier) repositoryUniqueIdExtension.getValue(); - entry.setRepositoryUniqueId(noPrefix(identifier.getValue())); + public SubmitObjectsRequest createMetadataUpdateRequest(SubmissionSet submissionSet, DocumentEntry documentEntry) { + submissionSet.setPatientId(documentEntry.getPatientId()); + int currentVersion = Integer.parseInt(documentEntry.getVersion().getVersionName()); + RegisterDocumentSetBuilder builder = new RegisterDocumentSetBuilder(true, submissionSet) + .withDocument(documentEntry) + .withAssociation(createHasMemberAssociationWithOriginalPreviousLabel(currentVersion, submissionSet, documentEntry)); + documentEntry.getVersion().setVersionName(Integer.toString(currentVersion + 1)); + + // Submission contains a DocumentEntry object. + // The logicalID attribute is present in the DocumentEntry object and has a UUID + // formatted value. + // The SubmissionSet to DocumentEntry HasMember Association has a Slot with name + // PreviousVersion. This Slot has a single value, the version number of the + // previous version, the one being replaced. + + return EbXML30Converters.convert(builder.build()); } - Extension documentAvailabilityExtension = documentReference - .getExtensionByUrl(MagConstants.FhirExtensionUrls.DOCUMENT_AVAILABILITY); - if (documentAvailabilityExtension != null && documentAvailabilityExtension.getValue() instanceof Coding) { - Coding coding = (Coding) documentAvailabilityExtension.getValue(); - if (MagConstants.FhirCodingSystemIds.RFC_3986.equals(coding.getSystem()) && coding.getCode().startsWith("urn:ihe:iti:2010:DocumentAvailability:")) { - entry.setDocumentAvailability(DocumentAvailability.valueOfOpcode(coding.getCode().substring(38))); - } - } + private void enrichSubmissionSet(SubmissionSet submissionSet, DocumentReference documentReference) { + Extension source = + getExtensionByUrl(documentReference, "https://profiles.ihe.net/ITI/MHD/StructureDefinition/ihe-sourceId"); + if (source != null && source.getValue() instanceof Identifier) { + submissionSet.setSourceId(noPrefix(((Identifier) source.getValue()).getValue())); + } else { + submissionSet.setSourceId(noPrefix(config.getDocumentSourceId())); + } + + Extension designationType = + getExtensionByUrl(documentReference, "https://profiles.ihe.net/ITI/MHD/StructureDefinition/ihe-designationType"); + if (designationType != null && designationType.getValue() instanceof CodeableConcept) { + submissionSet.setContentTypeCode(transformCodeableConcept((CodeableConcept) designationType.getValue())); + } else { + submissionSet.setContentTypeCode(new Code("71388002", new LocalizedString("Procedure"), "2.16.840.1.113883.6.96")); + } - submissionSet.setPatientId(entry.getPatientId()); - submissionSet.assignEntryUuid(); - builder.withDocument(entry); - - int version; - Extension versionExtension = documentReference.getExtensionByUrl(MagConstants.FhirExtensionUrls.DOCUMENT_ENTRY_VERSION); - if ((versionExtension != null) && (versionExtension.getValue() instanceof PositiveIntType)) { - PositiveIntType versionElement = (PositiveIntType) versionExtension.getValue(); - version = versionElement.getValue(); - } else { - version = 1; + Extension authorRoleExt = + getExtensionByUrl(documentReference, MagConstants.FhirExtensionUrls.CH_AUTHOR_ROLE); + if (authorRoleExt != null) { + Coding coding = authorRoleExt.castToCoding(authorRoleExt.getValue()); + if (coding != null) { + Identifiable identifiable = new Identifiable(coding.getCode(), new AssigningAuthority(noPrefix(coding.getSystem()))); + submissionSet.setAuthor(transformAuthor(null, null, identifiable)); + } + } } - builder.withAssociation(createHasMemberAssociationWithOriginalPreviousLabel(version, submissionSet, entry)); + private DocumentEntry translateDocumentMetadata(DocumentReference documentReference) { + DocumentEntry entry = new DocumentEntry(); + entry.setExtraMetadata(new HashMap<>()); + processDocumentReference(documentReference, entry); - // Submission contains a DocumentEntry object. - // The logicalID attribute is present in the DocumentEntry object and has a UUID - // formatted value. - // The SubmissionSet to DocumentEntry HasMember Association has a Slot with name - // PreviousVersion. This Slot has a single value, the version number of the - // previous version, the one being replaced. + Extension repositoryUniqueIdExtension = documentReference + .getExtensionByUrl(MagConstants.FhirExtensionUrls.REPOSITORY_UNIQUE_ID); + if (repositoryUniqueIdExtension != null && repositoryUniqueIdExtension.getValue() instanceof Identifier) { + Identifier identifier = (Identifier) repositoryUniqueIdExtension.getValue(); + entry.setRepositoryUniqueId(noPrefix(identifier.getValue())); + } - return EbXML30Converters.convert(builder.build()); - } + Extension documentAvailabilityExtension = documentReference + .getExtensionByUrl(MagConstants.FhirExtensionUrls.DOCUMENT_AVAILABILITY); + if (documentAvailabilityExtension != null && documentAvailabilityExtension.getValue() instanceof Coding) { + Coding coding = (Coding) documentAvailabilityExtension.getValue(); + if (MagConstants.FhirCodingSystemIds.RFC_3986.equals(coding.getSystem()) && coding.getCode().startsWith("urn:ihe:iti:2010:DocumentAvailability:")) { + entry.setDocumentAvailability(DocumentAvailability.valueOfOpcode(coding.getCode().substring(38))); + } + } - private Association createHasMemberAssociationWithOriginalPreviousLabel(int version, SubmissionSet submissionSet, DocumentEntry entry) { - var assoc = createHasMemberAssociation(entry.getEntryUuid(), submissionSet); - assoc.setLabel(AssociationLabel.ORIGINAL); - assoc.setPreviousVersion(Integer.toString(version)); - assoc.setAssociationPropagation(true); - return assoc; - } + int version; + Extension versionExtension = documentReference.getExtensionByUrl(MagConstants.FhirExtensionUrls.DOCUMENT_ENTRY_VERSION); + if ((versionExtension != null) && (versionExtension.getValue() instanceof PositiveIntType)) { + PositiveIntType versionElement = (PositiveIntType) versionExtension.getValue(); + version = versionElement.getValue(); + } else { + version = 1; + } + entry.setVersion(new Version(Integer.toString(version))); + return entry; + } - private Association createHasMemberAssociation(String entryUuid, SubmissionSet submissionSet) { - return new Association(AssociationType.HAS_MEMBER, new URN(UUID.randomUUID()).toString(), - submissionSet.getEntryUuid(), entryUuid); + private Association createHasMemberAssociationWithOriginalPreviousLabel(int version, SubmissionSet submissionSet, DocumentEntry entry) { + var assoc = createHasMemberAssociation(entry.getEntryUuid(), submissionSet); + assoc.setLabel(AssociationLabel.ORIGINAL); + assoc.setPreviousVersion(Integer.toString(version)); + assoc.setAssociationPropagation(true); + return assoc; + } - } + private Association createHasMemberAssociation(String entryUuid, SubmissionSet submissionSet) { + return new Association(AssociationType.HAS_MEMBER, new URN(UUID.randomUUID()).toString(), + submissionSet.getEntryUuid(), entryUuid); + + } + + public SubmissionSet createSubmissionSet() { + SubmissionSet submissionSet = new SubmissionSet(); + submissionSet.setSubmissionTime(new Timestamp(ZonedDateTime.now(), Timestamp.Precision.SECOND)); + submissionSet.setContentTypeCode(new Code("71388002", new LocalizedString("Procedure"), "2.16.840.1.113883.6.96")); + submissionSet.setSourceId(noPrefix(config.getDocumentSourceId())); + submissionSet.assignEntryUuid(); + submissionSet.assignUniqueId(); + return submissionSet; + } } diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67ResponseConverter.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67ResponseConverter.java index 143df2f9..70a6d996 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67ResponseConverter.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67ResponseConverter.java @@ -34,6 +34,8 @@ import org.openehealth.ipf.commons.ihe.xds.core.responses.QueryResponse; import org.openehealth.ipf.commons.ihe.xds.core.responses.Status; import org.owasp.esapi.codecs.Hex; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import java.util.*; @@ -42,8 +44,10 @@ * * @author alexander kreutz */ +@Component public class Iti67ResponseConverter extends BaseQueryResponseConverter { + @Autowired public Iti67ResponseConverter(final Config config) { super(config); } diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RouteBuilder.java index 2fe5305e..a8285e2b 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RouteBuilder.java @@ -18,12 +18,17 @@ import static org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelTranslators.translateToFhir; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ch.bfh.ti.i4mi.mag.MagConstants; import org.apache.camel.builder.PredicateBuilder; import org.apache.camel.builder.RouteBuilder; import org.hl7.fhir.r4.model.DocumentReference; import org.openehealth.ipf.commons.ihe.fhir.Constants; +import org.openehealth.ipf.commons.ihe.xds.core.metadata.*; import org.openehealth.ipf.commons.ihe.xds.core.responses.QueryResponse; import org.openehealth.ipf.commons.ihe.xds.core.responses.Response; +import org.openehealth.ipf.commons.ihe.xds.core.responses.Status; import org.openehealth.ipf.commons.ihe.xds.core.stub.ebrs30.lcm.SubmitObjectsRequest; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; @@ -33,6 +38,9 @@ import ch.bfh.ti.i4mi.mag.xua.AuthTokenConverter; import lombok.extern.slf4j.Slf4j; +import java.util.HashMap; +import java.util.List; + /** * IHE MHD: Find Document References [ITI-67] for Document Responder * https://oehf.github.io/ipf-docs/docs/ihe/iti67/ @@ -43,40 +51,39 @@ class Iti67RouteBuilder extends RouteBuilder { private final Config config; + private final Iti67ResponseConverter iti67ResponseConverter; private final Iti67RequestUpdateConverter iti67RequestUpdateConverter; + private final Iti67FromIti57ResponseConverter iti67FromIti57ResponseConverter; - public Iti67RouteBuilder(final Config config, Iti67RequestUpdateConverter iti67RequestUpdateConverter) { + public Iti67RouteBuilder(final Config config, + Iti67ResponseConverter iti67ResponseConverter, + Iti67RequestUpdateConverter iti67RequestUpdateConverter, + Iti67FromIti57ResponseConverter iti67FromIti57ResponseConverter) + { super(); log.debug("Iti67RouteBuilder initialized"); this.config = config; + this.iti67ResponseConverter = iti67ResponseConverter; this.iti67RequestUpdateConverter = iti67RequestUpdateConverter; + this.iti67FromIti57ResponseConverter = iti67FromIti57ResponseConverter; } - @Override - public void configure() throws Exception { - log.debug("Iti67RouteBuilder configure"); - final String endpoint = String.format("xds-iti18://%s" + - "?secure=%s", this.config.getIti18HostUrl(), this.config.isHttps() ? "true" : "false") - + + private String createEndpointUri(String schema, String partialUrl) { + return schema + "://" + partialUrl + + "?secure=" + config.isHttps() + "&audit=true" + "&auditContext=#myAuditContext" + - // "&sslContextParameters=#pixContext" + - "&inInterceptors=#soapResponseLogger" + + "&inInterceptors=#soapResponseLogger" + "&inFaultInterceptors=#soapResponseLogger"+ - "&outInterceptors=#soapRequestLogger" + + "&outInterceptors=#soapRequestLogger" + "&outFaultInterceptors=#soapRequestLogger"; - final String endpoint57 = String.format("xds-iti57://%s" + - "?secure=%s", this.config.getIti57HostUrl(), this.config.isHttps() ? "true" : "false") - + - "&audit=true" + - "&auditContext=#myAuditContext" + - // "&sslContextParameters=#pixContext" + - "&inInterceptors=#soapResponseLogger" + - "&inFaultInterceptors=#soapResponseLogger"+ - "&outInterceptors=#soapRequestLogger" + - "&outFaultInterceptors=#soapRequestLogger"; - - // xds-iti57:serviceName[?parameters] + } + + @Override + public void configure() throws Exception { + log.debug("Iti67RouteBuilder configure"); + final String metadataQueryEndpoint = createEndpointUri("xds-iti18", this.config.getIti18HostUrl()); + final String metadataUpdateEndpoint = createEndpointUri("xds-iti57", this.config.getIti57HostUrl()); from("mhd-iti67-v401:translation?audit=true&auditContext=#myAuditContext").routeId("mdh-documentreference-adapter") // pass back errors to the endpoint @@ -86,22 +93,65 @@ public void configure() throws Exception { .when(header(Constants.FHIR_REQUEST_PARAMETERS).isNotNull()) .bean(Utils.class,"searchParameterToBody") .bean(Iti67RequestConverter.class) - .to(endpoint) - .process(translateToFhir(new Iti67ResponseConverter(config) , QueryResponse.class)) + .to(metadataQueryEndpoint) + .process(translateToFhir(iti67ResponseConverter, QueryResponse.class)) .endChoice() .when(PredicateBuilder.and(header("FhirHttpUri").isNotNull(),header("FhirHttpMethod").isEqualTo("GET"))) .bean(IdRequestConverter.class) - .to(endpoint) - .process(translateToFhir(new Iti67ResponseConverter(config) , QueryResponse.class)) + .to(metadataQueryEndpoint) + .process(translateToFhir(iti67ResponseConverter, QueryResponse.class)) .endChoice() .when(PredicateBuilder.and(header("FhirHttpUri").isNotNull(),header("FhirHttpMethod").isEqualTo("PUT"))) .process(exchange -> { DocumentReference documentReference = exchange.getIn().getMandatoryBody(DocumentReference.class); - SubmitObjectsRequest submitObjectsRequest = iti67RequestUpdateConverter.convertDocumentReferenceToDocumentEntry(documentReference); + SubmitObjectsRequest submitObjectsRequest = iti67RequestUpdateConverter.createMetadataUpdateRequest(documentReference); exchange.getMessage().setBody(submitObjectsRequest); }) - .to(endpoint57) - .process(translateToFhir(new Iti67FromIti57ResponseConverter(config), Response.class)) + .to(metadataUpdateEndpoint) + .process(translateToFhir(iti67FromIti57ResponseConverter, Response.class)) + .endChoice() + .when(PredicateBuilder.and(header("FhirHttpUri").isNotNull(),header("FhirHttpMethod").isEqualTo("DELETE"))) + .process(exchange -> { + exchange.setProperty("DOCUMENT_ENTRY_LOGICAL_ID", IdRequestConverter.extractId(exchange.getIn().getHeader("FhirHttpUri", String.class))); + }) + .bean(IdRequestConverter.class) + .to(metadataQueryEndpoint) + .process(exchange -> { + QueryResponse queryResponse = exchange.getIn().getMandatoryBody(QueryResponse.class); + if (queryResponse.getStatus() != Status.SUCCESS) { + iti67FromIti57ResponseConverter.processError(queryResponse); + } + if (queryResponse.getDocumentEntries().isEmpty()) { + throw new ResourceNotFoundException(exchange.getProperty("DOCUMENT_ENTRY_LOGICAL_ID", String.class)); + } + if (queryResponse.getDocumentEntries().size() > 1) { + throw new InternalErrorException("Expected at most one Document Entry, got " + queryResponse.getDocumentEntries().size()); + } + + DocumentEntry documentEntry = queryResponse.getDocumentEntries().get(0); + if (documentEntry.getExtraMetadata() == null) { + documentEntry.setExtraMetadata(new HashMap<>()); + } + documentEntry.getExtraMetadata().put(MagConstants.XdsExtraMetadataSlotNames.CH_DELETION_STATUS, List.of(MagConstants.DeletionStatuses.REQUESTED)); + if (documentEntry.getLogicalUuid() == null) { + documentEntry.setLogicalUuid(documentEntry.getEntryUuid()); + } + documentEntry.assignEntryUuid(); + if (documentEntry.getVersion() == null) { + documentEntry.setVersion(new Version("1")); + } + + SubmissionSet submissionSet = iti67RequestUpdateConverter.createSubmissionSet(); + SubmitObjectsRequest updateRequest = iti67RequestUpdateConverter.createMetadataUpdateRequest(submissionSet, documentEntry); + exchange.getMessage().setBody(updateRequest); + log.info("Prepared document metadata update request"); + }) + .choice() + .when(exchange -> exchange.getIn().getBody() instanceof SubmitObjectsRequest) + .to(metadataUpdateEndpoint) + .process(translateToFhir(new Iti67FromIti57ResponseConverter(config), Response.class)) + .endChoice() + .end() .endChoice() .end(); } diff --git a/src/main/java/org/openehealth/ipf/commons/ihe/fhir/iti67_v401/Iti67ResourceProvider.java b/src/main/java/org/openehealth/ipf/commons/ihe/fhir/iti67_v401/Iti67ResourceProvider.java index eda0a995..a4309fb1 100644 --- a/src/main/java/org/openehealth/ipf/commons/ihe/fhir/iti67_v401/Iti67ResourceProvider.java +++ b/src/main/java/org/openehealth/ipf/commons/ihe/fhir/iti67_v401/Iti67ResourceProvider.java @@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import ca.uhn.fhir.rest.annotation.*; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.r4.model.DocumentReference; import org.hl7.fhir.r4.model.IdType; @@ -32,16 +33,6 @@ import org.openehealth.ipf.commons.ihe.fhir.iti67.Iti67SearchParameters; import ca.uhn.fhir.model.api.Include; -import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.IncludeParam; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.annotation.RequiredParam; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; @@ -206,7 +197,16 @@ public MethodOutcome update( // Run down the route return requestAction(theReference, null, httpServletRequest, httpServletResponse, requestDetails); } - + @SuppressWarnings("unused") + @Delete(type = DocumentReference.class) + public MethodOutcome delete( + @IdParam IdType id, + RequestDetails requestDetails, + HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse) + { + return requestAction(id, null, httpServletRequest, httpServletResponse, requestDetails); + } } diff --git a/src/test/java/ch/bfh/ti/i4mi/mag/mhd/Iti57RequestTranslatorTest.java b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/Iti57RequestTranslatorTest.java index 5c6cc690..1f01e445 100644 --- a/src/test/java/ch/bfh/ti/i4mi/mag/mhd/Iti57RequestTranslatorTest.java +++ b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/Iti57RequestTranslatorTest.java @@ -34,7 +34,7 @@ public static void beforeAll() { @Test public void test1() { DocumentReference documentReference = (DocumentReference) FhirContext.forR4().newJsonParser().parseResource(Iti57RequestTranslatorTest.class.getClassLoader().getResourceAsStream("update-request-1.json")); - SubmitObjectsRequest ebXml = iti67RequestUpdateConverter.convertDocumentReferenceToDocumentEntry(documentReference); + SubmitObjectsRequest ebXml = iti67RequestUpdateConverter.createMetadataUpdateRequest(documentReference); String ebXmlString = XdsRenderingUtils.renderEbxml(ebXml); Assertions.assertTrue(ebXmlString.contains(MagConstants.XdsExtraMetadataSlotNames.CH_DELETION_STATUS)); diff --git a/src/test/java/ch/bfh/ti/i4mi/mag/mhd/MhdTest.java b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/MhdTest.java index c6c5e3fd..082d3dee 100644 --- a/src/test/java/ch/bfh/ti/i4mi/mag/mhd/MhdTest.java +++ b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/MhdTest.java @@ -8,10 +8,11 @@ import lombok.extern.slf4j.Slf4j; import org.apache.camel.CamelContext; import org.apache.camel.ProducerTemplate; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.hl7.fhir.r4.model.DocumentReference; - -import static org.junit.jupiter.api.Assertions.*; - import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,7 +21,13 @@ import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Locale; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Dmytro Rud @@ -75,7 +82,20 @@ public void testMetadataUpdate() { errorCatched = true; } assertTrue(errorCatched); + } + @Test + public void testDocumentDeletion() throws IOException { + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + ClassicHttpRequest httpRequest = ClassicRequestBuilder.delete("http://localhost:" + serverPort + "/fhir/DocumentReference/" + UUID.randomUUID()) + .setCharset(StandardCharsets.UTF_8) + .build(); + String response = httpClient.execute(httpRequest, httpResponse -> { + assertEquals(200, httpResponse.getCode()); + return httpResponse.toString(); + }); + log.info(response); + } } } diff --git a/src/test/java/ch/bfh/ti/i4mi/mag/mhd/MhdTestRouteBuilder.java b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/MhdTestRouteBuilder.java index 6c71b0ed..d9e0e818 100644 --- a/src/test/java/ch/bfh/ti/i4mi/mag/mhd/MhdTestRouteBuilder.java +++ b/src/test/java/ch/bfh/ti/i4mi/mag/mhd/MhdTestRouteBuilder.java @@ -1,12 +1,26 @@ package ch.bfh.ti.i4mi.mag.mhd; +import ch.bfh.ti.i4mi.mag.MagConstants; import org.apache.camel.builder.RouteBuilder; +import org.openehealth.ipf.commons.ihe.xds.core.SampleData; +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.Version; +import org.openehealth.ipf.commons.ihe.xds.core.requests.QueryRegistry; import org.openehealth.ipf.commons.ihe.xds.core.requests.RegisterDocumentSet; +import org.openehealth.ipf.commons.ihe.xds.core.requests.query.GetDocumentsQuery; +import org.openehealth.ipf.commons.ihe.xds.core.responses.QueryResponse; import org.openehealth.ipf.commons.ihe.xds.core.responses.Response; import org.openehealth.ipf.commons.ihe.xds.core.responses.Status; import org.openehealth.ipf.platform.camel.ihe.xds.XdsCamelValidators; import org.springframework.stereotype.Component; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + /** * @author Dmytro Rud */ @@ -16,14 +30,60 @@ public class MhdTestRouteBuilder extends RouteBuilder { @Override public void configure() throws Exception { + from("xds-iti18://iti18Endpoint") + .process(XdsCamelValidators.iti18RequestValidator()) + .process(exchange -> { + log.info("Received ITI-18 request"); + QueryRegistry iti18Request = exchange.getIn().getMandatoryBody(QueryRegistry.class); + assertInstanceOf(GetDocumentsQuery.class, iti18Request.getQuery()); + + DocumentEntry documentEntry = SampleData.createDocumentEntry(new Identifiable("testIti18-1", new AssigningAuthority("1.2.3.4.5"))); + documentEntry.assignEntryUuid(); + documentEntry.setVersion(new Version("42")); + + QueryResponse iti18Response = new QueryResponse(Status.SUCCESS); + iti18Response.getDocumentEntries().add(documentEntry); + exchange.getMessage().setBody(iti18Response); + }) + .process(XdsCamelValidators.iti18ResponseValidator()) + ; + from("xds-iti57://iti57Endpoint") .process(XdsCamelValidators.iti57RequestValidator()) + .to("direct:handle-metadata-update") + .process(XdsCamelValidators.iti57ResponseValidator()) + ; + + from("rmu-iti92://iti92Endpoint") + .process(XdsCamelValidators.iti92RequestValidator()) + .to("direct:handle-metadata-update") + .process(XdsCamelValidators.iti92ResponseValidator()) + ; + + from("direct:handle-metadata-update") .process(exchange -> { - log.info("Received ITI-57 request"); - RegisterDocumentSet request = exchange.getIn().getMandatoryBody(RegisterDocumentSet.class); - Response response = new Response(); - response.setStatus((request.getDocumentEntries().get(0).getSize() % 2 == 0) ? Status.SUCCESS : Status.FAILURE); - exchange.getMessage().setBody(response); + log.info("Received metadata update request"); + RegisterDocumentSet updateRequest = exchange.getIn().getMandatoryBody(RegisterDocumentSet.class); + Response updateResponse = new Response(); + + if ("testIti18-1".equals(updateRequest.getSubmissionSet().getPatientId().getId())) { + assertEquals(1, updateRequest.getAssociations().size()); + assertEquals("42", updateRequest.getAssociations().get(0).getPreviousVersion()); + + assertEquals(1, updateRequest.getDocumentEntries().size()); + DocumentEntry documentEntry = updateRequest.getDocumentEntries().get(0); + assertEquals("43", documentEntry.getVersion().getVersionName()); + assertEquals(1, documentEntry.getExtraMetadata().size()); + List deletionStatuses = documentEntry.getExtraMetadata().get(MagConstants.XdsExtraMetadataSlotNames.CH_DELETION_STATUS); + assertEquals(1, deletionStatuses.size()); + assertEquals(MagConstants.DeletionStatuses.REQUESTED, deletionStatuses.get(0)); + + updateResponse.setStatus(Status.SUCCESS); + } else { + RegisterDocumentSet request = exchange.getIn().getMandatoryBody(RegisterDocumentSet.class); + updateResponse.setStatus((request.getDocumentEntries().get(0).getSize() % 2 == 0) ? Status.SUCCESS : Status.FAILURE); + } + exchange.getMessage().setBody(updateResponse); }) ;