Cleanup member-match handler (#4431)
Cleanup member-match helper. Pass RequestDetails down for partitioning context.
This commit is contained in:
parent
21b1820294
commit
0d79d2026e
|
@ -168,3 +168,4 @@ Snap.*
|
||||||
|
|
||||||
|
|
||||||
/database/
|
/database/
|
||||||
|
/.run/
|
||||||
|
|
|
@ -12,7 +12,7 @@ import ca.uhn.fhir.jpa.dao.r4.TransactionProcessorVersionAdapterR4;
|
||||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||||
import ca.uhn.fhir.jpa.graphql.GraphQLProviderWithIntrospection;
|
import ca.uhn.fhir.jpa.graphql.GraphQLProviderWithIntrospection;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.IConsentExtensionProvider;
|
import ca.uhn.fhir.jpa.provider.r4.IMemberMatchConsentHook;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.MemberMatchR4ResourceProvider;
|
import ca.uhn.fhir.jpa.provider.r4.MemberMatchR4ResourceProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.MemberMatcherR4Helper;
|
import ca.uhn.fhir.jpa.provider.r4.MemberMatcherR4Helper;
|
||||||
import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl;
|
import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl;
|
||||||
|
@ -105,7 +105,7 @@ public class JpaR4Config {
|
||||||
@Autowired IFhirResourceDao<Coverage> theCoverageDao,
|
@Autowired IFhirResourceDao<Coverage> theCoverageDao,
|
||||||
@Autowired IFhirResourceDao<Patient> thePatientDao,
|
@Autowired IFhirResourceDao<Patient> thePatientDao,
|
||||||
@Autowired IFhirResourceDao<Consent> theConsentDao,
|
@Autowired IFhirResourceDao<Consent> theConsentDao,
|
||||||
@Autowired(required = false) IConsentExtensionProvider theExtensionProvider
|
@Autowired(required = false) IMemberMatchConsentHook theExtensionProvider
|
||||||
) {
|
) {
|
||||||
return new MemberMatcherR4Helper(
|
return new MemberMatcherR4Helper(
|
||||||
theContext,
|
theContext,
|
||||||
|
|
|
@ -20,22 +20,34 @@ package ca.uhn.fhir.jpa.provider.r4;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.util.ExtensionUtil;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public interface IConsentExtensionProvider {
|
/**
|
||||||
|
* Hook for Consent pre-save additions.
|
||||||
/**
|
|
||||||
* Takes a Consent resource and returns a collection of Extensions that will
|
|
||||||
* be added to the base resource.
|
|
||||||
*
|
*
|
||||||
* @param theConsentResource - the consent resource
|
* @deprecated - we just use Consumer now
|
||||||
* @return - a collection of resources (or an empty collection if none).
|
* TODO delete this.
|
||||||
*/
|
*/
|
||||||
default Collection<IBaseExtension> getConsentExtension(IBaseResource theConsentResource) {
|
@Deprecated(since = "6.3.6", forRemoval = true)
|
||||||
return Collections.emptyList();
|
public interface IConsentExtensionProvider extends IMemberMatchConsentHook {
|
||||||
};
|
Logger ourLog = LoggerFactory.getLogger(IConsentExtensionProvider.class);
|
||||||
|
|
||||||
|
Collection<IBaseExtension> getConsentExtension(IBaseResource theConsentResource);
|
||||||
|
|
||||||
|
default void accept(IBaseResource theResource) {
|
||||||
|
Collection<IBaseExtension> extensions = getConsentExtension(theResource);
|
||||||
|
|
||||||
|
for (IBaseExtension ext : extensions) {
|
||||||
|
IBaseExtension<?, ?> e = ExtensionUtil.addExtension(theResource, ext.getUrl());
|
||||||
|
e.setValue(ext.getValue());
|
||||||
|
}
|
||||||
|
ourLog.trace("{} extension(s) added to Consent", extensions.size());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ca.uhn.fhir.jpa.provider.r4;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-save hook for Consent saved during $member-match.
|
||||||
|
*/
|
||||||
|
public interface IMemberMatchConsentHook extends Consumer<IBaseResource> {
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ import org.hl7.fhir.r4.model.Coverage;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class MemberMatchR4ResourceProvider {
|
public class MemberMatchR4ResourceProvider {
|
||||||
|
@ -76,32 +76,32 @@ public class MemberMatchR4ResourceProvider {
|
||||||
|
|
||||||
RequestDetails theRequestDetails
|
RequestDetails theRequestDetails
|
||||||
) {
|
) {
|
||||||
return doMemberMatchOperation(theServletRequest, theMemberPatient, oldCoverage, newCoverage, theConsent, theRequestDetails);
|
return doMemberMatchOperation(theMemberPatient, oldCoverage, newCoverage, theConsent, theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Parameters doMemberMatchOperation(HttpServletRequest theServletRequest, Patient theMemberPatient,
|
private Parameters doMemberMatchOperation(Patient theMemberPatient,
|
||||||
Coverage theCoverageToMatch, Coverage theCoverageToLink, Consent theConsent, RequestDetails theRequestDetails) {
|
Coverage theCoverageToMatch, Coverage theCoverageToLink, Consent theConsent, RequestDetails theRequestDetails) {
|
||||||
|
|
||||||
validateParams(theMemberPatient, theCoverageToMatch, theCoverageToLink, theConsent);
|
validateParams(theMemberPatient, theCoverageToMatch, theCoverageToLink, theConsent);
|
||||||
|
|
||||||
Optional<Coverage> coverageOpt = myMemberMatcherR4Helper.findMatchingCoverage(theCoverageToMatch);
|
Optional<Coverage> coverageOpt = myMemberMatcherR4Helper.findMatchingCoverage(theCoverageToMatch, theRequestDetails);
|
||||||
if ( ! coverageOpt.isPresent()) {
|
if (coverageOpt.isEmpty()) {
|
||||||
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
||||||
"operation.member.match.error.coverage.not.found");
|
"operation.member.match.error.coverage.not.found");
|
||||||
throw new UnprocessableEntityException(Msg.code(1155) + i18nMessage);
|
throw new UnprocessableEntityException(Msg.code(1155) + i18nMessage);
|
||||||
}
|
}
|
||||||
Coverage coverage = coverageOpt.get();
|
Coverage coverage = coverageOpt.get();
|
||||||
|
|
||||||
Optional<Patient> patientOpt = myMemberMatcherR4Helper.getBeneficiaryPatient(coverage);
|
Optional<Patient> patientOpt = myMemberMatcherR4Helper.getBeneficiaryPatient(coverage, theRequestDetails);
|
||||||
if (! patientOpt.isPresent()) {
|
if (patientOpt.isEmpty()) {
|
||||||
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
||||||
"operation.member.match.error.beneficiary.not.found");
|
"operation.member.match.error.beneficiary.not.found");
|
||||||
throw new UnprocessableEntityException(Msg.code(1156) + i18nMessage);
|
throw new UnprocessableEntityException(Msg.code(1156) + i18nMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
Patient patient = patientOpt.get();
|
Patient patient = patientOpt.get();
|
||||||
if (!myMemberMatcherR4Helper.validPatientMember(patient, theMemberPatient)) {
|
if (!myMemberMatcherR4Helper.validPatientMember(patient, theMemberPatient, theRequestDetails)) {
|
||||||
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
||||||
"operation.member.match.error.patient.not.found");
|
"operation.member.match.error.patient.not.found");
|
||||||
throw new UnprocessableEntityException(Msg.code(2146) + i18nMessage);
|
throw new UnprocessableEntityException(Msg.code(2146) + i18nMessage);
|
||||||
|
@ -120,7 +120,7 @@ public class MemberMatchR4ResourceProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
myMemberMatcherR4Helper.addMemberIdentifierToMemberPatient(theMemberPatient, patient.getIdentifierFirstRep());
|
myMemberMatcherR4Helper.addMemberIdentifierToMemberPatient(theMemberPatient, patient.getIdentifierFirstRep());
|
||||||
myMemberMatcherR4Helper.updateConsentForMemberMatch(theConsent, patient, theMemberPatient);
|
myMemberMatcherR4Helper.updateConsentForMemberMatch(theConsent, patient, theMemberPatient, theRequestDetails);
|
||||||
return myMemberMatcherR4Helper.buildSuccessReturnParameters(theMemberPatient, theCoverageToLink, theConsent);
|
return myMemberMatcherR4Helper.buildSuccessReturnParameters(theMemberPatient, theCoverageToLink, theConsent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ public class MemberMatchR4ResourceProvider {
|
||||||
validateConsentParam(theConsent);
|
validateConsentParam(theConsent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateParam(Object theParam, String theParamName) {
|
private void validateParam(@Nullable Object theParam, String theParamName) {
|
||||||
if (theParam == null) {
|
if (theParam == null) {
|
||||||
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
||||||
"operation.member.match.error.missing.parameter", theParamName);
|
"operation.member.match.error.missing.parameter", theParamName);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.param.DateParam;
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
@ -13,7 +14,6 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.ParametersUtil;
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
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;
|
||||||
|
@ -21,7 +21,6 @@ import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.Consent;
|
import org.hl7.fhir.r4.model.Consent;
|
||||||
import org.hl7.fhir.r4.model.Coverage;
|
import org.hl7.fhir.r4.model.Coverage;
|
||||||
import org.hl7.fhir.r4.model.Extension;
|
|
||||||
import org.hl7.fhir.r4.model.HumanName;
|
import org.hl7.fhir.r4.model.HumanName;
|
||||||
import org.hl7.fhir.r4.model.Identifier;
|
import org.hl7.fhir.r4.model.Identifier;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
|
@ -29,9 +28,9 @@ import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT;
|
import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT;
|
||||||
import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_IDENTIFIER;
|
import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_IDENTIFIER;
|
||||||
|
@ -59,7 +58,7 @@ import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class MemberMatcherR4Helper {
|
public class MemberMatcherR4Helper {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MemberMatcherR4Helper.class);
|
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MemberMatcherR4Helper.class);
|
||||||
|
|
||||||
private static final String OUT_COVERAGE_IDENTIFIER_CODE_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0203";
|
private static final String OUT_COVERAGE_IDENTIFIER_CODE_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0203";
|
||||||
private static final String OUT_COVERAGE_IDENTIFIER_CODE = "MB";
|
private static final String OUT_COVERAGE_IDENTIFIER_CODE = "MB";
|
||||||
|
@ -67,16 +66,14 @@ public class MemberMatcherR4Helper {
|
||||||
private static final String COVERAGE_TYPE = "Coverage";
|
private static final String COVERAGE_TYPE = "Coverage";
|
||||||
private static final String CONSENT_POLICY_REGULAR_TYPE = "regular";
|
private static final String CONSENT_POLICY_REGULAR_TYPE = "regular";
|
||||||
private static final String CONSENT_POLICY_SENSITIVE_TYPE = "sensitive";
|
private static final String CONSENT_POLICY_SENSITIVE_TYPE = "sensitive";
|
||||||
public static final String CONSENT_IDENTIFIER_CODE_SYSTEM = "https://smilecdr.com/fhir/ns/member-match-fixme";
|
public static final String CONSENT_IDENTIFIER_CODE_SYSTEM = "https://smilecdr.com/fhir/ns/member-match-source-client";
|
||||||
|
|
||||||
private final FhirContext myFhirContext;
|
private final FhirContext myFhirContext;
|
||||||
private final IFhirResourceDao<Coverage> myCoverageDao;
|
private final IFhirResourceDao<Coverage> myCoverageDao;
|
||||||
private final IFhirResourceDao<Patient> myPatientDao;
|
private final IFhirResourceDao<Patient> myPatientDao;
|
||||||
private final IFhirResourceDao<Consent> myConsentDao;
|
private final IFhirResourceDao<Consent> myConsentDao;
|
||||||
// by default, not provided
|
/** A hook to modify the Consent before save */
|
||||||
// but if it is, extensions can be added to Consent on $member-match
|
private final Consumer<IBaseResource> myConsentModifier;
|
||||||
@Nullable
|
|
||||||
private final IConsentExtensionProvider myIConsentExtensionProvider;
|
|
||||||
|
|
||||||
private boolean myRegularFilterSupported = false;
|
private boolean myRegularFilterSupported = false;
|
||||||
|
|
||||||
|
@ -85,27 +82,27 @@ public class MemberMatcherR4Helper {
|
||||||
IFhirResourceDao<Coverage> theCoverageDao,
|
IFhirResourceDao<Coverage> theCoverageDao,
|
||||||
IFhirResourceDao<Patient> thePatientDao,
|
IFhirResourceDao<Patient> thePatientDao,
|
||||||
IFhirResourceDao<Consent> theConsentDao,
|
IFhirResourceDao<Consent> theConsentDao,
|
||||||
@Nullable IConsentExtensionProvider theExtensionProvider
|
@Nullable IMemberMatchConsentHook theConsentModifier
|
||||||
) {
|
) {
|
||||||
myFhirContext = theContext;
|
myFhirContext = theContext;
|
||||||
myConsentDao = theConsentDao;
|
myConsentDao = theConsentDao;
|
||||||
myPatientDao = thePatientDao;
|
myPatientDao = thePatientDao;
|
||||||
myCoverageDao = theCoverageDao;
|
myCoverageDao = theCoverageDao;
|
||||||
myIConsentExtensionProvider = theExtensionProvider;
|
myConsentModifier = (theConsentModifier != null) ? theConsentModifier : noop -> {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find Coverage matching the received member (Patient) by coverage id or by coverage identifier only
|
* Find Coverage matching the received member (Patient) by coverage id or by coverage identifier only
|
||||||
*/
|
*/
|
||||||
public Optional<Coverage> findMatchingCoverage(Coverage theCoverageToMatch) {
|
public Optional<Coverage> findMatchingCoverage(Coverage theCoverageToMatch, RequestDetails theRequestDetails) {
|
||||||
// search by received old coverage id
|
// search by received old coverage id
|
||||||
List<IBaseResource> foundCoverages = findCoverageByCoverageId(theCoverageToMatch);
|
List<IBaseResource> foundCoverages = findCoverageByCoverageId(theCoverageToMatch, theRequestDetails);
|
||||||
if (foundCoverages.size() == 1 && isCoverage(foundCoverages.get(0))) {
|
if (foundCoverages.size() == 1 && isCoverage(foundCoverages.get(0))) {
|
||||||
return Optional.of((Coverage) foundCoverages.get(0));
|
return Optional.of((Coverage) foundCoverages.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// search by received old coverage identifier
|
// search by received old coverage identifier
|
||||||
foundCoverages = findCoverageByCoverageIdentifier(theCoverageToMatch);
|
foundCoverages = findCoverageByCoverageIdentifier(theCoverageToMatch, theRequestDetails);
|
||||||
if (foundCoverages.size() == 1 && isCoverage(foundCoverages.get(0))) {
|
if (foundCoverages.size() == 1 && isCoverage(foundCoverages.get(0))) {
|
||||||
return Optional.of((Coverage) foundCoverages.get(0));
|
return Optional.of((Coverage) foundCoverages.get(0));
|
||||||
}
|
}
|
||||||
|
@ -114,7 +111,7 @@ public class MemberMatcherR4Helper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<IBaseResource> findCoverageByCoverageIdentifier(Coverage theCoverageToMatch) {
|
private List<IBaseResource> findCoverageByCoverageIdentifier(Coverage theCoverageToMatch, RequestDetails theRequestDetails) {
|
||||||
TokenOrListParam identifierParam = new TokenOrListParam();
|
TokenOrListParam identifierParam = new TokenOrListParam();
|
||||||
for (Identifier identifier : theCoverageToMatch.getIdentifier()) {
|
for (Identifier identifier : theCoverageToMatch.getIdentifier()) {
|
||||||
identifierParam.add(identifier.getSystem(), identifier.getValue());
|
identifierParam.add(identifier.getSystem(), identifier.getValue());
|
||||||
|
@ -122,7 +119,7 @@ public class MemberMatcherR4Helper {
|
||||||
|
|
||||||
SearchParameterMap paramMap = new SearchParameterMap()
|
SearchParameterMap paramMap = new SearchParameterMap()
|
||||||
.add("identifier", identifierParam);
|
.add("identifier", identifierParam);
|
||||||
ca.uhn.fhir.rest.api.server.IBundleProvider retVal = myCoverageDao.search(paramMap);
|
ca.uhn.fhir.rest.api.server.IBundleProvider retVal = myCoverageDao.search(paramMap, theRequestDetails);
|
||||||
|
|
||||||
return retVal.getAllResources();
|
return retVal.getAllResources();
|
||||||
}
|
}
|
||||||
|
@ -133,21 +130,23 @@ public class MemberMatcherR4Helper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<IBaseResource> findCoverageByCoverageId(Coverage theCoverageToMatch) {
|
private List<IBaseResource> findCoverageByCoverageId(Coverage theCoverageToMatch, RequestDetails theRequestDetails) {
|
||||||
SearchParameterMap paramMap = new SearchParameterMap()
|
SearchParameterMap paramMap = new SearchParameterMap()
|
||||||
.add("_id", new StringParam(theCoverageToMatch.getId()));
|
.add("_id", new StringParam(theCoverageToMatch.getId()));
|
||||||
ca.uhn.fhir.rest.api.server.IBundleProvider retVal = myCoverageDao.search(paramMap);
|
ca.uhn.fhir.rest.api.server.IBundleProvider retVal = myCoverageDao.search(paramMap, theRequestDetails);
|
||||||
|
|
||||||
return retVal.getAllResources();
|
return retVal.getAllResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateConsentForMemberMatch(Consent theConsent, Patient thePatient, Patient theMemberPatient) {
|
public void updateConsentForMemberMatch(Consent theConsent, Patient thePatient, Patient theMemberPatient, RequestDetails theRequestDetails) {
|
||||||
addClientIdAsExtensionToConsentIfAvailable(theConsent);
|
|
||||||
addIdentifierToConsent(theConsent, theMemberPatient);
|
addIdentifierToConsent(theConsent, theMemberPatient);
|
||||||
updateConsentPatientAndPerformer(theConsent, thePatient);
|
updateConsentPatientAndPerformer(theConsent, thePatient);
|
||||||
|
myConsentModifier.accept(theConsent);
|
||||||
|
|
||||||
// save the resource
|
// WIPMB REVIEW QUESTION Which partition should we target?
|
||||||
myConsentDao.create(theConsent);
|
// Will RequestTenantPartitionInterceptor or PatientIdPartitionInterceptor do the right thing?
|
||||||
|
// Can we use the userdata field to hint at target partition?
|
||||||
|
myConsentDao.create(theConsent, theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Parameters buildSuccessReturnParameters(Patient theMemberPatient, Coverage theCoverage, Consent theConsent) {
|
public Parameters buildSuccessReturnParameters(Patient theMemberPatient, Coverage theCoverage, Consent theConsent) {
|
||||||
|
@ -160,24 +159,25 @@ public class MemberMatcherR4Helper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Identifier getIdentifier(Patient theMemberPatient) {
|
private Identifier getIdentifier(Patient theMemberPatient) {
|
||||||
List<Identifier> memberIdentifiers = theMemberPatient.getIdentifier();
|
return theMemberPatient.getIdentifier()
|
||||||
if (memberIdentifiers != null && memberIdentifiers.size() > 0) {
|
.stream()
|
||||||
for (Identifier memberIdentifier : memberIdentifiers) {
|
.filter(this::isTypeMB)
|
||||||
List<Coding> typeCodings = memberIdentifier.getType().getCoding();
|
.findFirst()
|
||||||
if (memberIdentifier.getType() != null && typeCodings != null && typeCodings.size() > 0) {
|
.orElseThrow(()->{
|
||||||
for (Coding typeCoding : typeCodings) {
|
|
||||||
if (typeCoding.getCode().equals("MB")) {
|
|
||||||
return memberIdentifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
String i18nMessage = myFhirContext.getLocalizer().getMessage(
|
||||||
"operation.member.match.error.beneficiary.without.identifier");
|
"operation.member.match.error.beneficiary.without.identifier");
|
||||||
throw new UnprocessableEntityException(Msg.code(2219) + i18nMessage);
|
return new UnprocessableEntityException(Msg.code(2219) + i18nMessage);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isTypeMB(Identifier theMemberIdentifier) {
|
||||||
|
return theMemberIdentifier.getType() != null &&
|
||||||
|
theMemberIdentifier.getType().getCoding()
|
||||||
|
.stream()
|
||||||
|
.anyMatch(typeCoding->typeCoding.getCode().equals("MB"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addMemberIdentifierToMemberPatient(Patient theMemberPatient, Identifier theNewIdentifier) {
|
public void addMemberIdentifierToMemberPatient(Patient theMemberPatient, Identifier theNewIdentifier) {
|
||||||
Coding coding = new Coding()
|
Coding coding = new Coding()
|
||||||
.setSystem(OUT_COVERAGE_IDENTIFIER_CODE_SYSTEM)
|
.setSystem(OUT_COVERAGE_IDENTIFIER_CODE_SYSTEM)
|
||||||
|
@ -198,31 +198,7 @@ public class MemberMatcherR4Helper {
|
||||||
theMemberPatient.addIdentifier(newIdentifier);
|
theMemberPatient.addIdentifier(newIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Optional<Patient> getBeneficiaryPatient(Coverage theCoverage, RequestDetails theRequestDetails) {
|
||||||
* If there is a client id
|
|
||||||
*
|
|
||||||
* @param theConsent - the consent to modify
|
|
||||||
*/
|
|
||||||
private void addClientIdAsExtensionToConsentIfAvailable(Consent theConsent) {
|
|
||||||
if (myIConsentExtensionProvider != null) {
|
|
||||||
Collection<IBaseExtension> extensions = myIConsentExtensionProvider.getConsentExtension(theConsent);
|
|
||||||
|
|
||||||
for (IBaseExtension ext : extensions) {
|
|
||||||
if (ext instanceof Extension) {
|
|
||||||
theConsent.addExtension((Extension) ext);
|
|
||||||
} else {
|
|
||||||
Extension extR4 = new Extension();
|
|
||||||
extR4.setUrl(ext.getUrl());
|
|
||||||
extR4.setValue(ext.getValue());
|
|
||||||
theConsent.addExtension(extR4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ourLog.trace("{} extension(s) added to Consent", extensions.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Patient> getBeneficiaryPatient(Coverage theCoverage) {
|
|
||||||
if (theCoverage.getBeneficiaryTarget() == null && theCoverage.getBeneficiary() == null) {
|
if (theCoverage.getBeneficiaryTarget() == null && theCoverage.getBeneficiary() == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
@ -245,15 +221,15 @@ public class MemberMatcherR4Helper {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
Patient beneficiary = myPatientDao.read(new IdDt(beneficiaryRef.getReference()));
|
Patient beneficiary = myPatientDao.read(new IdDt(beneficiaryRef.getReference()), theRequestDetails);
|
||||||
return Optional.ofNullable(beneficiary);
|
return Optional.ofNullable(beneficiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Matching by member patient demographics - family name and birthdate only
|
* Matching by member patient demographics - family name and birthdate only
|
||||||
*/
|
*/
|
||||||
public boolean validPatientMember(Patient thePatientFromContract, Patient thePatientToMatch) {
|
public boolean validPatientMember(Patient thePatientFromContract, Patient thePatientToMatch, RequestDetails theRequestDetails) {
|
||||||
if (thePatientFromContract == null || thePatientFromContract.getIdElement() == null) {
|
if (thePatientFromContract == null || thePatientFromContract.getIdElement() == null || thePatientToMatch == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
StringOrListParam familyName = new StringOrListParam();
|
StringOrListParam familyName = new StringOrListParam();
|
||||||
|
@ -263,7 +239,7 @@ public class MemberMatcherR4Helper {
|
||||||
SearchParameterMap map = new SearchParameterMap()
|
SearchParameterMap map = new SearchParameterMap()
|
||||||
.add("family", familyName)
|
.add("family", familyName)
|
||||||
.add("birthdate", new DateParam(thePatientToMatch.getBirthDateElement().getValueAsString()));
|
.add("birthdate", new DateParam(thePatientToMatch.getBirthDateElement().getValueAsString()));
|
||||||
ca.uhn.fhir.rest.api.server.IBundleProvider bundle = myPatientDao.search(map);
|
ca.uhn.fhir.rest.api.server.IBundleProvider bundle = myPatientDao.search(map, theRequestDetails);
|
||||||
for (IBaseResource patientResource : bundle.getAllResources()) {
|
for (IBaseResource patientResource : bundle.getAllResources()) {
|
||||||
IIdType patientId = patientResource.getIdElement().toUnqualifiedVersionless();
|
IIdType patientId = patientResource.getIdElement().toUnqualifiedVersionless();
|
||||||
if (patientId.getValue().equals(thePatientFromContract.getIdElement().toUnqualifiedVersionless().getValue())) {
|
if (patientId.getValue().equals(thePatientFromContract.getIdElement().toUnqualifiedVersionless().getValue())) {
|
||||||
|
@ -286,18 +262,16 @@ public class MemberMatcherR4Helper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is filtering the Consent policy data. The rule is specified in
|
* The consent policy rules are
|
||||||
* https://build.fhir.org/ig/HL7/davinci-ehrx/StructureDefinition-hrex-consent.html#notes
|
* <a href="https://build.fhir.org/ig/HL7/davinci-ehrx/StructureDefinition-hrex-consent.html#notes">
|
||||||
|
* described here.</a>
|
||||||
*/
|
*/
|
||||||
private boolean validConsentPolicy(String thePolicyUri) {
|
private boolean validConsentPolicy(String thePolicyUri) {
|
||||||
String policyTypes = StringUtils.substringAfterLast(thePolicyUri, "#");
|
String policyTypes = StringUtils.substringAfterLast(thePolicyUri, "#");
|
||||||
if (policyTypes.equals(CONSENT_POLICY_SENSITIVE_TYPE)) {
|
if (policyTypes.equals(CONSENT_POLICY_SENSITIVE_TYPE)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (policyTypes.equals(CONSENT_POLICY_REGULAR_TYPE) && myRegularFilterSupported) {
|
return policyTypes.equals(CONSENT_POLICY_REGULAR_TYPE) && myRegularFilterSupported;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addIdentifierToConsent(Consent theConsent, Patient thePatient) {
|
private void addIdentifierToConsent(Consent theConsent, Patient thePatient) {
|
||||||
|
|
|
@ -1,64 +1,60 @@
|
||||||
package ca.uhn.fhir.jpa.provider;
|
package ca.uhn.fhir.jpa.provider.r4;
|
||||||
|
|
||||||
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.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.IConsentExtensionProvider;
|
|
||||||
import ca.uhn.fhir.jpa.provider.r4.MemberMatcherR4Helper;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||||
|
import ca.uhn.test.util.LogbackCaptureTestExtension;
|
||||||
import ch.qos.logback.classic.Level;
|
import ch.qos.logback.classic.Level;
|
||||||
import ch.qos.logback.classic.Logger;
|
import ch.qos.logback.classic.Logger;
|
||||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
|
||||||
import ch.qos.logback.core.read.ListAppender;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.Consent;
|
import org.hl7.fhir.r4.model.Consent;
|
||||||
import org.hl7.fhir.r4.model.Coverage;
|
import org.hl7.fhir.r4.model.Coverage;
|
||||||
import org.hl7.fhir.r4.model.DateType;
|
import org.hl7.fhir.r4.model.DateType;
|
||||||
import org.hl7.fhir.r4.model.Extension;
|
|
||||||
import org.hl7.fhir.r4.model.HumanName;
|
import org.hl7.fhir.r4.model.HumanName;
|
||||||
import org.hl7.fhir.r4.model.Identifier;
|
import org.hl7.fhir.r4.model.Identifier;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Captor;
|
import org.mockito.Captor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static ca.uhn.fhir.jpa.provider.r4.MemberMatcherR4Helper.CONSENT_IDENTIFIER_CODE_SYSTEM;
|
|
||||||
import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT;
|
import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT;
|
||||||
import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_IDENTIFIER;
|
import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_IDENTIFIER;
|
||||||
import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT;
|
import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT;
|
||||||
import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE;
|
import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.isA;
|
import static org.mockito.ArgumentMatchers.isA;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.ArgumentMatchers.same;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
@ -66,11 +62,8 @@ import static org.mockito.Mockito.when;
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
public class MemberMatcherR4HelperTest {
|
public class MemberMatcherR4HelperTest {
|
||||||
|
|
||||||
private static final Logger ourLog = (Logger) LoggerFactory.getLogger(MemberMatcherR4Helper.class);
|
@RegisterExtension
|
||||||
|
LogbackCaptureTestExtension myLogCapture = new LogbackCaptureTestExtension((Logger) MemberMatcherR4Helper.ourLog, Level.TRACE);
|
||||||
@Mock
|
|
||||||
private ListAppender<ILoggingEvent> myAppender;
|
|
||||||
|
|
||||||
@Spy
|
@Spy
|
||||||
private final FhirContext myFhirContext = FhirContext.forR4();
|
private final FhirContext myFhirContext = FhirContext.forR4();
|
||||||
@Mock
|
@Mock
|
||||||
|
@ -81,6 +74,7 @@ public class MemberMatcherR4HelperTest {
|
||||||
private IFhirResourceDao<Consent> myConsentDao;
|
private IFhirResourceDao<Consent> myConsentDao;
|
||||||
|
|
||||||
private MemberMatcherR4Helper myHelper;
|
private MemberMatcherR4Helper myHelper;
|
||||||
|
RequestDetails myRequestDetails = new SystemRequestDetails();
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
|
@ -91,13 +85,6 @@ public class MemberMatcherR4HelperTest {
|
||||||
myConsentDao,
|
myConsentDao,
|
||||||
null // extension provider
|
null // extension provider
|
||||||
);
|
);
|
||||||
|
|
||||||
ourLog.addAppender(myAppender);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void after() {
|
|
||||||
ourLog.detachAppender(myAppender);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mock private Coverage myCoverageToMatch;
|
@Mock private Coverage myCoverageToMatch;
|
||||||
|
@ -114,13 +101,13 @@ public class MemberMatcherR4HelperTest {
|
||||||
@Test
|
@Test
|
||||||
void findMatchingCoverageMatchByIdReturnsMatched() {
|
void findMatchingCoverageMatchByIdReturnsMatched() {
|
||||||
when(myCoverageToMatch.getId()).thenReturn("cvg-to-match-id");
|
when(myCoverageToMatch.getId()).thenReturn("cvg-to-match-id");
|
||||||
when(myCoverageDao.search(isA(SearchParameterMap.class))).thenReturn(myBundleProvider);
|
when(myCoverageDao.search(isA(SearchParameterMap.class), same(myRequestDetails))).thenReturn(myBundleProvider);
|
||||||
when(myBundleProvider.getAllResources()).thenReturn(Collections.singletonList(myMatchedCoverage));
|
when(myBundleProvider.getAllResources()).thenReturn(Collections.singletonList(myMatchedCoverage));
|
||||||
|
|
||||||
Optional<Coverage> result = myHelper.findMatchingCoverage(myCoverageToMatch);
|
Optional<Coverage> result = myHelper.findMatchingCoverage(myCoverageToMatch, myRequestDetails);
|
||||||
|
|
||||||
assertEquals(Optional.of(myMatchedCoverage), result);
|
assertEquals(Optional.of(myMatchedCoverage), result);
|
||||||
verify(myCoverageDao).search(mySearchParameterMapCaptor.capture());
|
verify(myCoverageDao).search(mySearchParameterMapCaptor.capture(), same(myRequestDetails));
|
||||||
SearchParameterMap spMap = mySearchParameterMapCaptor.getValue();
|
SearchParameterMap spMap = mySearchParameterMapCaptor.getValue();
|
||||||
assertTrue(spMap.containsKey("_id"));
|
assertTrue(spMap.containsKey("_id"));
|
||||||
List<List<IQueryParameterType>> listListParams = spMap.get("_id");
|
List<List<IQueryParameterType>> listListParams = spMap.get("_id");
|
||||||
|
@ -135,14 +122,14 @@ public class MemberMatcherR4HelperTest {
|
||||||
void findMatchingCoverageMatchByIdentifierReturnsMatched() {
|
void findMatchingCoverageMatchByIdentifierReturnsMatched() {
|
||||||
when(myCoverageToMatch.getId()).thenReturn("non-matching-id");
|
when(myCoverageToMatch.getId()).thenReturn("non-matching-id");
|
||||||
when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier));
|
when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier));
|
||||||
when(myCoverageDao.search(isA(SearchParameterMap.class))).thenReturn(myBundleProvider);
|
when(myCoverageDao.search(isA(SearchParameterMap.class), same(myRequestDetails))).thenReturn(myBundleProvider);
|
||||||
when(myBundleProvider.getAllResources()).thenReturn(
|
when(myBundleProvider.getAllResources()).thenReturn(
|
||||||
Collections.emptyList(), Collections.singletonList(myMatchedCoverage));
|
Collections.emptyList(), Collections.singletonList(myMatchedCoverage));
|
||||||
|
|
||||||
Optional<Coverage> result = myHelper.findMatchingCoverage(myCoverageToMatch);
|
Optional<Coverage> result = myHelper.findMatchingCoverage(myCoverageToMatch, myRequestDetails);
|
||||||
|
|
||||||
assertEquals(Optional.of(myMatchedCoverage), result);
|
assertEquals(Optional.of(myMatchedCoverage), result);
|
||||||
verify(myCoverageDao, times(2)).search(mySearchParameterMapCaptor.capture());
|
verify(myCoverageDao, times(2)).search(mySearchParameterMapCaptor.capture(), same(myRequestDetails));
|
||||||
List<SearchParameterMap> spMap = mySearchParameterMapCaptor.getAllValues();
|
List<SearchParameterMap> spMap = mySearchParameterMapCaptor.getAllValues();
|
||||||
assertTrue(spMap.get(0).containsKey("_id"));
|
assertTrue(spMap.get(0).containsKey("_id"));
|
||||||
assertTrue(spMap.get(1).containsKey("identifier"));
|
assertTrue(spMap.get(1).containsKey("identifier"));
|
||||||
|
@ -159,10 +146,10 @@ public class MemberMatcherR4HelperTest {
|
||||||
void findMatchingCoverageNoMatchReturnsEmpty() {
|
void findMatchingCoverageNoMatchReturnsEmpty() {
|
||||||
when(myCoverageToMatch.getId()).thenReturn("non-matching-id");
|
when(myCoverageToMatch.getId()).thenReturn("non-matching-id");
|
||||||
when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier));
|
when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier));
|
||||||
when(myCoverageDao.search(any(SearchParameterMap.class))).thenReturn(myBundleProvider);
|
when(myCoverageDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenReturn(myBundleProvider);
|
||||||
when(myBundleProvider.getAllResources()).thenReturn(Collections.emptyList(), Collections.emptyList());
|
when(myBundleProvider.getAllResources()).thenReturn(Collections.emptyList(), Collections.emptyList());
|
||||||
|
|
||||||
Optional<Coverage> result = myHelper.findMatchingCoverage(myCoverageToMatch);
|
Optional<Coverage> result = myHelper.findMatchingCoverage(myCoverageToMatch, myRequestDetails);
|
||||||
|
|
||||||
assertFalse(result.isPresent());
|
assertFalse(result.isPresent());
|
||||||
}
|
}
|
||||||
|
@ -245,7 +232,7 @@ public class MemberMatcherR4HelperTest {
|
||||||
when(coverage.getBeneficiaryTarget()).thenReturn(null);
|
when(coverage.getBeneficiaryTarget()).thenReturn(null);
|
||||||
when(coverage.getBeneficiary()).thenReturn(null);
|
when(coverage.getBeneficiary()).thenReturn(null);
|
||||||
|
|
||||||
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage);
|
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails);
|
||||||
|
|
||||||
assertFalse(result.isPresent());
|
assertFalse(result.isPresent());
|
||||||
}
|
}
|
||||||
|
@ -256,7 +243,7 @@ public class MemberMatcherR4HelperTest {
|
||||||
when(coverage.getBeneficiary()).thenReturn(null);
|
when(coverage.getBeneficiary()).thenReturn(null);
|
||||||
when(coverage.getBeneficiaryTarget()).thenReturn(new Patient());
|
when(coverage.getBeneficiaryTarget()).thenReturn(new Patient());
|
||||||
|
|
||||||
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage);
|
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails);
|
||||||
|
|
||||||
assertFalse(result.isPresent());
|
assertFalse(result.isPresent());
|
||||||
}
|
}
|
||||||
|
@ -267,7 +254,7 @@ public class MemberMatcherR4HelperTest {
|
||||||
Patient patient = new Patient().setIdentifier(Collections.singletonList(new Identifier()));
|
Patient patient = new Patient().setIdentifier(Collections.singletonList(new Identifier()));
|
||||||
when(coverage.getBeneficiaryTarget()).thenReturn(patient);
|
when(coverage.getBeneficiaryTarget()).thenReturn(patient);
|
||||||
|
|
||||||
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage);
|
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails);
|
||||||
|
|
||||||
assertTrue(result.isPresent());
|
assertTrue(result.isPresent());
|
||||||
assertEquals(patient, result.get());
|
assertEquals(patient, result.get());
|
||||||
|
@ -280,7 +267,7 @@ public class MemberMatcherR4HelperTest {
|
||||||
when(coverage.getBeneficiaryTarget()).thenReturn(null);
|
when(coverage.getBeneficiaryTarget()).thenReturn(null);
|
||||||
when(coverage.getBeneficiary().getResource()).thenReturn(patient);
|
when(coverage.getBeneficiary().getResource()).thenReturn(patient);
|
||||||
|
|
||||||
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage);
|
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails);
|
||||||
|
|
||||||
assertTrue(result.isPresent());
|
assertTrue(result.isPresent());
|
||||||
assertEquals(patient, result.get());
|
assertEquals(patient, result.get());
|
||||||
|
@ -292,7 +279,7 @@ public class MemberMatcherR4HelperTest {
|
||||||
when(coverage.getBeneficiaryTarget()).thenReturn(null);
|
when(coverage.getBeneficiaryTarget()).thenReturn(null);
|
||||||
when(coverage.getBeneficiary()).thenReturn(new Reference());
|
when(coverage.getBeneficiary()).thenReturn(new Reference());
|
||||||
|
|
||||||
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage);
|
Optional<Patient> result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails);
|
||||||
|
|
||||||
assertFalse(result.isPresent());
|
assertFalse(result.isPresent());
|
||||||
}
|
}
|
||||||
|
@ -304,11 +291,10 @@ public class MemberMatcherR4HelperTest {
|
||||||
when(coverage.getBeneficiary().getResource()).thenReturn(null);
|
when(coverage.getBeneficiary().getResource()).thenReturn(null);
|
||||||
when(coverage.getBeneficiary().getReference()).thenReturn("patient-id");
|
when(coverage.getBeneficiary().getReference()).thenReturn("patient-id");
|
||||||
|
|
||||||
myHelper.getBeneficiaryPatient(coverage);
|
myHelper.getBeneficiaryPatient(coverage, myRequestDetails);
|
||||||
|
|
||||||
verify(myPatientDao).read(new IdDt("patient-id"));
|
verify(myPatientDao).read(new IdDt("patient-id"), myRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -317,19 +303,17 @@ public class MemberMatcherR4HelperTest {
|
||||||
@Nested
|
@Nested
|
||||||
public class TestValidPatientMember {
|
public class TestValidPatientMember {
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
private final Patient patient = new Patient();
|
||||||
private Coverage coverage;
|
|
||||||
private Patient patient;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void noPatientFoundFromContractReturnsFalse() {
|
void noPatientFoundFromContractReturnsFalse() {
|
||||||
boolean result = myHelper.validPatientMember(null, patient);
|
boolean result = myHelper.validPatientMember(null, patient, myRequestDetails);
|
||||||
assertFalse(result);
|
assertFalse(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void noPatientFoundFromPatientMemberReturnsFalse() {
|
void noPatientFoundFromPatientMemberReturnsFalse() {
|
||||||
boolean result = myHelper.validPatientMember(patient, null);
|
boolean result = myHelper.validPatientMember(patient, null, myRequestDetails);
|
||||||
assertFalse(result);
|
assertFalse(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,11 +321,11 @@ public class MemberMatcherR4HelperTest {
|
||||||
void noMatchingFamilyNameReturnsFalse() {
|
void noMatchingFamilyNameReturnsFalse() {
|
||||||
Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01");
|
Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01");
|
||||||
Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01");
|
Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01");
|
||||||
when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> {
|
when(myPatientDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenAnswer(t -> {
|
||||||
IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123")));
|
IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123")));
|
||||||
return provider;
|
return provider;
|
||||||
});
|
});
|
||||||
boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch);
|
boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch, myRequestDetails);
|
||||||
assertFalse(result);
|
assertFalse(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,11 +334,11 @@ public class MemberMatcherR4HelperTest {
|
||||||
void noMatchingBirthdayReturnsFalse() {
|
void noMatchingBirthdayReturnsFalse() {
|
||||||
Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01");
|
Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01");
|
||||||
Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01");
|
Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01");
|
||||||
when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> {
|
when(myPatientDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenAnswer(t -> {
|
||||||
IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123")));
|
IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123")));
|
||||||
return provider;
|
return provider;
|
||||||
});
|
});
|
||||||
boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch);
|
boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch, myRequestDetails);
|
||||||
assertFalse(result);
|
assertFalse(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,11 +346,11 @@ public class MemberMatcherR4HelperTest {
|
||||||
void noMatchingFieldsReturnsFalse() {
|
void noMatchingFieldsReturnsFalse() {
|
||||||
Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01");
|
Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01");
|
||||||
Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01");
|
Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01");
|
||||||
when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> {
|
when(myPatientDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenAnswer(t -> {
|
||||||
IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123")));
|
IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123")));
|
||||||
return provider;
|
return provider;
|
||||||
});
|
});
|
||||||
boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch);
|
boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch, myRequestDetails);
|
||||||
assertFalse(result);
|
assertFalse(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,11 +358,11 @@ public class MemberMatcherR4HelperTest {
|
||||||
void patientMatchingReturnTrue() {
|
void patientMatchingReturnTrue() {
|
||||||
Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01");
|
Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01");
|
||||||
Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01");
|
Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01");
|
||||||
when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> {
|
when(myPatientDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenAnswer(t -> {
|
||||||
IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(patientFromContractFound));
|
IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(patientFromContractFound));
|
||||||
return provider;
|
return provider;
|
||||||
});
|
});
|
||||||
boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch);
|
boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch, myRequestDetails);
|
||||||
assertTrue(result);
|
assertTrue(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,9 +388,6 @@ public class MemberMatcherR4HelperTest {
|
||||||
@Nested
|
@Nested
|
||||||
public class TestValidvalidConsentDataAccess {
|
public class TestValidvalidConsentDataAccess {
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
|
||||||
private Coverage coverage;
|
|
||||||
private Patient patient;
|
|
||||||
private Consent consent;
|
private Consent consent;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -521,45 +502,10 @@ public class MemberMatcherR4HelperTest {
|
||||||
return patient;
|
return patient;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyConsentUpdatedAfterMemberMatch(
|
|
||||||
Consent theConsent,
|
|
||||||
Patient thePatient,
|
|
||||||
Patient theMemberPatient,
|
|
||||||
List<Extension> theSavedExtensions
|
|
||||||
) {
|
|
||||||
// check consent identifier
|
|
||||||
assertEquals(1, theConsent.getIdentifier().size());
|
|
||||||
assertEquals(CONSENT_IDENTIFIER_CODE_SYSTEM, theConsent.getIdentifier().get(0).getSystem());
|
|
||||||
assertNotNull(theConsent.getIdentifier().get(0).getValue());
|
|
||||||
assertEquals(theConsent.getIdentifier().get(0).getValue(), theMemberPatient.getIdentifier().get(0).getValue());
|
|
||||||
|
|
||||||
// check consent patient info
|
|
||||||
String patientRef = thePatient.getIdElement().toUnqualifiedVersionless().getValue();
|
|
||||||
assertEquals(patientRef, theConsent.getPatient().getReference());
|
|
||||||
assertEquals(patientRef, theConsent.getPerformer().get(0).getReference());
|
|
||||||
|
|
||||||
if (!theSavedExtensions.isEmpty()) {
|
|
||||||
// check consent extensions
|
|
||||||
assertNotNull(theConsent.getExtension());
|
|
||||||
assertEquals(theSavedExtensions.size(), theConsent.getExtension().size());
|
|
||||||
for (Extension ext : theSavedExtensions) {
|
|
||||||
boolean found = false;
|
|
||||||
for (Extension consentExt : theConsent.getExtension()) {
|
|
||||||
if (consentExt.getUrl().equals(ext.getUrl())
|
|
||||||
&& consentExt.getValue().equals(ext.getValue())) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertTrue(found,
|
|
||||||
"Extension " + ext.getUrl() + "|" + ext.getValue().toString() + " not found"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
public class MemberMatchWithoutConsentProvider {
|
public class MemberMatchWithoutConsentProvider {
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<Consent> myDaoCaptor;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updateConsentForMemberMatch_noProvider_addsIdentifierUpdatePatientButNotExtensionAndSaves() {
|
public void updateConsentForMemberMatch_noProvider_addsIdentifierUpdatePatientButNotExtensionAndSaves() {
|
||||||
|
@ -569,19 +515,26 @@ public class MemberMatcherR4HelperTest {
|
||||||
Patient patient = createPatientForMemberMatchUpdate(false);
|
Patient patient = createPatientForMemberMatchUpdate(false);
|
||||||
Patient memberPatient = createPatientForMemberMatchUpdate(true);
|
Patient memberPatient = createPatientForMemberMatchUpdate(true);
|
||||||
|
|
||||||
ourLog.setLevel(Level.TRACE);
|
|
||||||
|
|
||||||
// test
|
// test
|
||||||
myHelper.updateConsentForMemberMatch(consent, patient, memberPatient);
|
myHelper.updateConsentForMemberMatch(consent, patient, memberPatient, myRequestDetails);
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
verify(myAppender, never())
|
|
||||||
.doAppend(any(ILoggingEvent.class));
|
|
||||||
|
|
||||||
ArgumentCaptor<Consent> consentCaptor = ArgumentCaptor.forClass(Consent.class);
|
verify(myConsentDao).create(myDaoCaptor.capture(), same(myRequestDetails));
|
||||||
verify(myConsentDao).create(consentCaptor.capture());
|
Consent saved = myDaoCaptor.getValue();
|
||||||
Consent saved = consentCaptor.getValue();
|
// check consent identifier
|
||||||
verifyConsentUpdatedAfterMemberMatch(saved, patient, memberPatient, Collections.emptyList());
|
assertEquals(1, saved.getIdentifier().size());
|
||||||
|
assertEquals(MemberMatcherR4Helper.CONSENT_IDENTIFIER_CODE_SYSTEM, saved.getIdentifier().get(0).getSystem());
|
||||||
|
assertNotNull(saved.getIdentifier().get(0).getValue());
|
||||||
|
assertEquals(saved.getIdentifier().get(0).getValue(), memberPatient.getIdentifier().get(0).getValue());
|
||||||
|
|
||||||
|
// check consent patient info
|
||||||
|
String patientRef = patient.getIdElement().toUnqualifiedVersionless().getValue();
|
||||||
|
assertEquals(patientRef, saved.getPatient().getReference());
|
||||||
|
assertEquals(patientRef, saved.getPerformer().get(0).getReference());
|
||||||
|
|
||||||
|
assertThat(myLogCapture.getLogEvents(), empty());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,6 +542,8 @@ public class MemberMatcherR4HelperTest {
|
||||||
public class MemberMatchWithConsentProvider {
|
public class MemberMatchWithConsentProvider {
|
||||||
@Mock
|
@Mock
|
||||||
private IConsentExtensionProvider myExtensionProvider;
|
private IConsentExtensionProvider myExtensionProvider;
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<Consent> myHookCaptor;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
|
@ -607,31 +562,17 @@ public class MemberMatcherR4HelperTest {
|
||||||
Consent consent = getConsent();
|
Consent consent = getConsent();
|
||||||
consent.addPolicy(constructConsentPolicyComponent("#sensitive"));
|
consent.addPolicy(constructConsentPolicyComponent("#sensitive"));
|
||||||
consent.setId("Consent/RED");
|
consent.setId("Consent/RED");
|
||||||
Extension ext = new Extension();
|
|
||||||
ext.setUrl("http://example.com");
|
|
||||||
ext.setValue(new StringType("value"));
|
|
||||||
Patient patient = createPatientForMemberMatchUpdate(false);
|
Patient patient = createPatientForMemberMatchUpdate(false);
|
||||||
Patient memberPatient = createPatientForMemberMatchUpdate(true);
|
Patient memberPatient = createPatientForMemberMatchUpdate(true);
|
||||||
|
|
||||||
ourLog.setLevel(Level.TRACE);
|
|
||||||
|
|
||||||
// when
|
|
||||||
when(myExtensionProvider.getConsentExtension(any(IBaseResource.class)))
|
|
||||||
.thenReturn(Collections.singleton(ext));
|
|
||||||
|
|
||||||
// test
|
// test
|
||||||
myHelper.updateConsentForMemberMatch(consent, patient, memberPatient);
|
myHelper.updateConsentForMemberMatch(consent, patient, memberPatient, myRequestDetails);
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
ArgumentCaptor<Consent> consentCaptor = ArgumentCaptor.forClass(Consent.class);
|
verify(myExtensionProvider).accept(myHookCaptor.capture());
|
||||||
verify(myConsentDao).create(consentCaptor.capture());
|
|
||||||
Consent saved = consentCaptor.getValue();
|
|
||||||
verifyConsentUpdatedAfterMemberMatch(saved, patient, memberPatient, Collections.emptyList());
|
|
||||||
|
|
||||||
ArgumentCaptor<ILoggingEvent> eventCaptor = ArgumentCaptor.forClass(ILoggingEvent.class);
|
assertSame(consent, myHookCaptor.getValue());
|
||||||
verify(myAppender).doAppend(eventCaptor.capture());
|
assertThat(myLogCapture.getLogEvents(), empty());
|
||||||
ILoggingEvent event = eventCaptor.getValue();
|
|
||||||
assertEquals("1 extension(s) added to Consent", event.getFormattedMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -113,13 +113,13 @@ public class LogbackCaptureTestExtension implements BeforeEachCallback, AfterEac
|
||||||
* Guts of beforeEach exposed for manual lifecycle.
|
* Guts of beforeEach exposed for manual lifecycle.
|
||||||
*/
|
*/
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
myListAppender = new ListAppender<>();
|
||||||
|
myListAppender.start();
|
||||||
|
myLogger.addAppender(myListAppender);
|
||||||
if (myLevel != null) {
|
if (myLevel != null) {
|
||||||
mySavedLevel = myLogger.getLevel();
|
mySavedLevel = myLogger.getLevel();
|
||||||
myLogger.setLevel(myLevel);
|
myLogger.setLevel(myLevel);
|
||||||
}
|
}
|
||||||
myListAppender = new ListAppender<>();
|
|
||||||
myListAppender.start();
|
|
||||||
myLogger.addAppender(myListAppender);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue