Skip to content

Commit

Permalink
Merge branch 'feature/iti119' into feature/ipf5prep
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Ohr committed Dec 18, 2024
2 parents 4193d0e + f614a8b commit 1de35a1
Show file tree
Hide file tree
Showing 27 changed files with 1,023 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.narrative2.NullNarrativeGenerator;
import ca.uhn.fhir.rest.server.ApacheProxyAddressStrategy;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IServerAddressStrategy;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.*;
import jakarta.servlet.Filter;
import org.hl7.fhir.instance.model.api.IBaseConformance;
import org.openehealth.ipf.boot.atna.IpfAtnaAutoConfiguration;
import org.openehealth.ipf.commons.ihe.fhir.IpfFhirServlet;
Expand All @@ -44,7 +41,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CorsFilter;

import jakarta.servlet.Filter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -152,6 +148,16 @@ public IpfFhirServlet fhirServlet(
if (narrativeGenerator != null) {
fhirServlet.setNarrativeGenerator(narrativeGenerator);
}
// Register server interceptor for ITI-119 if on classpath
try {
var clazz = Class.forName("org.openehealth.ipf.commons.ihe.fhir.iti119.MatchGradeEnumInterceptor");
fhirServlet.registerInterceptor(clazz.getConstructor().newInstance());
} catch (ClassNotFoundException e) {
// ok
} catch (Exception e) {
// should never happen
throw new RuntimeException(e);
}
return fhirServlet;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public enum FhirEventTypeCode implements EventType, EnumeratedCodedValue<EventTy
RetrieveATNAAuditEvent("ITI-81", IHE_SYSTEM_NAME, "Retrieve ATNA AuditEvent"),
MobilePatientIdentifierCrossReferenceQuery("ITI-83", IHE_SYSTEM_NAME, "Mobile Patient Identifier Cross-reference Query"),
SimplifiedPublish("ITI-105", IHE_SYSTEM_NAME, "Simplified Publish"),
PatientDemographicsMatch("ITI-119", IHE_SYSTEM_NAME, "Patient Demographics Match"),
MobileQueryExistingData("PCC-44", IHE_SYSTEM_NAME, "Mobile Query Existing Data"),
QueryPharmacyDocumentsOverMhd("PHARM-5", IHE_SYSTEM_NAME, "Query Pharmacy Documents over MHD"),
MobilePrivacyPolicyFeed("PPQ-3", EHS_SYSTEM_NAME, "Mobile Privacy Policy Feed"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public enum FhirParticipantObjectIdTypeCode implements ParticipantObjectIdType,
MobilePatientDemographicsQuery("ITI-78", IHE_SYSTEM_NAME, "Mobile Patient Demographics Query"),
RetrieveATNAAuditEvent("ITI-81", IHE_SYSTEM_NAME, "Retrieve ATNA AuditEvent"),
MobilePatientIdentifierCrossReferenceQuery("ITI-83", IHE_SYSTEM_NAME, "Mobile Patient Identifier Cross-reference Query"),
PatientDemographicsMatch("ITI-119", IHE_SYSTEM_NAME, "Patient Demographics Match"),
MobileQueryExistingData("PCC-44", IHE_SYSTEM_NAME, "Mobile Query Existing Data"),
QueryPharmacyDocumentsOverMhd("PHARM-5", IHE_SYSTEM_NAME, "Query Pharmacy Documents over MHD"),
MobilePrivacyPolicyQuery("PPQ-5", EHS_SYSTEM_NAME, "Mobile Privacy Policy Query");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,13 @@ public void validateRequest(Object payload, Map<String, Object> parameters) {
}
}

@Override
public void validateResponse(Object payload, Map<String, Object> parameters) {
var resource = (IBaseResource) payload;
var validationResult = validator.validateWithResult(resource);
if (!validationResult.isSuccessful()) {
var operationOutcome = validationResult.toOperationOutcome();
throw FhirUtils.exception(UnprocessableEntityException::new, operationOutcome, "Validation Failed");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2024 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.iti119;

import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import org.hl7.fhir.r4.model.codesystems.MatchGrade;

public abstract class AdditionalResourceMetadataKeyEnum {

public static final ResourceMetadataKeyEnum<MatchGrade> ENTRY_MATCH_GRADE =
new ResourceMetadataKeyEnum<>("ENTRY_MATCH_GRADE", MatchGrade.class) {};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2024 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.iti119;

import org.hl7.fhir.r4.model.Parameters;
import org.openehealth.ipf.commons.audit.AuditContext;
import org.openehealth.ipf.commons.audit.model.AuditMessage;
import org.openehealth.ipf.commons.ihe.fhir.audit.FhirQueryAuditDataset;
import org.openehealth.ipf.commons.ihe.fhir.audit.FhirQueryAuditStrategy;
import org.openehealth.ipf.commons.ihe.fhir.audit.codes.FhirEventTypeCode;
import org.openehealth.ipf.commons.ihe.fhir.audit.codes.FhirParticipantObjectIdTypeCode;
import org.openehealth.ipf.commons.ihe.fhir.audit.events.BalpQueryInformationBuilder;

import java.util.Map;

/**
* Strategy for auditing ITI-78 transactions
*
* @author Christian Ohr
* @since 3.6
*/
class Iti119AuditStrategy extends FhirQueryAuditStrategy {

protected Iti119AuditStrategy(boolean serverSide) {
super(serverSide);
}

@Override
public AuditMessage[] makeAuditMessage(AuditContext auditContext, FhirQueryAuditDataset auditDataset) {
return new BalpQueryInformationBuilder(auditContext, auditDataset, FhirEventTypeCode.PatientDemographicsMatch)
.addPatients(auditDataset.getPatientIds())
.setQueryParameters(
"PatientDemographicsMatch",
FhirParticipantObjectIdTypeCode.PatientDemographicsMatch,
auditDataset.getQueryString())

.getMessages();
}

@Override
public FhirQueryAuditDataset enrichAuditDatasetFromRequest(FhirQueryAuditDataset auditDataset, Object request, Map<String, Object> parameters) {
var dataset = super.enrichAuditDatasetFromRequest(auditDataset, request, parameters);
if (request instanceof Parameters p && auditDataset.getFhirContext() != null) {
dataset.setQueryString(auditDataset.getFhirContext().newJsonParser().encodeToString(p));
}
return dataset;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2024 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.iti119;

/**
* Strategy for auditing ITI-119 transactions on the client side
*
* @author Christian Ohr
* @since 5.0
*/
public class Iti119ClientAuditStrategy extends Iti119AuditStrategy {

public Iti119ClientAuditStrategy() {
super(false);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2024 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.iti119;

import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import org.openehealth.ipf.commons.ihe.fhir.ClientRequestFactory;

import java.util.Map;

import static org.openehealth.ipf.commons.ihe.fhir.iti119.Iti119Constants.RESOURCE;

/**
* Request Factory for Iti-119 requests
*
* @author Christian Ohr
* @since 5.0
*/
public class Iti119ClientRequestFactory implements ClientRequestFactory<IOperationUntypedWithInput<Bundle>> {

@Override
public IClientExecutable<IOperationUntypedWithInput<Bundle>, ?> getClientExecutable(IGenericClient client, Object requestData, Map<String, Object> parameters) {

if (requestData instanceof Parameters p) {
return getClientExecutable(client, p);
}

var p = new Parameters();
if (requestData instanceof Patient patient) {
p.addParameter().setResource(patient);
} else if (parameters.containsKey(RESOURCE) && parameters.get(RESOURCE) instanceof Patient patient) {
p.addParameter().setResource(patient);
}
parameters.entrySet().stream()
.filter(entry -> Iti119Constants.ITI119_PARAMETERS.contains(entry.getKey()))
.forEach(entry -> p.addParameter()
.setName(entry.getKey())
.setValue(new StringType(entry.getValue().toString())));

return getClientExecutable(client, p);
}


private IClientExecutable<IOperationUntypedWithInput<Bundle>, ?> getClientExecutable(IGenericClient client, Parameters requestData) {
return client.operation()
.onType(Patient.class)
.named(Iti119Constants.PDQM_MATCH_OPERATION_NAME)
.withParameters(requestData)
.returnResourceType(Bundle.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2024 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.iti119;

import org.openehealth.ipf.commons.ihe.fhir.Constants;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
* @author Christian Ohr
* @since 5.0
*/
public interface Iti119Constants {

String RESOURCE = "resource";
String ONLY_CERTAIN_MATCHES = "onlyCertainMatches";
String COUNT = "count";

Set<String> ITI119_PARAMETERS = new HashSet<>(Arrays.asList(
ONLY_CERTAIN_MATCHES,
COUNT));

String PDQM_MATCH_OPERATION_NAME = "$match";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2024 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.iti119;

import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.PositiveIntType;
import org.hl7.fhir.r4.model.ResourceType;
import org.openehealth.ipf.commons.ihe.fhir.AbstractPlainProvider;
import org.openehealth.ipf.commons.ihe.fhir.iti78.PdqPatient;

import java.util.Set;

import static org.openehealth.ipf.commons.ihe.fhir.iti119.Iti119Constants.*;

/**
* Resource Provider for PDQm Match (ITI-119) for R4
*
* @author Christian Ohr
* @since 5.0
*/
public class Iti119ResourceProvider extends AbstractPlainProvider {

/**
* Handles the PDQm Match request
*
* @param resource patient match input
* @param onlyCertainMatches may be optionally set to true to indicate that the Patient Demographics Consumer
* would only like matches returned when they are certain to be matches for the subject of the request
* @param count can be used to limit the number of results the Patient Demographics Supplier returns
* @param httpServletRequest servlet request
* @param httpServletResponse servlet response
* @param sortSpec sort specification
* @param includeSpec include specification
* @param requestDetails request details
* @return {@link IBundleProvider} instance that manages retrieving patients
*/
@SuppressWarnings("unused")
@Operation(name = PDQM_MATCH_OPERATION_NAME, type = PdqPatient.class, bundleType = BundleTypeEnum.SEARCHSET)
public IBundleProvider pdqmMatch(
@OperationParam(name = RESOURCE, type = Patient.class) Patient resource,
@OperationParam(name = ONLY_CERTAIN_MATCHES, type = BooleanType.class) BooleanType onlyCertainMatches,
@OperationParam(name = COUNT, type = PositiveIntType.class) PositiveIntType count,
@Sort SortSpec sortSpec,
@IncludeParam Set<Include> includeSpec,
RequestDetails requestDetails,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {

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

}
Loading

0 comments on commit 1de35a1

Please sign in to comment.