Skip to content

Commit

Permalink
#369: additions for ITI-65 and 66 for MHD 4.2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Ohr committed Dec 13, 2023
1 parent 0ccc6bc commit 2a6f06d
Show file tree
Hide file tree
Showing 61 changed files with 64,414 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.r4.model.*;
import org.openehealth.ipf.commons.ihe.fhir.AbstractPlainProvider;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Set;

/**
* Resource Provider for MHD (ITI-66 List)
*
* @author Christian Ohr
* @since 4.8
*/
public class Iti66ListResourceProvider extends Iti66ResourceProvider {

// Supported as of MHD 4.2.1

private static final String SP_DESIGNATION_TYPE = "designationType";
private static final String SP_SOURCE_ID = "sourceId";

@SuppressWarnings("unused")
@Search(type = ListResource.class)
public IBundleProvider listSearch(
@RequiredParam(name = ListResource.SP_PATIENT, chainWhitelist = {"", Patient.SP_IDENTIFIER}) ReferenceParam patient,
@OptionalParam(name = ListResource.SP_DATE) DateRangeParam date,
@OptionalParam(name = ListResource.SP_SOURCE, chainWhitelist = { Practitioner.SP_FAMILY, Practitioner.SP_GIVEN }) ReferenceAndListParam author,
@OptionalParam(name = ListResource.SP_IDENTIFIER) TokenParam identifier,
@OptionalParam(name = ListResource.SP_CODE) TokenOrListParam code,
@OptionalParam(name = ListResource.SP_STATUS) TokenOrListParam status,
@OptionalParam(name = SP_SOURCE_ID) TokenOrListParam sourceId,
@OptionalParam(name = SP_DESIGNATION_TYPE) TokenOrListParam designationType,
// Extension to ITI-66
@OptionalParam(name = IAnyResource.SP_RES_ID) TokenParam resourceId,
@Sort SortSpec sortSpec,
@IncludeParam Set<Include> includeSpec,
RequestDetails requestDetails,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {


var searchParameters = Iti66ListSearchParameters.builder()
.date(date)
.code(code)
.designationType(designationType)
.sourceId(sourceId)
.status(status)
.identifier(identifier)
._id(resourceId)
.sortSpec(sortSpec)
.includeSpec(includeSpec)
.fhirContext(getFhirContext())
.build();

searchParameters.setAuthor(author);

var chain = patient.getChain();
if (Patient.SP_IDENTIFIER.equals(chain)) {
searchParameters.setPatientIdentifier(patient.toTokenParam(getFhirContext()));
} else if (chain == null || chain.isEmpty()) {
searchParameters.setPatientReference(patient);
}

// Run down the route
return requestBundleProvider(null, searchParameters, ResourceType.List.name(),
httpServletRequest, httpServletResponse, requestDetails);
}

/**
* Handles List Retrieve. This is not an actual part of the ITI-66 specification, but in the
* context of restful FHIR IHE transaction it makes sense to be able to retrieve a ListResource by
* its resource ID.
*
* @param id resource ID
* @param httpServletRequest servlet request
* @param httpServletResponse servlet response
* @param requestDetails request details
* @return {@link ListResource} resource
*/
@SuppressWarnings("unused")
@Read(version = true, type = ListResource.class)
public ListResource listRetrieve(
@IdParam IdType id,
RequestDetails requestDetails,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
if (id == null) throw new InvalidRequestException("Must provide ID with READ request");
// Run down the route
return requestResource(id, null, ListResource.class, httpServletRequest, httpServletResponse, requestDetails);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.*;
import lombok.*;
import org.hl7.fhir.r4.model.DocumentManifest;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.PractitionerRole;
import org.openehealth.ipf.commons.ihe.fhir.FhirSearchAndSortParameters;

import java.util.*;

import static java.util.Comparator.comparing;
import static java.util.Comparator.nullsLast;

/**
* @since 3.6
*/
@Builder
@ToString
@AllArgsConstructor
public class Iti66ListSearchParameters extends FhirSearchAndSortParameters<DocumentManifest> {

@Getter @Setter private DateRangeParam date;
@Getter @Setter private StringParam authorFamilyName;
@Getter @Setter private StringParam authorGivenName;
@Getter @Setter private TokenOrListParam code;
@Getter @Setter private TokenOrListParam designationType;
@Getter @Setter private TokenOrListParam sourceId;
@Getter @Setter private TokenOrListParam status;
@Getter @Setter private TokenParam identifier;
@Getter @Setter private ReferenceParam patientReference;
@Getter @Setter private TokenParam patientIdentifier;
@Getter @Setter private TokenParam _id;

@Getter @Setter private SortSpec sortSpec;
@Getter @Setter private Set<Include> includeSpec;

@Getter
private final FhirContext fhirContext;

@Override
public List<TokenParam> getPatientIdParam() {
if (_id != null)
return Collections.singletonList(_id);
if (patientReference != null)
return Collections.singletonList(patientReference.toTokenParam(fhirContext));

return Collections.singletonList(patientIdentifier);
}

public Iti66ListSearchParameters setAuthor(ReferenceAndListParam author) {
if (author != null) {
author.getValuesAsQueryTokens().forEach(param -> {
var ref = param.getValuesAsQueryTokens().get(0);
var authorChain = ref.getChain();
if (Practitioner.SP_FAMILY.equals(authorChain)) {
setAuthorFamilyName(ref.toStringParam(getFhirContext()));
} else if (Practitioner.SP_GIVEN.equals(authorChain)) {
setAuthorGivenName(ref.toStringParam(getFhirContext()));
}
});
}
return this;
}

@Override
public Optional<Comparator<DocumentManifest>> comparatorFor(String paramName) {
if (DocumentManifest.SP_CREATED.equals(paramName)) {
return Optional.of(CP_CREATED);
} else if (DocumentManifest.SP_AUTHOR.equals(paramName)) {
return Optional.of(CP_AUTHOR);
}
return Optional.empty();
}

private static final Comparator<DocumentManifest> CP_CREATED = nullsLast(comparing(DocumentManifest::getCreated));

private static final Comparator<DocumentManifest> CP_AUTHOR = nullsLast(comparing(documentManifest -> {
if (!documentManifest.hasAuthor()) return null;
var author = documentManifest.getAuthorFirstRep();
if (author.getResource() instanceof PractitionerRole) {
var practitionerRole = (PractitionerRole) author.getResource();
if (!practitionerRole.hasPractitioner()) return null;
author = practitionerRole.getPractitioner();
}
if (author.getResource() == null) return null;
if (author.getResource() instanceof Practitioner) {
var practitioner = (Practitioner) author.getResource();
if (!practitioner.hasName()) return null;
var name = practitioner.getNameFirstRep();
return name.getFamilyElement().getValueNotNull() + name.getGivenAsSingleString();
}
return null;
}));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openehealth.ipf.commons.ihe.fhir.mhd;

import org.hl7.fhir.r4.model.Coding;

interface MhdConstants {

// Bundle Profiles V4

String ITI65_MINIMAL_BUNDLE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.Minimal.ProvideBundle";
String ITI65_COMPREHENSIVE_BUNDLE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.Comprehensive.ProvideBundle";
String ITI65_UNCONTAINED_COMPREHENSIVE_BUNDLE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.UnContained.Comprehensive.ProvideBundle";
String ITI65_PROVIDE_DOCUMENT_BUNDLE_RESPONSE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.ProvideDocumentBundleResponse";
String ITI66_FIND_DOCUMENT_LISTS_RESPONSE_BUNDLE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.FindDocumentListsResponseMessage";
String ITI67_FIND_DOCUMENT_REFERENCES_RESPONSE_BUNDLE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.FindDocumentReferencesResponseMessage";
String ITI67_FIND_DOCUMENT_REFERENCES_COMPREHENSIVE_RESPONSE_BUNDLE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.FindDocumentReferencesComprehensiveResponseMessage";

// DocumentManifest/List profiles

String MHD_LIST_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.List";
String COMPREHENSIVE_SUBMISSIONSET_TYPE_LIST_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.Comprehensive.SubmissionSet";
String UNCONTAINED_COMPREHENSIVE_SUBMISSIONSET_TYPE_LIST_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.UnContained.Comprehensive.SubmissionSet";
String MINIMAL_SUBMISSIONSET_TYPE_LIST_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.Minimal.SubmissionSet";

// List Profile

String MINIMAL_FOLDER_TYPE_LIST_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.Minimal.Folder";
String COMPREHENSIVE_FOLDER_TYPE_LIST_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.Comprehensive.Folder";

// DocumentReference profiles

String COMPREHENSIVE_DOCUMENT_REFERENCE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.Comprehensive.DocumentReference";
String UNCONTAINED_COMPREHENSIVE_DOCUMENT_REFERENCE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.UnContained.Comprehensive.DocumentReference";
String MINIMAL_DOCUMENT_REFERENCE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.Minimal.DocumentReference";
String SIMPLIFIED_PUBLISH_DOCUMENT_REFERENCE_PROFILE = "https://profiles.ihe.net/ITI/MHD/StructureDefinition/IHE.MHD.SimplifiedPublish.DocumentReference";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openehealth.ipf.commons.ihe.fhir.mhd;

import ca.uhn.fhir.context.FhirContext;
import lombok.Getter;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.Resource;
import org.openehealth.ipf.commons.ihe.fhir.mhd.model.*;

import java.util.Arrays;
import java.util.List;

@Getter
public enum MhdProfiles implements MhdConstants {


// Bundle Profiles V4

ITI65_MINIMAL_BUNDLE(
ITI65_MINIMAL_BUNDLE_PROFILE, MinimalProvideDocumentBundle.class),
ITI65_COMPREHENSIVE_BUNDLE(
ITI65_COMPREHENSIVE_BUNDLE_PROFILE, ComprehensiveProvideDocumentBundle.class),
ITI65_UNCONTAINED_COMPREHENSIVE_BUNDLE(
ITI65_UNCONTAINED_COMPREHENSIVE_BUNDLE_PROFILE, UncontainedComprehensiveProvideDocumentBundle.class),
ITI65_PROVIDE_DOCUMENT_BUNDLE_RESPONSE(
ITI65_PROVIDE_DOCUMENT_BUNDLE_RESPONSE_PROFILE, ProvideDocumentBundleResponse.class),
ITI66_FIND_DOCUMENT_LISTS_RESPONSE_BUNDLE(
ITI66_FIND_DOCUMENT_LISTS_RESPONSE_BUNDLE_PROFILE, FindDocumentListsResponseBundle.class),
ITI67_FIND_DOCUMENT_REFERENCES_RESPONSE_BUNDLE(
ITI67_FIND_DOCUMENT_REFERENCES_RESPONSE_BUNDLE_PROFILE, FindMinimalDocumentReferencesResponseBundle.class),
ITI67_FIND_DOCUMENT_REFERENCES_COMPREHENSIVE_RESPONSE_BUNDLE(
ITI67_FIND_DOCUMENT_REFERENCES_COMPREHENSIVE_RESPONSE_BUNDLE_PROFILE, FindComprehensiveDocumentReferencesResponseBundle.class),

// List profiles

MHD_LIST(MHD_LIST_PROFILE, MhdList.class),
COMPREHENSIVE_SUBMISSIONSET_TYPE_LIST(COMPREHENSIVE_SUBMISSIONSET_TYPE_LIST_PROFILE, ComprehensiveSubmissionSetList.class),
UNCONTAINED_COMPREHENSIVE_SUBMISSIONSET_TYPE_LIST(UNCONTAINED_COMPREHENSIVE_SUBMISSIONSET_TYPE_LIST_PROFILE, UncontainedComprehensiveProvideDocumentBundle.class),
MINIMAL_SUBMISSIONSET_TYPE_LIST(MINIMAL_SUBMISSIONSET_TYPE_LIST_PROFILE, MinimalSubmissionSetList.class),

// List Profile

MINIMAL_FOLDER_TYPE_LIST(MINIMAL_FOLDER_TYPE_LIST_PROFILE, MinimalFolderList.class),
COMPREHENSIVE_FOLDER_TYPE_LIST(COMPREHENSIVE_FOLDER_TYPE_LIST_PROFILE, ComprehensiveFolderList.class),

// DocumentReference profiles

COMPREHENSIVE_DOCUMENT_REFERENCE(COMPREHENSIVE_DOCUMENT_REFERENCE_PROFILE, ComprehensiveDocumentReference.class),
UNCONTAINED_COMPREHENSIVE_DOCUMENT_REFERENCE(UNCONTAINED_COMPREHENSIVE_DOCUMENT_REFERENCE_PROFILE, UncontainedComprehensiveDocumentReference.class),
MINIMAL_DOCUMENT_REFERENCE(MINIMAL_DOCUMENT_REFERENCE_PROFILE, MinimalDocumentReference.class),
SIMPLIFIED_PUBLISH_DOCUMENT_REFERENCE(SIMPLIFIED_PUBLISH_DOCUMENT_REFERENCE_PROFILE, SimplifiedPublishDocumentReference.class);


private final String url;

private final Class<? extends IBaseResource> resourceClass;


MhdProfiles(String url, Class<? extends IBaseResource> resourceClass) {
this.url = url;
this.resourceClass = resourceClass;
}

/**
* Set the Meta/Profile of the resource
*
* @param resource FHIR resource
*/
public void setProfile(Resource resource) {
resource.getMeta().setProfile(List.of(new CanonicalType(url)));
}

/**
* Registers all the profiles and implementing classes in the {@link FhirContext}
*
* @param fhirContext FhirContext
*/
public static void registerDefaultTypes(FhirContext fhirContext) {
Arrays.stream(MhdProfiles.values()).forEach(profile ->
fhirContext.setDefaultTypeForProfile(profile.getUrl(), profile.resourceClass));
}

}
Loading

0 comments on commit 2a6f06d

Please sign in to comment.