wpi merge operation provider
This commit is contained in:
parent
054f7e6678
commit
7979f037e9
|
@ -19,8 +19,11 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.jpa.provider;
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient;
|
||||||
import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters;
|
import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters;
|
||||||
|
import ca.uhn.fhir.jpa.dao.merge.MergeOperationParameters;
|
||||||
|
import ca.uhn.fhir.jpa.dao.merge.ResourceMergeService;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
@ -39,12 +42,25 @@ import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
import ca.uhn.fhir.util.CanonicalIdentifier;
|
||||||
|
import ca.uhn.fhir.util.IdentifierUtil;
|
||||||
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
import org.hl7.fhir.r4.model.Identifier;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
@ -240,6 +256,90 @@ public abstract class BaseJpaResourceProviderPatient<T extends IBaseResource> ex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* /Patient/$merge
|
||||||
|
*/
|
||||||
|
@Operation(
|
||||||
|
name = ProviderConstants.OPERATION_MERGE,
|
||||||
|
canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-merge")
|
||||||
|
public void patientMerge(
|
||||||
|
HttpServletRequest theServletRequest,
|
||||||
|
HttpServletResponse theServletResponse,
|
||||||
|
ServletRequestDetails theRequestDetails,
|
||||||
|
@OperationParam(name = ProviderConstants.OPERATION_MERGE_SOURCE_PATIENT_IDENTIFIER)
|
||||||
|
List<Identifier> theSourcePatientIdentifier,
|
||||||
|
@OperationParam(name = ProviderConstants.OPERATION_MERGE_TARGET_PATIENT_IDENTIFIER)
|
||||||
|
List<Identifier> theTargetPatientIdentifier,
|
||||||
|
@OperationParam(name = ProviderConstants.OPERATION_MERGE_SOURCE_PATIENT, max = 1)
|
||||||
|
IBaseReference theSourcePatient,
|
||||||
|
@OperationParam(name = ProviderConstants.OPERATION_MERGE_TARGET_PATIENT, max = 1)
|
||||||
|
IBaseReference theTargetPatient,
|
||||||
|
@OperationParam(name = ProviderConstants.OPERATION_MERGE_PREVIEW, typeName = "boolean", max = 1)
|
||||||
|
IPrimitiveType<Boolean> thePreview,
|
||||||
|
@OperationParam(name = ProviderConstants.OPERATION_MERGE_RESULT_PATIENT, max = 1)
|
||||||
|
IBaseResource theResultPatient)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
startRequest(theServletRequest);
|
||||||
|
try {
|
||||||
|
MergeOperationParameters mergeOperationParameters = createMergeOperationParameters(
|
||||||
|
theSourcePatientIdentifier,
|
||||||
|
theTargetPatientIdentifier,
|
||||||
|
theSourcePatient,
|
||||||
|
theTargetPatient,
|
||||||
|
thePreview,
|
||||||
|
theResultPatient);
|
||||||
|
|
||||||
|
IFhirResourceDaoPatient<?> dao = (IFhirResourceDaoPatient<?>) getDao();
|
||||||
|
ResourceMergeService resourceMergeService = new ResourceMergeService(dao);
|
||||||
|
|
||||||
|
FhirContext fhirContext = dao.getContext();
|
||||||
|
|
||||||
|
ResourceMergeService.MergeOutcome mergeOutcome =
|
||||||
|
resourceMergeService.merge(mergeOperationParameters, theRequestDetails);
|
||||||
|
|
||||||
|
IBaseParameters retVal = ParametersUtil.newInstance(fhirContext);
|
||||||
|
ParametersUtil.addParameterToParameters(fhirContext, retVal, "outcome", mergeOutcome.getOperationOutcome());
|
||||||
|
|
||||||
|
theServletResponse.setStatus(mergeOutcome.getHttpStatusCode());
|
||||||
|
// we are writing the response to directly, otherwise the response status we set above is ignored.
|
||||||
|
fhirContext
|
||||||
|
.newJsonParser()
|
||||||
|
.setPrettyPrint(true)
|
||||||
|
.encodeResourceToWriter(retVal, theServletResponse.getWriter());
|
||||||
|
theServletResponse.getWriter().close();
|
||||||
|
} finally {
|
||||||
|
endRequest(theServletRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MergeOperationParameters createMergeOperationParameters(
|
||||||
|
List<Identifier> theSourcePatientIdentifier,
|
||||||
|
List<Identifier> theTargetPatientIdentifier,
|
||||||
|
IBaseReference theSourcePatient,
|
||||||
|
IBaseReference theTargetPatient,
|
||||||
|
IPrimitiveType<Boolean> thePreview,
|
||||||
|
IBaseResource theResultPatient) {
|
||||||
|
MergeOperationParameters mergeOperationParameters = new MergeOperationParameters();
|
||||||
|
if (theSourcePatientIdentifier != null) {
|
||||||
|
List<CanonicalIdentifier> sourceResourceIdentifiers = theSourcePatientIdentifier.stream()
|
||||||
|
.map(IdentifierUtil::identifierDtFromIdentifier)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
mergeOperationParameters.setSourceResourceIdentifiers(sourceResourceIdentifiers);
|
||||||
|
}
|
||||||
|
if (theTargetPatientIdentifier != null) {
|
||||||
|
List<CanonicalIdentifier> targetResourceIdentifiers = theTargetPatientIdentifier.stream()
|
||||||
|
.map(IdentifierUtil::identifierDtFromIdentifier)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
mergeOperationParameters.setTargetResourceIdentifiers(targetResourceIdentifiers);
|
||||||
|
}
|
||||||
|
mergeOperationParameters.setSourceResource(theSourcePatient);
|
||||||
|
mergeOperationParameters.setTargetResource(theTargetPatient);
|
||||||
|
mergeOperationParameters.setPreview(thePreview != null && thePreview.getValue());
|
||||||
|
mergeOperationParameters.setResultPatient((Patient) theResultPatient);
|
||||||
|
return mergeOperationParameters;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a list of string types, return only the ID portions of any parameters passed in.
|
* Given a list of string types, return only the ID portions of any parameters passed in.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -22,7 +22,6 @@ package ca.uhn.fhir.mdm.util;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.mdm.model.CanonicalEID;
|
import ca.uhn.fhir.mdm.model.CanonicalEID;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|
||||||
import ca.uhn.fhir.util.CanonicalIdentifier;
|
import ca.uhn.fhir.util.CanonicalIdentifier;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
|
||||||
|
@ -31,23 +30,7 @@ public final class IdentifierUtil {
|
||||||
private IdentifierUtil() {}
|
private IdentifierUtil() {}
|
||||||
|
|
||||||
public static CanonicalIdentifier identifierDtFromIdentifier(IBase theIdentifier) {
|
public static CanonicalIdentifier identifierDtFromIdentifier(IBase theIdentifier) {
|
||||||
CanonicalIdentifier retval = new CanonicalIdentifier();
|
return ca.uhn.fhir.util.IdentifierUtil.identifierDtFromIdentifier(theIdentifier);
|
||||||
|
|
||||||
// TODO add other fields like "use" etc
|
|
||||||
if (theIdentifier instanceof org.hl7.fhir.dstu3.model.Identifier) {
|
|
||||||
org.hl7.fhir.dstu3.model.Identifier ident = (org.hl7.fhir.dstu3.model.Identifier) theIdentifier;
|
|
||||||
retval.setSystem(ident.getSystem()).setValue(ident.getValue());
|
|
||||||
} else if (theIdentifier instanceof org.hl7.fhir.r4.model.Identifier) {
|
|
||||||
org.hl7.fhir.r4.model.Identifier ident = (org.hl7.fhir.r4.model.Identifier) theIdentifier;
|
|
||||||
retval.setSystem(ident.getSystem()).setValue(ident.getValue());
|
|
||||||
} else if (theIdentifier instanceof org.hl7.fhir.r5.model.Identifier) {
|
|
||||||
org.hl7.fhir.r5.model.Identifier ident = (org.hl7.fhir.r5.model.Identifier) theIdentifier;
|
|
||||||
retval.setSystem(ident.getSystem()).setValue(ident.getValue());
|
|
||||||
} else {
|
|
||||||
throw new InternalErrorException(Msg.code(1486) + "Expected 'Identifier' type but was '"
|
|
||||||
+ theIdentifier.getClass().getName() + "'");
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -252,6 +252,7 @@ public class ProviderConstants {
|
||||||
* Patient $merge operation parameters
|
* Patient $merge operation parameters
|
||||||
*/
|
*/
|
||||||
public static final String OPERATION_MERGE_SOURCE_PATIENT = "source-patient";
|
public static final String OPERATION_MERGE_SOURCE_PATIENT = "source-patient";
|
||||||
|
|
||||||
public static final String OPERATION_MERGE_SOURCE_PATIENT_IDENTIFIER = "source-patient-identifier";
|
public static final String OPERATION_MERGE_SOURCE_PATIENT_IDENTIFIER = "source-patient-identifier";
|
||||||
public static final String OPERATION_MERGE_TARGET_PATIENT = "target-patient";
|
public static final String OPERATION_MERGE_TARGET_PATIENT = "target-patient";
|
||||||
public static final String OPERATION_MERGE_TARGET_PATIENT_IDENTIFIER = "target-patient-identifier";
|
public static final String OPERATION_MERGE_TARGET_PATIENT_IDENTIFIER = "target-patient-identifier";
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Storage api
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* 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.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package ca.uhn.fhir.jpa.dao.merge;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.util.CanonicalIdentifier;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MergeOperationParameters {
|
||||||
|
|
||||||
|
private List<CanonicalIdentifier> mySourceResourceIdentifiers;
|
||||||
|
private List<CanonicalIdentifier> myTargetResourceIdentifiers;
|
||||||
|
private IBaseReference mySourceResource;
|
||||||
|
private IBaseReference myTargetResource;
|
||||||
|
private boolean myPreview;
|
||||||
|
|
||||||
|
// TODO: this can be changed to a generic resource to support other resources
|
||||||
|
private Patient myResultResource;
|
||||||
|
|
||||||
|
public List<CanonicalIdentifier> getSourceIdentifiers() {
|
||||||
|
return mySourceResourceIdentifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAtLeastOneSourceIdentifier() {
|
||||||
|
return mySourceResourceIdentifiers != null && !mySourceResourceIdentifiers.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceResourceIdentifiers(List<CanonicalIdentifier> theSourceIdentifiers) {
|
||||||
|
this.mySourceResourceIdentifiers = theSourceIdentifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CanonicalIdentifier> getTargetIdentifiers() {
|
||||||
|
return myTargetResourceIdentifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAtLeastOneTargetIdentifier() {
|
||||||
|
return myTargetResourceIdentifiers != null && !myTargetResourceIdentifiers.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetResourceIdentifiers(List<CanonicalIdentifier> theTargetIdentifiers) {
|
||||||
|
this.myTargetResourceIdentifiers = theTargetIdentifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPreview() {
|
||||||
|
return myPreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreview(boolean thePreview) {
|
||||||
|
this.myPreview = thePreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Patient getResultPatient() {
|
||||||
|
return myResultResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResultPatient(Patient theResultPatient) {
|
||||||
|
this.myResultResource = theResultPatient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBaseReference getSourceResource() {
|
||||||
|
return mySourceResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceResource(IBaseReference theSourceResource) {
|
||||||
|
this.mySourceResource = theSourceResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBaseReference getTargetResource() {
|
||||||
|
return myTargetResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetResource(IBaseReference theTargetResource) {
|
||||||
|
this.myTargetResource = theTargetResource;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Storage api
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* 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.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package ca.uhn.fhir.jpa.dao.merge;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.rest.api.Constants.STATUS_HTTP_400_BAD_REQUEST;
|
||||||
|
|
||||||
|
public class ResourceMergeService {
|
||||||
|
|
||||||
|
IFhirResourceDaoPatient<?> myDao;
|
||||||
|
FhirContext myFhirContext;
|
||||||
|
|
||||||
|
public ResourceMergeService(IFhirResourceDaoPatient<?> thePatientDao) {
|
||||||
|
myDao = thePatientDao;
|
||||||
|
myFhirContext = myDao.getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implemention of the $merge operation for resources
|
||||||
|
* @param theMergeOperationParameters the merge operation parameters
|
||||||
|
* @param theRequestDetails the request details
|
||||||
|
* @return the merge outcome containing OperationOutcome and HTTP status code
|
||||||
|
*/
|
||||||
|
public MergeOutcome merge(MergeOperationParameters theMergeOperationParameters, RequestDetails theRequestDetails) {
|
||||||
|
|
||||||
|
MergeOutcome mergeOutcome = new MergeOutcome();
|
||||||
|
IBaseOperationOutcome outcome = OperationOutcomeUtil.newInstance(myFhirContext);
|
||||||
|
mergeOutcome.setOperationOutcome(outcome);
|
||||||
|
|
||||||
|
if (!validateMergeOperationParameters(theMergeOperationParameters, outcome)) {
|
||||||
|
mergeOutcome.setHttpStatusCode(STATUS_HTTP_400_BAD_REQUEST);
|
||||||
|
return mergeOutcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergeOutcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the merge operation parameters and adds validation errors to the outcome
|
||||||
|
* @param theMergeOperationParameters the merge operation parameters
|
||||||
|
* @param theOutcome the outcome to add validation errors to
|
||||||
|
* @return true if the parameters are valid, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean validateMergeOperationParameters(
|
||||||
|
MergeOperationParameters theMergeOperationParameters, IBaseOperationOutcome theOutcome) {
|
||||||
|
List<String> errorMessages = new ArrayList<>();
|
||||||
|
if (!theMergeOperationParameters.hasAtLeastOneSourceIdentifier()
|
||||||
|
&& theMergeOperationParameters.getSourceResource() == null) {
|
||||||
|
errorMessages.add("There are no source resource parameters provided, include either a source-patient, "
|
||||||
|
+ "source-patient-identifier parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spec has conflicting information about this case
|
||||||
|
if (theMergeOperationParameters.hasAtLeastOneSourceIdentifier()
|
||||||
|
&& theMergeOperationParameters.getSourceResource() != null) {
|
||||||
|
errorMessages.add(
|
||||||
|
"Source patient must be provided either by source-patient-identifier or by source-resource, not both.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!theMergeOperationParameters.hasAtLeastOneTargetIdentifier()
|
||||||
|
&& theMergeOperationParameters.getTargetResource() == null) {
|
||||||
|
errorMessages.add("There are no target resource parameters provided, include either a target-patient, "
|
||||||
|
+ "target-patient-identifier parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spec has conflicting information about this case
|
||||||
|
if (theMergeOperationParameters.hasAtLeastOneTargetIdentifier()
|
||||||
|
&& theMergeOperationParameters.getTargetResource() != null) {
|
||||||
|
errorMessages.add("Target patient must be provided either by target-patient-identifier or by "
|
||||||
|
+ "target-resource, not both.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!errorMessages.isEmpty()) {
|
||||||
|
for (String validationError : errorMessages) {
|
||||||
|
OperationOutcomeUtil.addIssue(myFhirContext, theOutcome, "error", validationError, null, null);
|
||||||
|
}
|
||||||
|
// there are validation errors
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no validation errors
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MergeOutcome {
|
||||||
|
private IBaseOperationOutcome myOperationOutcome;
|
||||||
|
private int myHttpStatusCode;
|
||||||
|
|
||||||
|
public IBaseOperationOutcome getOperationOutcome() {
|
||||||
|
return myOperationOutcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOperationOutcome(IBaseOperationOutcome theOperationOutcome) {
|
||||||
|
this.myOperationOutcome = theOperationOutcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHttpStatusCode() {
|
||||||
|
return myHttpStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHttpStatusCode(int theHttpStatusCode) {
|
||||||
|
this.myHttpStatusCode = theHttpStatusCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Storage api
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* 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.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
|
||||||
|
public final class IdentifierUtil {
|
||||||
|
|
||||||
|
private IdentifierUtil() {}
|
||||||
|
|
||||||
|
public static CanonicalIdentifier identifierDtFromIdentifier(IBase theIdentifier) {
|
||||||
|
CanonicalIdentifier retval = new CanonicalIdentifier();
|
||||||
|
|
||||||
|
// TODO add other fields like "use" etc
|
||||||
|
if (theIdentifier instanceof org.hl7.fhir.dstu3.model.Identifier) {
|
||||||
|
org.hl7.fhir.dstu3.model.Identifier ident = (org.hl7.fhir.dstu3.model.Identifier) theIdentifier;
|
||||||
|
retval.setSystem(ident.getSystem()).setValue(ident.getValue());
|
||||||
|
} else if (theIdentifier instanceof org.hl7.fhir.r4.model.Identifier) {
|
||||||
|
org.hl7.fhir.r4.model.Identifier ident = (org.hl7.fhir.r4.model.Identifier) theIdentifier;
|
||||||
|
retval.setSystem(ident.getSystem()).setValue(ident.getValue());
|
||||||
|
} else if (theIdentifier instanceof org.hl7.fhir.r5.model.Identifier) {
|
||||||
|
org.hl7.fhir.r5.model.Identifier ident = (org.hl7.fhir.r5.model.Identifier) theIdentifier;
|
||||||
|
retval.setSystem(ident.getSystem()).setValue(ident.getValue());
|
||||||
|
} else {
|
||||||
|
throw new InternalErrorException(Msg.code(1486) + "Expected 'Identifier' type but was '"
|
||||||
|
+ theIdentifier.getClass().getName() + "'");
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.merge;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.util.CanonicalIdentifier;
|
||||||
|
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||||
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class ResourceMergeServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IFhirResourceDaoPatient<?> myDaoMock;
|
||||||
|
@Mock
|
||||||
|
RequestDetails myRequestDetailsMock;
|
||||||
|
|
||||||
|
private ResourceMergeService myResourceMergeService;
|
||||||
|
|
||||||
|
private final FhirContext myFhirContext = FhirContext.forR4Cached();
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() {
|
||||||
|
when(myDaoMock.getContext()).thenReturn(myFhirContext);
|
||||||
|
myResourceMergeService = new ResourceMergeService(myDaoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidatesInputParameters_MissingSourcePatientParams_ReturnsErrorInOutcomeWith400Status() {
|
||||||
|
// Given
|
||||||
|
MergeOperationParameters mergeOperationParameters = new MergeOperationParameters();
|
||||||
|
mergeOperationParameters.setTargetResource(new Reference("Patient/123"));
|
||||||
|
|
||||||
|
// When
|
||||||
|
ResourceMergeService.MergeOutcome mergeOutcome = myResourceMergeService.merge(mergeOperationParameters, myRequestDetailsMock);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
OperationOutcome operationOutcome = (OperationOutcome) mergeOutcome.getOperationOutcome();
|
||||||
|
assertThat(mergeOutcome.getHttpStatusCode()).isEqualTo(400);
|
||||||
|
assertThat(operationOutcome.getIssue()).hasSize(1);
|
||||||
|
assertThat(operationOutcome.getIssueFirstRep().getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR);
|
||||||
|
assertThat(operationOutcome.getIssueFirstRep().getDiagnostics()).contains("There are no source resource parameters provided, include either a source-patient, " +
|
||||||
|
"source-patient-identifier parameter.");
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(myDaoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidatesInputParameters_MissingTargetPatientParams_ReturnsErrorInOutcomeWith400Status() {
|
||||||
|
// Given
|
||||||
|
MergeOperationParameters mergeOperationParameters = new MergeOperationParameters();
|
||||||
|
mergeOperationParameters.setSourceResource(new Reference("Patient/123"));
|
||||||
|
|
||||||
|
// When
|
||||||
|
ResourceMergeService.MergeOutcome mergeOutcome = myResourceMergeService.merge(mergeOperationParameters, myRequestDetailsMock);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
OperationOutcome operationOutcome = (OperationOutcome) mergeOutcome.getOperationOutcome();
|
||||||
|
assertThat(mergeOutcome.getHttpStatusCode()).isEqualTo(400);
|
||||||
|
|
||||||
|
assertThat(operationOutcome.getIssue()).hasSize(1);
|
||||||
|
assertThat(operationOutcome.getIssueFirstRep().getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR);
|
||||||
|
assertThat(operationOutcome.getIssueFirstRep().getDiagnostics()).contains("There are no target resource " +
|
||||||
|
"parameters provided, include either a target-patient, target-patient-identifier parameter.");
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(myDaoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidatesInputParameters_MissingBothSourceAndTargetPatientParams_ReturnsErrorsInOutcomeWith400Status() {
|
||||||
|
// Given
|
||||||
|
MergeOperationParameters mergeOperationParameters = new MergeOperationParameters();
|
||||||
|
|
||||||
|
// When
|
||||||
|
ResourceMergeService.MergeOutcome mergeOutcome = myResourceMergeService.merge(mergeOperationParameters, myRequestDetailsMock);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
OperationOutcome operationOutcome = (OperationOutcome) mergeOutcome.getOperationOutcome();
|
||||||
|
assertThat(mergeOutcome.getHttpStatusCode()).isEqualTo(400);
|
||||||
|
assertThat(operationOutcome.getIssue()).hasSize(2);
|
||||||
|
assertThat(operationOutcome.getIssue().get(0).getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR);
|
||||||
|
assertThat(operationOutcome.getIssue().get(0).getDiagnostics()).contains("There are no source resource " +
|
||||||
|
"parameters provided, include either a source-patient, source-patient-identifier parameter.");
|
||||||
|
assertThat(operationOutcome.getIssue().get(1).getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR);
|
||||||
|
assertThat(operationOutcome.getIssue().get(1).getDiagnostics()).contains("There are no target resource " +
|
||||||
|
"parameters provided, include either a target-patient, target-patient-identifier parameter.");
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(myDaoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidatesInputParameters_BothSourceResourceParamsProvided_ReturnsErrorInOutcomeWith400Status() {
|
||||||
|
// Given
|
||||||
|
MergeOperationParameters mergeOperationParameters = new MergeOperationParameters();
|
||||||
|
mergeOperationParameters.setSourceResource(new Reference("Patient/123"));
|
||||||
|
mergeOperationParameters.setSourceResourceIdentifiers(List.of(new CanonicalIdentifier().setSystem("sys").setValue( "val")));
|
||||||
|
mergeOperationParameters.setTargetResource(new Reference("Patient/345"));
|
||||||
|
// When
|
||||||
|
ResourceMergeService.MergeOutcome mergeOutcome = myResourceMergeService.merge(mergeOperationParameters, myRequestDetailsMock);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
OperationOutcome operationOutcome = (OperationOutcome) mergeOutcome.getOperationOutcome();
|
||||||
|
assertThat(mergeOutcome.getHttpStatusCode()).isEqualTo(400);
|
||||||
|
|
||||||
|
assertThat(operationOutcome.getIssue()).hasSize(1);
|
||||||
|
assertThat(operationOutcome.getIssueFirstRep().getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR);
|
||||||
|
assertThat(operationOutcome.getIssueFirstRep().getDiagnostics()).contains("Source patient must be provided " +
|
||||||
|
"either by source-patient-identifier or by source-resource, not both.");
|
||||||
|
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(myDaoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidatesInputParameters_BothTargetResourceParamsProvided_ReturnsErrorInOutcomeWith400Status() {
|
||||||
|
// Given
|
||||||
|
MergeOperationParameters mergeOperationParameters = new MergeOperationParameters();
|
||||||
|
mergeOperationParameters.setTargetResource(new Reference("Patient/123"));
|
||||||
|
mergeOperationParameters.setTargetResourceIdentifiers(List.of(new CanonicalIdentifier().setSystem("sys").setValue( "val")));
|
||||||
|
mergeOperationParameters.setSourceResource(new Reference("Patient/345"));
|
||||||
|
// When
|
||||||
|
ResourceMergeService.MergeOutcome mergeOutcome = myResourceMergeService.merge(mergeOperationParameters, myRequestDetailsMock);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
OperationOutcome operationOutcome = (OperationOutcome) mergeOutcome.getOperationOutcome();
|
||||||
|
assertThat(mergeOutcome.getHttpStatusCode()).isEqualTo(400);
|
||||||
|
|
||||||
|
assertThat(operationOutcome.getIssue()).hasSize(1);
|
||||||
|
assertThat(operationOutcome.getIssueFirstRep().getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR);
|
||||||
|
assertThat(operationOutcome.getIssueFirstRep().getDiagnostics()).contains("Target patient must be provided " +
|
||||||
|
"either by target-patient-identifier or by target-resource, not both.");
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(myDaoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue