From 1b55f49a60f93b845b8e5d1d567584fb7ef032bb Mon Sep 17 00:00:00 2001 From: samuelwlee2 <100384063+samuelwlee2@users.noreply.github.com> Date: Mon, 31 Oct 2022 11:33:54 -0600 Subject: [PATCH] 4153 extend $member match operation to store consent resource if matched (#4165) * added senstive and regular filter * add UUID to Consent * blah * fix merge with master * fixing merge * blah * fhir tests * use constructor injection * put all tests together * blah * blah * finalize * add consent update patient and performer reference * fixes after code review * blah * refactor to save consent once * blah * fix performer and add validation * code clean up * add changelog * add hrex member match flag * fix PatientMemberMatchOperationR4Test * adding code fixes after code review * move changelog file * bump version Co-authored-by: leif stawnyczy --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- .../java/ca/uhn/fhir/rest/api/Constants.java | 3 + .../ca/uhn/fhir/i18n/hapi-messages.properties | 2 +- hapi-fhir-batch/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- ...ber-match-to-store-consent-if-matched.yaml | 4 + hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../ca/uhn/fhir/jpa/config/JpaConfig.java | 22 +- .../uhn/fhir/jpa/config/r4/JpaR4Config.java | 37 +- .../r4/IConsentExtensionProvider.java | 21 + .../r4/MemberMatchR4ResourceProvider.java | 34 +- .../provider/r4/MemberMatcherR4Helper.java | 123 +++- .../provider/MemberMatcherR4HelperTest.java | 597 ++++++++++++++++++ hapi-fhir-jpaserver-cql/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- .../uhn/fhir/jpa/model/util/JpaConstants.java | 5 + hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- .../r4/MemberMatcherR4HelperTest.java | 327 ---------- .../r4/PatientMemberMatchOperationR4Test.java | 179 +++++- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r4b/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../java/ca/uhn/fhir/to/BaseController.java | 8 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 18 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 4 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 82 files changed, 1060 insertions(+), 460 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_3_0/4153-extend-member-match-to-store-consent-if-matched.yaml create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IConsentExtensionProvider.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/MemberMatcherR4HelperTest.java delete mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 0f912c19794..e2b1a4e85c4 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index ce84d65ced5..6c2342e66dd 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 5929271e832..37aa8760b9b 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index 7be590b3469..2d49950d01a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -210,8 +210,11 @@ public class Constants { public static final String PARAM_MEMBER_PATIENT = "MemberPatient"; public static final String PARAM_OLD_COVERAGE = "OldCoverage"; public static final String PARAM_NEW_COVERAGE = "NewCoverage"; + public static final String PARAM_CONSENT = "Consent"; public static final String PARAM_MEMBER_PATIENT_NAME = PARAM_MEMBER_PATIENT + " Name"; public static final String PARAM_MEMBER_PATIENT_BIRTHDATE = PARAM_MEMBER_PATIENT + " Birthdate"; + public static final String PARAM_CONSENT_PATIENT_REFERENCE = PARAM_CONSENT + "'s Patient Reference"; + public static final String PARAM_CONSENT_PERFORMER_REFERENCE = PARAM_CONSENT + "'s Performer Reference"; public static final String PARAMQUALIFIER_MISSING = ":missing"; public static final String PARAMQUALIFIER_MISSING_FALSE = "false"; diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index 46cee008a5e..455ee02f3a6 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -190,4 +190,4 @@ operation.member.match.error.beneficiary.not.found=Could not find beneficiary fo operation.member.match.error.missing.parameter=Parameter "{0}" is required. operation.member.match.error.beneficiary.without.identifier=Coverage beneficiary does not have an identifier. operation.member.match.error.patient.not.found=Could not find matching patient for coverage. - +operation.member.match.error.consent.release.data.mismatch=Consent policy does not match the data release segmentation capabilities. diff --git a/hapi-fhir-batch/pom.xml b/hapi-fhir-batch/pom.xml index 5d3d17b7998..4c835e08e17 100644 --- a/hapi-fhir-batch/pom.xml +++ b/hapi-fhir-batch/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 4630a252af7..68e9fcd26b1 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -3,14 +3,14 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 5a600f7227b..6b762465e87 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index f95564325cb..201b0a61378 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 6bb03060a21..f025c1daf09 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index f68da1908d3..9426400ce16 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 52d53c7ec48..1d093cab89b 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 01c15a20474..ce54439eed4 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index b34796331b8..83c21fee9d3 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 29cd0fa0aa0..21a43107a8b 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 9be262111c9..0672fad4433 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 38c6685dbd3..ca16d485654 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_3_0/4153-extend-member-match-to-store-consent-if-matched.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_3_0/4153-extend-member-match-to-store-consent-if-matched.yaml new file mode 100644 index 00000000000..157ce31e627 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_3_0/4153-extend-member-match-to-store-consent-if-matched.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 4153 +title: "Extend $member-match to store Consent resource when there is a matching patient." diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index c503e374500..d43c4d57588 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index ebf8794ee2a..a3ebdf61763 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 63616d5dcdd..fdaed9ee04e 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index b08fca2910f..1a18839bc90 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index e38742ae454..3074016e1a2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -8,6 +8,7 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.executor.InterceptorService; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.ExpungeOptions; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; @@ -63,6 +64,7 @@ import ca.uhn.fhir.jpa.provider.DiffProvider; import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; +import ca.uhn.fhir.jpa.provider.r4.IConsentExtensionProvider; import ca.uhn.fhir.jpa.provider.r4.MemberMatcherR4Helper; import ca.uhn.fhir.jpa.sched.AutowiringSpringBeanJobFactory; import ca.uhn.fhir.jpa.sched.HapiSchedulerServiceImpl; @@ -111,8 +113,8 @@ import ca.uhn.fhir.jpa.searchparam.nickname.NicknameInterceptor; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider; import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl; -import ca.uhn.fhir.jpa.term.TermReadSvcImpl; import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl; +import ca.uhn.fhir.jpa.term.TermReadSvcImpl; import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.config.TermCodeSystemConfig; @@ -129,6 +131,9 @@ import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices; import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import org.hl7.fhir.common.hapi.validation.support.UnknownCodeSystemWarningValidationSupport; +import org.hl7.fhir.r4.model.Consent; +import org.hl7.fhir.r4.model.Coverage; +import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices; import org.hl7.fhir.utilities.npm.PackageClient; import org.springframework.beans.factory.annotation.Autowired; @@ -710,6 +715,20 @@ public class JpaConfig { return new UnknownCodeSystemWarningValidationSupport(theFhirContext); } + @Lazy + @Bean + public MemberMatcherR4Helper memberMatcherR4Helper( + @Autowired FhirContext theContext, + @Autowired IFhirResourceDao theCoverageDao, + @Autowired IFhirResourceDao thePatientDao, + @Autowired IFhirResourceDao theConsentDao, + @Autowired(required = false) IConsentExtensionProvider theExtensionProvider + ) { + return new MemberMatcherR4Helper( + theContext, theCoverageDao, thePatientDao, theConsentDao, theExtensionProvider + ); + } + @Lazy @Bean public NicknameInterceptor nicknameInterceptor() throws IOException { @@ -732,5 +751,4 @@ public class JpaConfig { return new TermReadSvcImpl(); } - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/JpaR4Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/JpaR4Config.java index 978514440a2..21f12606c4d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/JpaR4Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/JpaR4Config.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.api.IDaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigR4; import ca.uhn.fhir.jpa.config.JpaConfig; @@ -12,6 +13,7 @@ import ca.uhn.fhir.jpa.dao.r4.TransactionProcessorVersionAdapterR4; import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.graphql.GraphQLProviderWithIntrospection; import ca.uhn.fhir.jpa.provider.JpaSystemProvider; +import ca.uhn.fhir.jpa.provider.r4.IConsentExtensionProvider; import ca.uhn.fhir.jpa.provider.r4.MemberMatchR4ResourceProvider; import ca.uhn.fhir.jpa.provider.r4.MemberMatcherR4Helper; import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl; @@ -23,8 +25,12 @@ import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Consent; +import org.hl7.fhir.r4.model.Coverage; import org.hl7.fhir.r4.model.Meta; +import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -97,27 +103,24 @@ public class JpaR4Config { } @Bean - public MemberMatcherR4Helper memberMatcherR4Helper(FhirContext theFhirContext) { - return new MemberMatcherR4Helper(theFhirContext); + public MemberMatcherR4Helper memberMatcherR4Helper( + @Autowired FhirContext theContext, + @Autowired IFhirResourceDao theCoverageDao, + @Autowired IFhirResourceDao thePatientDao, + @Autowired IFhirResourceDao theConsentDao, + @Autowired(required = false) IConsentExtensionProvider theExtensionProvider + ) { + return new MemberMatcherR4Helper( + theContext, + theCoverageDao, + thePatientDao, + theConsentDao, + theExtensionProvider + ); } @Bean public MemberMatchR4ResourceProvider memberMatchR4ResourceProvider(FhirContext theFhirContext, MemberMatcherR4Helper theMemberMatchR4Helper) { return new MemberMatchR4ResourceProvider(theFhirContext, theMemberMatchR4Helper); } - - @Bean - public ProviderLoader r4ProviderLoader(ResourceProviderFactory theResourceProviderFactory, MemberMatchR4ResourceProvider theMemberMatchR4ResourceProvider) { - return new ProviderLoader(theResourceProviderFactory, theMemberMatchR4ResourceProvider); - } - - public static class ProviderLoader { - - public ProviderLoader(ResourceProviderFactory theResourceProviderFactory, MemberMatchR4ResourceProvider theMemberMatchR4ResourceProvider) { - theResourceProviderFactory.addSupplier(()->theMemberMatchR4ResourceProvider); - } - - } - - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IConsentExtensionProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IConsentExtensionProvider.java new file mode 100644 index 00000000000..0483a6fe452 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IConsentExtensionProvider.java @@ -0,0 +1,21 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import java.util.Collection; +import java.util.Collections; + +public interface IConsentExtensionProvider { + + /** + * Takes a Consent resource and returns a collection of Extensions that will + * be added to the base resource. + * + * @param theConsentResource - the consent resource + * @return - a collection of resources (or an empty collection if none). + */ + default Collection getConsentExtension(IBaseResource theConsentResource) { + return Collections.emptyList(); + }; +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java index ae0157ae2c6..09cd4cc8782 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.server.provider.ProviderConstants; import org.hl7.fhir.r4.model.Coverage; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Consent; import javax.servlet.http.HttpServletRequest; import java.util.Optional; @@ -69,16 +70,20 @@ public class MemberMatchR4ResourceProvider { @OperationParam(name = Constants.PARAM_NEW_COVERAGE, min = 1, max = 1) Coverage newCoverage, + @Description(shortDefinition = "Consent information. Consent held by the system seeking the match that grants permission to access the patient information.") + @OperationParam(name = Constants.PARAM_CONSENT, min = 1, max = 1) + Consent theConsent, + RequestDetails theRequestDetails ) { - return doMemberMatchOperation(theServletRequest, theMemberPatient, oldCoverage, newCoverage, theRequestDetails); + return doMemberMatchOperation(theServletRequest, theMemberPatient, oldCoverage, newCoverage, theConsent, theRequestDetails); } private Parameters doMemberMatchOperation(HttpServletRequest theServletRequest, Patient theMemberPatient, - Coverage theCoverageToMatch, Coverage theCoverageToLink, RequestDetails theRequestDetails) { + Coverage theCoverageToMatch, Coverage theCoverageToLink, Consent theConsent, RequestDetails theRequestDetails) { - validateParams(theMemberPatient, theCoverageToMatch, theCoverageToLink); + validateParams(theMemberPatient, theCoverageToMatch, theCoverageToLink, theConsent); Optional coverageOpt = myMemberMatcherR4Helper.findMatchingCoverage(theCoverageToMatch); if ( ! coverageOpt.isPresent()) { @@ -108,16 +113,24 @@ public class MemberMatchR4ResourceProvider { throw new UnprocessableEntityException(Msg.code(1157) + i18nMessage); } - myMemberMatcherR4Helper.addMemberIdentifierToMemberPatient(theMemberPatient, patient.getIdentifierFirstRep()); + if (!myMemberMatcherR4Helper.validConsentDataAccess(theConsent)) { + String i18nMessage = myFhirContext.getLocalizer().getMessage( + "operation.member.match.error.consent.release.data.mismatch"); + throw new UnprocessableEntityException(Msg.code(2147) + i18nMessage); + } - return myMemberMatcherR4Helper.buildSuccessReturnParameters(theMemberPatient, theCoverageToLink); + myMemberMatcherR4Helper.addMemberIdentifierToMemberPatient(theMemberPatient, patient.getIdentifierFirstRep()); + myMemberMatcherR4Helper.updateConsentForMemberMatch(theConsent, patient); + return myMemberMatcherR4Helper.buildSuccessReturnParameters(theMemberPatient, theCoverageToLink, theConsent); } - private void validateParams(Patient theMemberPatient, Coverage theOldCoverage, Coverage theNewCoverage) { + private void validateParams(Patient theMemberPatient, Coverage theOldCoverage, Coverage theNewCoverage, Consent theConsent) { validateParam(theMemberPatient, Constants.PARAM_MEMBER_PATIENT); validateParam(theOldCoverage, Constants.PARAM_OLD_COVERAGE); validateParam(theNewCoverage, Constants.PARAM_NEW_COVERAGE); + validateParam(theConsent, Constants.PARAM_CONSENT); validateMemberPatientParam(theMemberPatient); + validateConsentParam(theConsent); } private void validateParam(Object theParam, String theParamName) { @@ -136,4 +149,13 @@ public class MemberMatchR4ResourceProvider { validateParam(theMemberPatient.getName().get(0).getFamily(), Constants.PARAM_MEMBER_PATIENT_NAME); validateParam(theMemberPatient.getBirthDate(), Constants.PARAM_MEMBER_PATIENT_BIRTHDATE); } + + private void validateConsentParam(Consent theConsent) { + if (theConsent.getPatient().isEmpty()) { + validateParam(null, Constants.PARAM_CONSENT_PATIENT_REFERENCE); + } + if (theConsent.getPerformer().isEmpty()) { + validateParam(null, Constants.PARAM_CONSENT_PERFORMER_REFERENCE); + } + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java index c3336ba87d8..a42eb2ae2f1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.param.DateParam; @@ -10,22 +11,31 @@ import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.util.ParametersUtil; import com.google.common.collect.Lists; +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.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Consent; 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.Identifier; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.StringType; import org.springframework.beans.factory.annotation.Autowired; +import javax.annotation.Nullable; +import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.UUID; +import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT; import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT; import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; @@ -50,23 +60,39 @@ import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; */ public class MemberMatcherR4Helper { + private 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 = "MB"; private static final String OUT_COVERAGE_IDENTIFIER_TEXT = "Member Number"; private static final String COVERAGE_TYPE = "Coverage"; + private static final String CONSENT_POLICY_REGULAR_TYPE = "regular"; + 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"; private final FhirContext myFhirContext; + private final IFhirResourceDao myCoverageDao; + private final IFhirResourceDao myPatientDao; + private final IFhirResourceDao myConsentDao; + // by default, not provided + // but if it is, extensions can be added to Consent on $member-match + @Nullable + private final IConsentExtensionProvider myIConsentExtensionProvider; - @Autowired - private IFhirResourceDao myCoverageDao; + private boolean myRegularFilterSupported = false; - @Autowired - private IFhirResourceDao myPatientDao; - - - public MemberMatcherR4Helper(FhirContext theContext) { + public MemberMatcherR4Helper( + FhirContext theContext, + IFhirResourceDao theCoverageDao, + IFhirResourceDao thePatientDao, + IFhirResourceDao theConsentDao, + @Nullable IConsentExtensionProvider theExtensionProvider + ) { myFhirContext = theContext; + myConsentDao = theConsentDao; + myPatientDao = thePatientDao; + myCoverageDao = theCoverageDao; + myIConsentExtensionProvider = theExtensionProvider; } /** @@ -116,15 +142,23 @@ public class MemberMatcherR4Helper { return retVal.getAllResources(); } + public void updateConsentForMemberMatch(Consent theConsent, Patient thePatient) { + addClientIdAsExtensionToConsentIfAvailable(theConsent); + addIdentifierToConsent(theConsent); + updateConsentPatientAndPerformer(theConsent, thePatient); - public Parameters buildSuccessReturnParameters(Patient theMemberPatient, Coverage theCoverage) { + // save the resource + myConsentDao.create(theConsent); + } + + public Parameters buildSuccessReturnParameters(Patient theMemberPatient, Coverage theCoverage, Consent theConsent) { IBaseParameters parameters = ParametersUtil.newInstance(myFhirContext); ParametersUtil.addParameterToParameters(myFhirContext, parameters, PARAM_MEMBER_PATIENT, theMemberPatient); ParametersUtil.addParameterToParameters(myFhirContext, parameters, PARAM_NEW_COVERAGE, theCoverage); + ParametersUtil.addParameterToParameters(myFhirContext, parameters, PARAM_CONSENT, theConsent); return (Parameters) parameters; } - public void addMemberIdentifierToMemberPatient(Patient theMemberPatient, Identifier theNewIdentifier) { Coding coding = new Coding() .setSystem(OUT_COVERAGE_IDENTIFIER_CODE_SYSTEM) @@ -145,6 +179,28 @@ public class MemberMatcherR4Helper { theMemberPatient.addIdentifier(newIdentifier); } + /** + * If there is a client id + * @param theConsent - the consent to modify + */ + private void addClientIdAsExtensionToConsentIfAvailable(Consent theConsent) { + if (myIConsentExtensionProvider != null) { + Collection 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 getBeneficiaryPatient(Coverage theCoverage) { if (theCoverage.getBeneficiaryTarget() == null && theCoverage.getBeneficiary() == null) { @@ -181,19 +237,62 @@ public class MemberMatcherR4Helper { return false; } StringOrListParam familyName = new StringOrListParam(); - for (HumanName name: thePatientToMatch.getName()) { + for (HumanName name : thePatientToMatch.getName()) { familyName.addOr(new StringParam(name.getFamily())); } SearchParameterMap map = new SearchParameterMap() .add("family", familyName) .add("birthdate", new DateParam(thePatientToMatch.getBirthDateElement().getValueAsString())); ca.uhn.fhir.rest.api.server.IBundleProvider bundle = myPatientDao.search(map); - for (IBaseResource patientResource: bundle.getAllResources()) { + for (IBaseResource patientResource : bundle.getAllResources()) { IIdType patientId = patientResource.getIdElement().toUnqualifiedVersionless(); - if ( patientId.getValue().equals(thePatientFromContract.getIdElement().toUnqualifiedVersionless().getValue())) { + if (patientId.getValue().equals(thePatientFromContract.getIdElement().toUnqualifiedVersionless().getValue())) { return true; } } return false; } + + public boolean validConsentDataAccess(Consent theConsent) { + if (theConsent.getPolicy().isEmpty()) { + return false; + } + for (Consent.ConsentPolicyComponent policyComponent: theConsent.getPolicy()) { + if (policyComponent.getUri() == null || !validConsentPolicy(policyComponent.getUri())) { + return false; + } + } + return true; + } + + /** + * This is filtering the Consent policy data. The rule is specified in + * https://build.fhir.org/ig/HL7/davinci-ehrx/StructureDefinition-hrex-consent.html#notes + */ + private boolean validConsentPolicy(String thePolicyUri) { + String policyTypes = StringUtils.substringAfterLast(thePolicyUri, "#"); + if (policyTypes.equals(CONSENT_POLICY_SENSITIVE_TYPE)) { + return true; + } + if (policyTypes.equals(CONSENT_POLICY_REGULAR_TYPE) && myRegularFilterSupported) { + return true; + } + return false; + } + + private void addIdentifierToConsent(Consent theConsent) { + String consentId = UUID.randomUUID().toString(); + Identifier consentIdentifier = new Identifier().setSystem(CONSENT_IDENTIFIER_CODE_SYSTEM).setValue(consentId); + theConsent.addIdentifier(consentIdentifier); + } + + public void setRegularFilterSupported(boolean theRegularFilterSupported) { + myRegularFilterSupported = theRegularFilterSupported; + } + + private void updateConsentPatientAndPerformer(Consent theConsent, Patient thePatient) { + String patientRef = thePatient.getIdElement().toUnqualifiedVersionless().getValue(); + theConsent.getPatient().setReference(patientRef); + theConsent.getPerformer().set(0, new Reference(patientRef)); + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/MemberMatcherR4HelperTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/MemberMatcherR4HelperTest.java new file mode 100644 index 00000000000..7fc778c588c --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/MemberMatcherR4HelperTest.java @@ -0,0 +1,597 @@ +package ca.uhn.fhir.jpa.provider; + +import ca.uhn.fhir.context.FhirContext; +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.model.api.IQueryParameterType; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.server.SimpleBundleProvider; +import ch.qos.logback.classic.Level; +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 org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Consent; +import org.hl7.fhir.r4.model.Coverage; +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.Identifier; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Patient; +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.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; +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_MEMBER_PATIENT; +import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class MemberMatcherR4HelperTest { + + private static final Logger ourLog = (Logger) LoggerFactory.getLogger(MemberMatcherR4Helper.class); + + @Mock + private ListAppender myAppender; + + @Spy + private final FhirContext myFhirContext = FhirContext.forR4(); + @Mock + private IFhirResourceDao myCoverageDao; + @Mock + private IFhirResourceDao myPatientDao; + @Mock + private IFhirResourceDao myConsentDao; + + private MemberMatcherR4Helper myHelper; + + @BeforeEach + public void before() { + myHelper = new MemberMatcherR4Helper( + myFhirContext, + myCoverageDao, + myPatientDao, + myConsentDao, + null // extension provider + ); + + ourLog.addAppender(myAppender); + } + + @AfterEach + public void after() { + ourLog.detachAppender(myAppender); + } + + @Mock private Coverage myCoverageToMatch; + @Mock private IBundleProvider myBundleProvider; + + private final Coverage myMatchedCoverage = new Coverage() + .setBeneficiary(new Reference("Patient/123")); + private final Identifier myMatchingIdentifier = new Identifier() + .setSystem("identifier-system").setValue("identifier-value"); + + @Captor + ArgumentCaptor mySearchParameterMapCaptor; + + @Test + void findMatchingCoverageMatchByIdReturnsMatched() { + when(myCoverageToMatch.getId()).thenReturn("cvg-to-match-id"); + when(myCoverageDao.search(isA(SearchParameterMap.class))).thenReturn(myBundleProvider); + when(myBundleProvider.getAllResources()).thenReturn(Collections.singletonList(myMatchedCoverage)); + + Optional result = myHelper.findMatchingCoverage(myCoverageToMatch); + + assertEquals(Optional.of(myMatchedCoverage), result); + verify(myCoverageDao).search(mySearchParameterMapCaptor.capture()); + SearchParameterMap spMap = mySearchParameterMapCaptor.getValue(); + assertTrue(spMap.containsKey("_id")); + List> listListParams = spMap.get("_id"); + assertEquals(1, listListParams.size()); + assertEquals(1, listListParams.get(0).size()); + IQueryParameterType param = listListParams.get(0).get(0); + assertEquals("cvg-to-match-id", param.getValueAsQueryToken(myFhirContext)); + } + + + @Test + void findMatchingCoverageMatchByIdentifierReturnsMatched() { + when(myCoverageToMatch.getId()).thenReturn("non-matching-id"); + when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier)); + when(myCoverageDao.search(isA(SearchParameterMap.class))).thenReturn(myBundleProvider); + when(myBundleProvider.getAllResources()).thenReturn( + Collections.emptyList(), Collections.singletonList(myMatchedCoverage)); + + Optional result = myHelper.findMatchingCoverage(myCoverageToMatch); + + assertEquals(Optional.of(myMatchedCoverage), result); + verify(myCoverageDao, times(2)).search(mySearchParameterMapCaptor.capture()); + List spMap = mySearchParameterMapCaptor.getAllValues(); + assertTrue(spMap.get(0).containsKey("_id")); + assertTrue(spMap.get(1).containsKey("identifier")); + List> listListParams = spMap.get(1).get("identifier"); + assertEquals(1, listListParams.size()); + assertEquals(1, listListParams.get(0).size()); + IQueryParameterType param = listListParams.get(0).get(0); + assertEquals(myMatchingIdentifier.getSystem() + "|" + myMatchingIdentifier.getValue(), + param.getValueAsQueryToken(myFhirContext)); + } + + + @Test + void findMatchingCoverageNoMatchReturnsEmpty() { + when(myCoverageToMatch.getId()).thenReturn("non-matching-id"); + when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier)); + when(myCoverageDao.search(any(SearchParameterMap.class))).thenReturn(myBundleProvider); + when(myBundleProvider.getAllResources()).thenReturn(Collections.emptyList(), Collections.emptyList()); + + Optional result = myHelper.findMatchingCoverage(myCoverageToMatch); + + assertFalse(result.isPresent()); + } + + + @Test + void buildSuccessReturnParameters() { + Patient patient = new Patient(); + Coverage coverage = new Coverage(); + Consent consent = new Consent(); + + Parameters result = myHelper.buildSuccessReturnParameters(patient, coverage, consent); + + assertEquals(PARAM_MEMBER_PATIENT, result.getParameter().get(0).getName()); + assertEquals(patient, result.getParameter().get(0).getResource()); + + assertEquals(PARAM_NEW_COVERAGE, result.getParameter().get(1).getName()); + assertEquals(coverage, result.getParameter().get(1).getResource()); + + assertEquals(PARAM_CONSENT, result.getParameter().get(2).getName()); + assertEquals(consent, result.getParameter().get(2).getResource()); + } + + + @Test + void addMemberIdentifierToMemberPatient() { + Identifier originalIdentifier = new Identifier() + .setSystem("original-identifier-system").setValue("original-identifier-value"); + + Identifier newIdentifier = new Identifier() + .setSystem("new-identifier-system").setValue("new-identifier-value"); + + Patient patient = new Patient().setIdentifier(Lists.newArrayList(originalIdentifier)); + + myHelper.addMemberIdentifierToMemberPatient(patient, newIdentifier); + + assertEquals(2, patient.getIdentifier().size()); + + assertEquals("original-identifier-system", patient.getIdentifier().get(0).getSystem()); + assertEquals("original-identifier-value", patient.getIdentifier().get(0).getValue()); + + assertEquals("new-identifier-system", patient.getIdentifier().get(1).getSystem()); + assertEquals("new-identifier-value", patient.getIdentifier().get(1).getValue()); + } + + /** + * Testing multiple scenarios for getting patient resource from coverage's plan beneficiary + */ + @Nested + public class TestGetBeneficiaryPatient { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Coverage coverage; + + + @Test + void noBeneficiaryOrBeneficiaryTargetReturnsEmpty() { + when(coverage.getBeneficiaryTarget()).thenReturn(null); + when(coverage.getBeneficiary()).thenReturn(null); + + Optional result = myHelper.getBeneficiaryPatient(coverage); + + assertFalse(result.isPresent()); + } + + + @Test + void beneficiaryTargetWithNoIdentifierReturnsEmpty() { + when(coverage.getBeneficiary()).thenReturn(null); + when(coverage.getBeneficiaryTarget()).thenReturn(new Patient()); + + Optional result = myHelper.getBeneficiaryPatient(coverage); + + assertFalse(result.isPresent()); + } + + + @Test + void beneficiaryTargetWithIdentifierReturnsBeneficiary() { + Patient patient = new Patient().setIdentifier(Collections.singletonList(new Identifier())); + when(coverage.getBeneficiaryTarget()).thenReturn(patient); + + Optional result = myHelper.getBeneficiaryPatient(coverage); + + assertTrue(result.isPresent()); + assertEquals(patient, result.get()); + } + + + @Test + void beneficiaryReferenceResourceReturnsBeneficiary() { + Patient patient = new Patient().setIdentifier(Collections.singletonList(new Identifier())); + when(coverage.getBeneficiaryTarget()).thenReturn(null); + when(coverage.getBeneficiary().getResource()).thenReturn(patient); + + Optional result = myHelper.getBeneficiaryPatient(coverage); + + assertTrue(result.isPresent()); + assertEquals(patient, result.get()); + } + + + @Test + void beneficiaryReferenceNoResourceOrReferenceReturnsEmpty() { + when(coverage.getBeneficiaryTarget()).thenReturn(null); + when(coverage.getBeneficiary()).thenReturn(new Reference()); + + Optional result = myHelper.getBeneficiaryPatient(coverage); + + assertFalse(result.isPresent()); + } + + + @Test + void beneficiaryReferenceReferenceReturnsReadPatient() { + when(coverage.getBeneficiaryTarget()).thenReturn(null); + when(coverage.getBeneficiary().getResource()).thenReturn(null); + when(coverage.getBeneficiary().getReference()).thenReturn("patient-id"); + + myHelper.getBeneficiaryPatient(coverage); + + verify(myPatientDao).read(new IdDt("patient-id")); + } + + } + + /** + * Testing multiple scenarios for validity of Patient Member parameter + */ + @Nested + public class TestValidPatientMember { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Coverage coverage; + private Patient patient; + + @Test + void noPatientFoundFromContractReturnsFalse() { + boolean result = myHelper.validPatientMember(null, patient); + assertFalse(result); + } + + @Test + void noPatientFoundFromPatientMemberReturnsFalse() { + boolean result = myHelper.validPatientMember(patient, null); + assertFalse(result); + } + + @Test + void noMatchingFamilyNameReturnsFalse() { + Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01"); + Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01"); + when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> { + IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123"))); + return provider; + }); + boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch); + assertFalse(result); + } + + + @Test + void noMatchingBirthdayReturnsFalse() { + Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01"); + Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01"); + when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> { + IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123"))); + return provider; + }); + boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch); + assertFalse(result); + } + + @Test + void noMatchingFieldsReturnsFalse() { + Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01"); + Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01"); + when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> { + IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123"))); + return provider; + }); + boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch); + assertFalse(result); + } + + @Test + void patientMatchingReturnTrue() { + Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01"); + Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01"); + when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> { + IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(patientFromContractFound)); + return provider; + }); + boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch); + assertTrue(result); + } + + private Patient getPatientWithNoIDParm(String familyName, String birthdate) { + Patient patient = new Patient().setName(Lists.newArrayList(new HumanName() + .setUse(HumanName.NameUse.OFFICIAL).setFamily(familyName))) + .setBirthDateElement(new DateType(birthdate)); + return patient; + } + + private Patient getPatientWithIDParm(String id, String familyName, String birthdate) { + Patient patient = getPatientWithNoIDParm(familyName, birthdate); + patient.setId(id); + return patient; + } + + } + + /** + * Testing multiple scenarios for consent's policy data that is defined in + * https://build.fhir.org/ig/HL7/davinci-ehrx/StructureDefinition-hrex-consent.html#notes + */ + @Nested + public class TestValidvalidConsentDataAccess { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Coverage coverage; + private Patient patient; + private Consent consent; + + @Test + void noConsentProfileFoundReturnsFalse() { + consent = new Consent(); + boolean result = myHelper.validConsentDataAccess(consent); + assertFalse(result); + } + + @Test + void noDataAccessValueProvidedReturnsFalse() { + consent = getConsent(); + boolean result = myHelper.validConsentDataAccess(consent); + assertFalse(result); + } + + @Test + void wrongDataAccessValueProvidedReturnsFalse() { + consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#access_data")); + boolean result = myHelper.validConsentDataAccess(consent); + assertFalse(result); + } + + @Test + void regularDataAccessWithRegularNotAllowedReturnsFalse() { + consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#regular")); + boolean result = myHelper.validConsentDataAccess(consent); + assertFalse(result); + } + + @Test + void regularDataAccessWithRegularAllowedReturnsTrue() { + myHelper.setRegularFilterSupported(true); + consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#regular")); + boolean result = myHelper.validConsentDataAccess(consent); + assertTrue(result); + } + + @Test + void sensitiveDataAccessAllowedReturnsTrue() { + consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#sensitive")); + boolean result = myHelper.validConsentDataAccess(consent); + assertTrue(result); + } + + @Test + void multipleSensitivePolicyDataAccessAllowedReturnsTrue() { + consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#sensitive")); + consent.addPolicy(constructConsentPolicyComponent("#sensitive")); + boolean result = myHelper.validConsentDataAccess(consent); + assertTrue(result); + } + + @Test + void multipleRegularPolicyDataAccessWithRegularAllowedReturnsTrue() { + myHelper.setRegularFilterSupported(true); + consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#regular")); + consent.addPolicy(constructConsentPolicyComponent("#regular")); + boolean result = myHelper.validConsentDataAccess(consent); + assertTrue(result); + } + + @Test + void multipleMixedPolicyDataAccessWithRegularNotAllowedReturnsFalse() { + consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#regular")); + consent.addPolicy(constructConsentPolicyComponent("#sensitive")); + boolean result = myHelper.validConsentDataAccess(consent); + assertFalse(result); + } + + @Test + void multipleMixedPolicyDataAccessWithRegularAllowedReturnsTrue() { + myHelper.setRegularFilterSupported(true); + consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#regular")); + consent.addPolicy(constructConsentPolicyComponent("#sensitive")); + boolean result = myHelper.validConsentDataAccess(consent); + assertTrue(result); + } + } + + private Consent getConsent() { + Consent consent = new Consent(); + consent.getPerformer().add(new Reference("Patient/1")); + return consent; + } + + private Consent.ConsentPolicyComponent constructConsentPolicyComponent(String uriAccess) { + String uri = "http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html"; + return new Consent.ConsentPolicyComponent().setUri(uri + uriAccess); + } + + private Patient createPatientForMemberMatchUpdate() { + Patient patient = new Patient(); + patient.setId("Patient/RED"); + + return patient; + } + + private void verifyConsentUpdatedAfterMemberMatch( + Consent theConsent, + Patient thePatient, + List 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()); + + // 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 + public class MemberMatchWithoutConsentProvider { + + @Test + public void updateConsentForMemberMatch_noProvider_addsIdentifierUpdatePatientButNotExtensionAndSaves() { + // setup + Consent consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#sensitive")); + Patient patient = createPatientForMemberMatchUpdate(); + + ourLog.setLevel(Level.TRACE); + + // test + myHelper.updateConsentForMemberMatch(consent, patient); + + // verify + verify(myAppender, never()) + .doAppend(any(ILoggingEvent.class)); + + ArgumentCaptor consentCaptor = ArgumentCaptor.forClass(Consent.class); + verify(myConsentDao).create(consentCaptor.capture()); + Consent saved = consentCaptor.getValue(); + verifyConsentUpdatedAfterMemberMatch(saved, patient, Collections.emptyList()); + } + } + + @Nested + public class MemberMatchWithConsentProvider { + @Mock + private IConsentExtensionProvider myExtensionProvider; + + @BeforeEach + public void before() { + myHelper = new MemberMatcherR4Helper( + myFhirContext, + myCoverageDao, + myPatientDao, + myConsentDao, + myExtensionProvider + ); + } + + @Test + public void addClientIdAsExtensionToConsentIfAvailable_withProvider_addsExtensionAndSaves() { + // setup + Consent consent = getConsent(); + consent.addPolicy(constructConsentPolicyComponent("#sensitive")); + consent.setId("Consent/RED"); + Extension ext = new Extension(); + ext.setUrl("http://example.com"); + ext.setValue(new StringType("value")); + Patient patient = createPatientForMemberMatchUpdate(); + + ourLog.setLevel(Level.TRACE); + + // when + when(myExtensionProvider.getConsentExtension(any(IBaseResource.class))) + .thenReturn(Collections.singleton(ext)); + + // test + myHelper.updateConsentForMemberMatch(consent, patient); + + // verify + ArgumentCaptor consentCaptor = ArgumentCaptor.forClass(Consent.class); + verify(myConsentDao).create(consentCaptor.capture()); + Consent saved = consentCaptor.getValue(); + verifyConsentUpdatedAfterMemberMatch(saved, patient, Collections.emptyList()); + + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(ILoggingEvent.class); + verify(myAppender).doAppend(eventCaptor.capture()); + ILoggingEvent event = eventCaptor.getValue(); + assertEquals("1 extension(s) added to Consent", event.getFormattedMessage()); + } + } +} diff --git a/hapi-fhir-jpaserver-cql/pom.xml b/hapi-fhir-jpaserver-cql/pom.xml index 32d02cfefd4..db7a25faeba 100644 --- a/hapi-fhir-jpaserver-cql/pom.xml +++ b/hapi-fhir-jpaserver-cql/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index 12dabc769ed..3ae132f069d 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 3c1b4ec3757..0bbc1a3799d 100644 --- a/hapi-fhir-jpaserver-mdm/pom.xml +++ b/hapi-fhir-jpaserver-mdm/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index e05cf224956..2c24cb96fdf 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java index 30f7b9f467c..0dfd78544b6 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java @@ -172,6 +172,11 @@ public class JpaConstants { */ public static final String OPERATION_LASTN = "$lastn"; + /** + * Operation name for the $member-match operation + */ + public static final String OPERATION_MEMBER_MATCH = "$member-match"; + /** * Parameter for the $export operation */ diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index bd28a08e9ea..d0edd4fb80a 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 363c06dd6ae..8972baf7789 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml index e486e66c733..dbe117f54a6 100644 --- a/hapi-fhir-jpaserver-test-dstu2/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu2/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 7506bb60588..e75a6ceb78c 100644 --- a/hapi-fhir-jpaserver-test-dstu3/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu3/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 6c7b10f0338..9fbe1e4995b 100644 --- a/hapi-fhir-jpaserver-test-r4/pom.xml +++ b/hapi-fhir-jpaserver-test-r4/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java deleted file mode 100644 index 52b1e2dc5cb..00000000000 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java +++ /dev/null @@ -1,327 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.model.api.IQueryParameterType; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.server.SimpleBundleProvider; -import com.google.common.collect.Lists; -import org.hl7.fhir.r4.model.Coverage; -import org.hl7.fhir.r4.model.DateType; -import org.hl7.fhir.r4.model.HumanName; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Reference; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class MemberMatcherR4HelperTest { - - private final FhirContext myFhirContext = FhirContext.forR4(); - @Mock private IFhirResourceDao myCoverageDao; - @Mock private IFhirResourceDao myPatientDao; - - private MemberMatcherR4Helper myTestedHelper; - - @Mock private Coverage myCoverageToMatch; - @Mock private IBundleProvider myBundleProvider; - - private final Coverage myMatchedCoverage = new Coverage() - .setBeneficiary(new Reference("Patient/123")); - private final Identifier myMatchingIdentifier = new Identifier() - .setSystem("identifier-system").setValue("identifier-value"); - - @Captor ArgumentCaptor mySearchParameterMapCaptor; - - @BeforeEach - public void beforeEach() { - myTestedHelper = new MemberMatcherR4Helper(myFhirContext); - - // @InjectMocks didn't work - ReflectionTestUtils.setField(myTestedHelper, "myCoverageDao", myCoverageDao); - ReflectionTestUtils.setField(myTestedHelper, "myPatientDao", myPatientDao); - } - - - @Test - void findMatchingCoverageMatchByIdReturnsMatched() { - when(myCoverageToMatch.getId()).thenReturn("cvg-to-match-id"); - when(myCoverageDao.search(isA(SearchParameterMap.class))).thenReturn(myBundleProvider); - when(myBundleProvider.getAllResources()).thenReturn(Collections.singletonList(myMatchedCoverage)); - - Optional result = myTestedHelper.findMatchingCoverage(myCoverageToMatch); - - assertEquals(Optional.of(myMatchedCoverage), result); - verify(myCoverageDao).search(mySearchParameterMapCaptor.capture()); - SearchParameterMap spMap = mySearchParameterMapCaptor.getValue(); - assertTrue(spMap.containsKey("_id")); - List> listListParams = spMap.get("_id"); - assertEquals(1, listListParams.size()); - assertEquals(1, listListParams.get(0).size()); - IQueryParameterType param = listListParams.get(0).get(0); - assertEquals("cvg-to-match-id", param.getValueAsQueryToken(myFhirContext)); - } - - - @Test - void findMatchingCoverageMatchByIdentifierReturnsMatched() { - when(myCoverageToMatch.getId()).thenReturn("non-matching-id"); - when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier)); - when(myCoverageDao.search(isA(SearchParameterMap.class))).thenReturn(myBundleProvider); - when(myBundleProvider.getAllResources()).thenReturn( - Collections.emptyList(), Collections.singletonList(myMatchedCoverage)); - - Optional result = myTestedHelper.findMatchingCoverage(myCoverageToMatch); - - assertEquals(Optional.of(myMatchedCoverage), result); - verify(myCoverageDao, times(2)).search(mySearchParameterMapCaptor.capture()); - List spMap = mySearchParameterMapCaptor.getAllValues(); - assertTrue(spMap.get(0).containsKey("_id")); - assertTrue(spMap.get(1).containsKey("identifier")); - List> listListParams = spMap.get(1).get("identifier"); - assertEquals(1, listListParams.size()); - assertEquals(1, listListParams.get(0).size()); - IQueryParameterType param = listListParams.get(0).get(0); - assertEquals(myMatchingIdentifier.getSystem() + "|" + myMatchingIdentifier.getValue(), - param.getValueAsQueryToken(myFhirContext)); - } - - - @Test - void findMatchingCoverageNoMatchReturnsEmpty() { - when(myCoverageToMatch.getId()).thenReturn("non-matching-id"); - when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier)); - when(myCoverageDao.search(isA(SearchParameterMap.class))).thenReturn(myBundleProvider); - when(myBundleProvider.getAllResources()).thenReturn(Collections.emptyList(), Collections.emptyList()); - - Optional result = myTestedHelper.findMatchingCoverage(myCoverageToMatch); - - assertFalse(result.isPresent()); - } - - - @Test - void buildSuccessReturnParameters() { - Patient patient = new Patient(); - Coverage coverage = new Coverage(); - - Parameters result = myTestedHelper.buildSuccessReturnParameters(patient, coverage); - - assertEquals(PARAM_MEMBER_PATIENT, result.getParameter().get(0).getName()); - assertEquals(patient, result.getParameter().get(0).getResource()); - - assertEquals(PARAM_NEW_COVERAGE, result.getParameter().get(1).getName()); - assertEquals(coverage, result.getParameter().get(1).getResource()); - } - - - @Test - void addMemberIdentifierToMemberPatient() { - Identifier originalIdentifier = new Identifier() - .setSystem("original-identifier-system").setValue("original-identifier-value"); - - Identifier newIdentifier = new Identifier() - .setSystem("new-identifier-system").setValue("new-identifier-value"); - - Patient patient = new Patient().setIdentifier(Lists.newArrayList(originalIdentifier)); - - myTestedHelper.addMemberIdentifierToMemberPatient(patient, newIdentifier); - - assertEquals(2, patient.getIdentifier().size()); - - assertEquals("original-identifier-system", patient.getIdentifier().get(0).getSystem()); - assertEquals("original-identifier-value", patient.getIdentifier().get(0).getValue()); - - assertEquals("new-identifier-system", patient.getIdentifier().get(1).getSystem()); - assertEquals("new-identifier-value", patient.getIdentifier().get(1).getValue()); - } - - @Nested - public class TestGetBeneficiaryPatient { - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Coverage coverage; - - - @Test - void noBeneficiaryOrBeneficiaryTargetReturnsEmpty() { - when(coverage.getBeneficiaryTarget()).thenReturn(null); - when(coverage.getBeneficiary()).thenReturn(null); - - Optional result = myTestedHelper.getBeneficiaryPatient(coverage); - - assertFalse(result.isPresent()); - } - - - @Test - void beneficiaryTargetWithNoIdentifierReturnsEmpty() { - when(coverage.getBeneficiary()).thenReturn(null); - when(coverage.getBeneficiaryTarget()).thenReturn(new Patient()); - - Optional result = myTestedHelper.getBeneficiaryPatient(coverage); - - assertFalse(result.isPresent()); - } - - - @Test - void beneficiaryTargetWithIdentifierReturnsBeneficiary() { - Patient patient = new Patient().setIdentifier(Collections.singletonList(new Identifier())); - when(coverage.getBeneficiaryTarget()).thenReturn(patient); - - Optional result = myTestedHelper.getBeneficiaryPatient(coverage); - - assertTrue(result.isPresent()); - assertEquals(patient, result.get()); - } - - - @Test - void beneficiaryReferenceResourceReturnsBeneficiary() { - Patient patient = new Patient().setIdentifier(Collections.singletonList(new Identifier())); - when(coverage.getBeneficiaryTarget()).thenReturn(null); - when(coverage.getBeneficiary().getResource()).thenReturn(patient); - - Optional result = myTestedHelper.getBeneficiaryPatient(coverage); - - assertTrue(result.isPresent()); - assertEquals(patient, result.get()); - } - - - @Test - void beneficiaryReferenceNoResourceOrReferenceReturnsEmpty() { - when(coverage.getBeneficiaryTarget()).thenReturn(null); - when(coverage.getBeneficiary()).thenReturn(new Reference()); - - Optional result = myTestedHelper.getBeneficiaryPatient(coverage); - - assertFalse(result.isPresent()); - } - - - @Test - void beneficiaryReferenceReferenceReturnsReadPatient() { - when(coverage.getBeneficiaryTarget()).thenReturn(null); - when(coverage.getBeneficiary().getResource()).thenReturn(null); - when(coverage.getBeneficiary().getReference()).thenReturn("patient-id"); - - myTestedHelper.getBeneficiaryPatient(coverage); - - verify(myPatientDao).read(new IdDt("patient-id")); - } - - } - - @Nested - public class TestValidPatientMember { - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Coverage coverage; - private Patient patient; - - @Test - void noPatientFoundFromContractReturnsFalse() { - boolean result = myTestedHelper.validPatientMember(null, patient); - assertFalse(result); - } - - @Test - void noPatientFoundFromPatientMemberReturnsFalse() { - boolean result = myTestedHelper.validPatientMember(patient, null); - assertFalse(result); - } - - @Test - void noMatchingFamilyNameReturnsFalse() { - Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01"); - Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01"); - when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> { - IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123"))); - return provider; - }); - boolean result = myTestedHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch); - assertFalse(result); - } - - - @Test - void noMatchingBirthdayReturnsFalse() { - Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01"); - Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01"); - when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> { - IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123"))); - return provider; - }); - boolean result = myTestedHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch); - assertFalse(result); - } - - @Test - void noMatchingFieldsReturnsFalse() { - Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01"); - Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01"); - when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> { - IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123"))); - return provider; - }); - boolean result = myTestedHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch); - assertFalse(result); - } - - @Test - void patientMatchingReturnTrue() { - Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01"); - Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01"); - when(myPatientDao.search(any(SearchParameterMap.class))).thenAnswer(t -> { - IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(patientFromContractFound)); - return provider; - }); - boolean result = myTestedHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch); - assertTrue(result); - } - - private Patient getPatientWithNoIDParm(String familyName, String birthdate) { - Patient patient = new Patient().setName(Lists.newArrayList(new HumanName() - .setUse(HumanName.NameUse.OFFICIAL).setFamily(familyName))) - .setBirthDateElement(new DateType(birthdate)); - return patient; - } - - private Patient getPatientWithIDParm(String id, String familyName, String birthdate) { - Patient patient = getPatientWithNoIDParm(familyName, birthdate); - patient.setId(id); - return patient; - } - - } - -} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java index 9cc527f9443..73e4bfec966 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java @@ -5,7 +5,6 @@ import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.apache.ResourceEntity; -import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.util.ParametersUtil; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; @@ -14,12 +13,12 @@ import org.apache.http.client.methods.HttpPost; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Consent; import org.hl7.fhir.r4.model.Coverage; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Reference; @@ -27,14 +26,20 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; +import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT_PATIENT_REFERENCE; +import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT_PERFORMER_REFERENCE; import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT; +import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT_BIRTHDATE; +import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT_NAME; import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; import static ca.uhn.fhir.rest.api.Constants.PARAM_OLD_COVERAGE; +import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -51,11 +56,20 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes private static final String EXISTING_COVERAGE_IDENT_VALUE = "U1234567890"; private static final String EXISTING_COVERAGE_PATIENT_IDENT_SYSTEM = "http://oldhealthplan.example.com"; private static final String EXISTING_COVERAGE_PATIENT_IDENT_VALUE = "DHU-55678"; + private static final String CONSENT_POLICY_SENSITIVE_DATA_URI = "http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html#sensitive"; + private static final String CONSENT_POLICY_REGULAR_DATA_URI = "http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html#regular"; private Identifier ourExistingCoverageIdentifier; private Patient myPatient; private Coverage oldCoverage; // Old Coverage (must match field) private Coverage newCoverage; // New Coverage (must return unchanged) + private Consent myConsent; + + @Autowired + MemberMatcherR4Helper myMemberMatcherR4Helper; + + @Autowired + MemberMatchR4ResourceProvider theMemberMatchR4ResourceProvider; @BeforeEach public void beforeDisableResultReuse() { @@ -70,6 +84,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes myDaoConfig.setEverythingIncludesFetchPageSize(new DaoConfig().getEverythingIncludesFetchPageSize()); myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds()); myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); + ourRestServer.unregisterProvider(theMemberMatchR4ResourceProvider); } @Override @@ -90,6 +105,15 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes newCoverage = (Coverage) new Coverage() .setIdentifier(Lists.newArrayList(new Identifier().setSystem("http://newealthplan.example.com").setValue("234567"))) .setId("AA87654"); + + myConsent = new Consent() + .setStatus(Consent.ConsentState.ACTIVE) + .setScope(new CodeableConcept().addCoding(new Coding("http://terminology.hl7.org/CodeSystem/consentscope", "patient-privacy", null))) + .addPolicy(new Consent.ConsentPolicyComponent().setUri(CONSENT_POLICY_SENSITIVE_DATA_URI)) + .setPatient(new Reference("Patient/A123")) + .addPerformer(new Reference("Patient/A123")); + ourRestServer.registerProvider(theMemberMatchR4ResourceProvider); + myMemberMatcherR4Helper.setRegularFilterSupported(false); } private void createCoverageWithBeneficiary( @@ -125,12 +149,11 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes myClient.create().resource(ourExistingCoverage).execute().getId().toUnqualifiedVersionless().getValue(); } - @Test public void testMemberMatchByCoverageId() throws Exception { createCoverageWithBeneficiary(true, true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); Parameters parametersResponse = performOperation(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters); @@ -143,7 +166,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes public void testCoverageNoBeneficiaryReturns422() throws Exception { createCoverageWithBeneficiary(false, false); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Could not find beneficiary for coverage."); } @@ -152,7 +175,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes public void testCoverageBeneficiaryNoIdentifierReturns422() throws Exception { createCoverageWithBeneficiary(true, false); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Coverage beneficiary does not have an identifier."); } @@ -162,7 +185,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes createCoverageWithBeneficiary(true, true); myPatient.setName(Lists.newArrayList(new HumanName().setUse(HumanName.NameUse.OFFICIAL).setFamily("Smith"))); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Could not find matching patient for coverage."); } @@ -172,16 +195,64 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes createCoverageWithBeneficiary(true, false); myPatient.setBirthDateElement(new DateType("2000-01-01")); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Could not find matching patient for coverage."); } + @Test + public void testRegularContentProfileAccessWithRegularNotAllowedReturns422() throws Exception { + createCoverageWithBeneficiary(true, true); + myConsent.getPolicy().get(0).setUri(CONSENT_POLICY_REGULAR_DATA_URI); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, + "Consent policy does not match the data release segmentation capabilities."); + } + + @Test + public void testSensitiveContentProfileAccessWithRegularNotAllowed() throws Exception { + createCoverageWithBeneficiary(true, true); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters parametersResponse = performOperation(ourServerBase + ourQuery, + EncodingEnum.JSON, inputParameters); + + validateMemberPatient(parametersResponse); + validateNewCoverage(parametersResponse, newCoverage); + } + + @Test + public void testRegularContentProfileAccessWithRegularAllowed() throws Exception { + createCoverageWithBeneficiary(true, true); + myConsent.getPolicy().get(0).setUri(CONSENT_POLICY_REGULAR_DATA_URI); + myMemberMatcherR4Helper.setRegularFilterSupported(true); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters parametersResponse = performOperation(ourServerBase + ourQuery, + EncodingEnum.JSON, inputParameters); + + validateMemberPatient(parametersResponse); + validateNewCoverage(parametersResponse, newCoverage); + } + + @Test + public void testConsentReturns() throws Exception { + createCoverageWithBeneficiary(true, true); + myConsent.getPolicy().get(0).setUri(CONSENT_POLICY_REGULAR_DATA_URI); + myMemberMatcherR4Helper.setRegularFilterSupported(true); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters parametersResponse = performOperation(ourServerBase + ourQuery, + EncodingEnum.JSON, inputParameters); + + validateMemberPatient(parametersResponse); + validateNewCoverage(parametersResponse, newCoverage); + validateResponseConsent(parametersResponse, myConsent); + } + @Nested public class ValidateParameterErrors { private Patient ourPatient; private Coverage ourOldCoverage; private Coverage ourNewCoverage; + private Consent ourConsent; @BeforeEach public void beforeValidateParameterErrors() { @@ -194,28 +265,77 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes ourNewCoverage.setId("AA87654"); ourNewCoverage.setIdentifier(Lists.newArrayList( new Identifier().setSystem("http://newealthplan.example.com").setValue("234567"))); + + ourConsent = new Consent(); + ourConsent.setStatus(Consent.ConsentState.ACTIVE); } @Test public void testInvalidPatient() throws Exception { - Parameters inputParameters = buildInputParameters(new Patient(), ourOldCoverage, ourNewCoverage); + Parameters inputParameters = buildInputParameters(new Patient(), ourOldCoverage, ourNewCoverage, ourConsent); performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Parameter \\\"" + PARAM_MEMBER_PATIENT + "\\\" is required."); } @Test public void testInvalidOldCoverage() throws Exception { - Parameters inputParameters = buildInputParameters(ourPatient, new Coverage(), ourNewCoverage); + Parameters inputParameters = buildInputParameters(ourPatient, new Coverage(), ourNewCoverage, ourConsent); performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Parameter \\\"" + PARAM_OLD_COVERAGE + "\\\" is required."); } @Test public void testInvalidNewCoverage() throws Exception { - Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, new Coverage()); + Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, new Coverage(), ourConsent); performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Parameter \\\"" + PARAM_NEW_COVERAGE + "\\\" is required."); } + + @Test + public void testInvalidConsent() throws Exception { + Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, new Consent()); + performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, + "Parameter \\\"" + PARAM_CONSENT + "\\\" is required."); + } + + @Test + public void testMissingPatientFamilyName() throws Exception { + Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, ourConsent); + performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, + "Parameter \\\"" + PARAM_MEMBER_PATIENT_NAME + "\\\" is required."); + } + + @Test + public void testMissingPatientBirthdate() throws Exception { + ourPatient.setName(Lists.newArrayList(new HumanName() + .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))); + Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, ourConsent); + performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, + "Parameter \\\"" + PARAM_MEMBER_PATIENT_BIRTHDATE + "\\\" is required."); + } + + @Test + public void testMissingConsentPatientReference() throws Exception { + ourPatient.setName(Lists.newArrayList(new HumanName() + .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))) + .setBirthDateElement(new DateType("2020-01-01")); + + Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, ourConsent); + performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, + "Parameter \\\"" + PARAM_CONSENT_PATIENT_REFERENCE + "\\\" is required."); + } + + @Test + public void testMissingConsentPerformerReference() throws Exception { + ourPatient.setName(Lists.newArrayList(new HumanName() + .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))) + .setBirthDateElement(new DateType("2020-01-01")); + + ourConsent.setPatient(new Reference("Patient/1")); + Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, ourConsent); + performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, + "Parameter \\\"" + PARAM_CONSENT_PERFORMER_REFERENCE + "\\\" is required."); + } } @@ -223,7 +343,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes public void testMemberMatchByCoverageIdentifier() throws Exception { createCoverageWithBeneficiary(true, true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); Parameters parametersResponse = performOperation(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters); validateMemberPatient(parametersResponse); @@ -244,6 +364,12 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes assertEquals(theOriginalCoverage.getIdentifierFirstRep().getValue(), respCoverage.getIdentifierFirstRep().getValue()); } + private void validateConsentPatientAndPerformerRef(Patient thePatient, Consent theConsent) { + String patientRef = thePatient.getIdElement().toUnqualifiedVersionless().getValue(); + assertEquals(patientRef, theConsent.getPatient().getReference()); + assertEquals(patientRef, theConsent.getPerformer().get(0).getReference()); + } + private void validateMemberPatient(Parameters response) { // parameter MemberPatient must have a new identifier with: @@ -279,17 +405,29 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes @Test public void testNoCoverageMatchFound() throws Exception { - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); performOperationExpecting422(ourServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Could not find coverage for member"); } + @Test + public void testConsentUpdatePatientAndPerformer() throws Exception { + createCoverageWithBeneficiary(true, true); + Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters parametersResponse = performOperation(ourServerBase + ourQuery, + EncodingEnum.JSON, inputParameters); - private Parameters buildInputParameters(Patient thePatient, Coverage theOldCoverage, Coverage theNewCoverage) { + Consent respConsent = validateResponseConsent(parametersResponse, myConsent); + validateConsentPatientAndPerformerRef(myPatient, respConsent); + } + + + private Parameters buildInputParameters(Patient thePatient, Coverage theOldCoverage, Coverage theNewCoverage, Consent theConsent) { Parameters p = new Parameters(); ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_MEMBER_PATIENT, thePatient); ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_OLD_COVERAGE, theOldCoverage); ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_NEW_COVERAGE, theNewCoverage); + ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_CONSENT, theConsent); return p; } @@ -345,6 +483,19 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes assertFalse(coding.getUserSelected()); } + /** + * Validates that consent from the response is same as the received consent with additional identifier and extension + */ + private Consent validateResponseConsent(Parameters theResponse, Consent theOriginalConsent) { + List consentList = ParametersUtil.getNamedParameters(this.getFhirContext(), theResponse, PARAM_CONSENT); + assertEquals(1, consentList.size()); + Consent respConsent= (Consent) theResponse.getParameter().get(2).getResource(); + assertEquals(theOriginalConsent.getScope().getCodingFirstRep().getSystem(), respConsent.getScope().getCodingFirstRep().getSystem()); + assertEquals(theOriginalConsent.getScope().getCodingFirstRep().getCode(), respConsent.getScope().getCodingFirstRep().getCode()); + assertEquals(myMemberMatcherR4Helper.CONSENT_IDENTIFIER_CODE_SYSTEM, respConsent.getIdentifier().get(0).getSystem()); + assertNotNull(respConsent.getIdentifier().get(0).getValue()); + return respConsent; + } } diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index 3001abce656..1ed22408ee5 100644 --- a/hapi-fhir-jpaserver-test-r4b/pom.xml +++ b/hapi-fhir-jpaserver-test-r4b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index b7691f2314b..a84c5d01af4 100644 --- a/hapi-fhir-jpaserver-test-r5/pom.xml +++ b/hapi-fhir-jpaserver-test-r5/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index b19277e2d3a..9ca5a676fa1 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index e94c5fe7be3..41298dfc6f7 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 475273cbfd2..1d9e1d02ef1 100644 --- a/hapi-fhir-server-mdm/pom.xml +++ b/hapi-fhir-server-mdm/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 0a86cffe6ba..567d2d91d91 100644 --- a/hapi-fhir-server-openapi/pom.xml +++ b/hapi-fhir-server-openapi/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 939c3215739..11d42001f7f 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index d516cd9aa5c..6b54240e8ec 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 6dda78886cb..d604e590ee6 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index d23b43c8885..a2b86796529 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index f7df0a7dfed..90a9ac73b68 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index f850c3d590b..040bd66fd2e 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 7be7a52667a..f934e564e8c 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 57fdb3ef852..d00b63f9e79 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 67b35543832..79a854a1e53 100644 --- a/hapi-fhir-sql-migrate/pom.xml +++ b/hapi-fhir-sql-migrate/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 8f3e2d962f0..66d907374b1 100644 --- a/hapi-fhir-storage-batch2-jobs/pom.xml +++ b/hapi-fhir-storage-batch2-jobs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index e4d7deb6b51..1b6ba40bef2 100644 --- a/hapi-fhir-storage-batch2/pom.xml +++ b/hapi-fhir-storage-batch2/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index efbbb95c559..0cc54962563 100644 --- a/hapi-fhir-storage-mdm/pom.xml +++ b/hapi-fhir-storage-mdm/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index 71d4387cdec..76f9b766ddd 100644 --- a/hapi-fhir-storage-test-utilities/pom.xml +++ b/hapi-fhir-storage-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 73614ba09bb..3002598eb2b 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index a690cbd863b..7fb29f3b479 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index e1f1462884c..1ab9b676a87 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index cd5a96aa6d5..e870ec9b19a 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index c967e7ed072..a0888be5bad 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 02566a02587..299b268d308 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index 09d0469a850..f8e1975157b 100644 --- a/hapi-fhir-structures-r4b/pom.xml +++ b/hapi-fhir-structures-r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 85a9951e805..99903f06a14 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 786a4dbae6c..5df3a925631 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index c2ea5180f34..e4474237130 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java index a5e83c74644..6fb3dcb66b2 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java @@ -1,9 +1,9 @@ package ca.uhn.fhir.to; -import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; @@ -35,7 +35,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static ca.uhn.fhir.util.UrlUtil.sanitizeUrlPart; import static org.apache.commons.lang3.StringUtils.defaultString; diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 554b53e3526..c2a1a7eb6eb 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index f9e25f07842..1cda9139883 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index ce79b51c3b0..6570d325bd4 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index dc790e26971..ddf5804120e 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index efae68d09c6..5abcf27bd4a 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 0bec314353c..e6bf11151fc 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index df1ca4fc9b2..0d041806e5c 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml @@ -65,42 +65,42 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4b - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 0268f0a7b46..3700638251c 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 912bb270c95..be98dd3f52a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io @@ -1998,7 +1998,7 @@ ca.uhn.hapi.fhir hapi-fhir-checkstyle - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index 0678f314119..b247541c7a6 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index 302e863962c..2277e49cecc 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 3434b73cd9b..5f60aff00de 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.3.0-PRE1-SNAPSHOT + 6.3.0-PRE2-SNAPSHOT ../../pom.xml