diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0660fb8843c..0c8988aebd2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -24,6 +24,8 @@ stages: module: hapi-fhir-jpaserver-test-dstu3 - name: hapi_fhir_jpaserver_test_r4 module: hapi-fhir-jpaserver-test-r4 + - name: hapi_fhir_jpaserver_test_r4b + module: hapi-fhir-jpaserver-test-r4b - name: hapi_fhir_jpaserver_test_r5 module: hapi-fhir-jpaserver-test-r5 - name: hapi_fhir_jpaserver_test_utilities diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index ec5a611e570..7f63c5980be 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 74caf5ac438..6ed9f03ad8a 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 733a249c638..017efa859e5 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index 88249a18310..3e48b0dc0ee 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -194,6 +194,8 @@ public class FhirContext { myVersion = FhirVersionEnum.DSTU3.getVersionImplementation(); } else if (FhirVersionEnum.R4.isPresentOnClasspath()) { myVersion = FhirVersionEnum.R4.getVersionImplementation(); + } else if (FhirVersionEnum.R4B.isPresentOnClasspath()) { + myVersion = FhirVersionEnum.R4B.getVersionImplementation(); } else { throw new IllegalStateException(Msg.code(1681) + getLocalizer().getMessage(FhirContext.class, "noStructures")); } @@ -238,6 +240,14 @@ public class FhirContext { return forCached(FhirVersionEnum.DSTU2); } + /** + * @since 6.2.0 + */ + public static FhirContext forDstu2Hl7OrgCached() { + return forCached(FhirVersionEnum.DSTU2_HL7ORG); + } + + /** * @since 5.5.0 */ @@ -252,6 +262,13 @@ public class FhirContext { return forCached(FhirVersionEnum.R4); } + /** + * @since 6.1.0 + */ + public static FhirContext forR4BCached() { + return forCached(FhirVersionEnum.R4B); + } + /** * @since 5.5.0 */ @@ -1180,6 +1197,15 @@ public class FhirContext { return new FhirContext(FhirVersionEnum.R4); } + /** + * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R4B R4B} + * + * @since 6.2.0 + */ + public static FhirContext forR4B() { + return new FhirContext(FhirVersionEnum.R4B); + } + /** * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R5 R5} * @@ -1217,4 +1243,5 @@ public class FhirContext { } return retVal; } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java index fbeb38b6c6c..55082c59dd6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java @@ -45,6 +45,8 @@ public enum FhirVersionEnum { R4("org.hl7.fhir.r4.hapi.ctx.FhirR4", null, true, new R4Version()), + R4B("org.hl7.fhir.r4b.hapi.ctx.FhirR4B", null, true, new R4BVersion()), + R5("org.hl7.fhir.r5.hapi.ctx.FhirR5", null, true, new R5Version()); // If you add new constants, add to the various methods below too! @@ -139,6 +141,8 @@ public enum FhirVersionEnum { return FhirContext.forDstu3(); case R4: return FhirContext.forR4(); + case R4B: + return FhirContext.forR4B(); case R5: return FhirContext.forR5(); } @@ -232,6 +236,26 @@ public enum FhirVersionEnum { } + private static class R4BVersion implements IVersionProvider { + + private String myVersion; + + R4BVersion() { + try { + Class c = Class.forName("org.hl7.fhir.r4b.model.Constants"); + myVersion = (String) c.getDeclaredField("VERSION").get(null); + } catch (Exception e) { + myVersion = "4.3.0"; + } + } + + @Override + public String provideVersion() { + return myVersion; + } + + } + private static class R5Version implements IVersionProvider { private String myVersion; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index 1e7a6b79c23..15489a44e15 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -341,6 +341,10 @@ class ModelScanner { if (resourceDef.getStructureVersion() == myVersion) { myNameToResourceDefinitions.put(resourceNameLowerCase, resourceDef); } + // TODO: Address this when core lib version is bumped + if (resourceDef.getStructureVersion() == FhirVersionEnum.R5 && myVersion == FhirVersionEnum.R4B) { + myNameToResourceDefinitions.put(resourceNameLowerCase, resourceDef); + } } myIdToResourceDefinition.put(resourceId, resourceDef); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeResourceDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeResourceDefinition.java index a16ea4198d4..ba2df98db2d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeResourceDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeResourceDefinition.java @@ -65,7 +65,14 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini } myStructureVersion = instance.getStructureFhirVersionEnum(); if (myStructureVersion != theContext.getVersion().getVersion()) { - throw new ConfigurationException(Msg.code(1731) + myContext.getLocalizer().getMessage(getClass(), "typeWrongVersion", theContext.getVersion().getVersion(), theClass.getName(), myStructureVersion)); + if (myStructureVersion == FhirVersionEnum.R5 && theContext.getVersion().getVersion() == FhirVersionEnum.R4B) { + // TODO: remove this exception once we've bumped FHIR core to a new version + // TODO: also fix the TODO in ModelScanner + // TODO: also fix the TODO in RestfulServerUtils + // TODO: also fix the TODO in BaseParser + } else { + throw new ConfigurationException(Msg.code(1731) + myContext.getLocalizer().getMessage(getClass(), "typeWrongVersion", theContext.getVersion().getVersion(), theClass.getName(), myStructureVersion)); + } } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java index d5749b4242d..97aa7a3c776 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java @@ -288,6 +288,9 @@ public abstract class BaseParser implements IParser { Validate.notNull(theWriter, "theWriter can not be null"); Validate.notNull(theEncodeContext, "theEncodeContext can not be null"); + if (myContext.getVersion().getVersion() == FhirVersionEnum.R4B && theResource.getStructureFhirVersionEnum() == FhirVersionEnum.R5) { + // TODO: remove once we've bumped the core lib version + } else if (theResource.getStructureFhirVersionEnum() != myContext.getVersion().getVersion()) { throw new IllegalArgumentException(Msg.code(1829) + "This parser is for FHIR version " + myContext.getVersion().getVersion() + " - Can not encode a structure for version " + theResource.getStructureFhirVersionEnum()); } 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 ce7b449ecdd..46cee008a5e 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 @@ -1,13 +1,13 @@ -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionRefersToUnknownCs=Unknown CodeSystem URI "{0}" referenced from ValueSet -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetNotYetExpanded=ValueSet "{0}" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: {1} | {2} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetNotYetExpanded_OffsetNotAllowed=ValueSet expansion can not combine "offset" with "ValueSet.compose.exclude" unless the ValueSet has been pre-expanded. ValueSet "{0}" must be pre-expanded for this operation to work. -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetExpandedUsingPreExpansion=ValueSet was expanded using an expansion that was pre-calculated at {0} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetExpandedUsingInMemoryExpansion=ValueSet with URL "{0}" was expanded using an in-memory expansion -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.validationPerformedAgainstPreExpansion=Code validation occurred using a ValueSet expansion that was pre-calculated at {0} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetNotFoundInTerminologyDatabase=ValueSet can not be found in terminology database: {0} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetPreExpansionInvalidated=ValueSet with URL "{0}" precaluclated expansion with {1} concept(s) has been invalidated -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.valueSetCantInvalidateNotYetPrecalculated=ValueSet with URL "{0}" already has status: {1} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.expansionRefersToUnknownCs=Unknown CodeSystem URI "{0}" referenced from ValueSet +ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetNotYetExpanded=ValueSet "{0}" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: {1} | {2} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetNotYetExpanded_OffsetNotAllowed=ValueSet expansion can not combine "offset" with "ValueSet.compose.exclude" unless the ValueSet has been pre-expanded. ValueSet "{0}" must be pre-expanded for this operation to work. +ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetExpandedUsingPreExpansion=ValueSet was expanded using an expansion that was pre-calculated at {0} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetExpandedUsingInMemoryExpansion=ValueSet with URL "{0}" was expanded using an in-memory expansion +ca.uhn.fhir.jpa.term.TermReadSvcImpl.validationPerformedAgainstPreExpansion=Code validation occurred using a ValueSet expansion that was pre-calculated at {0} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetNotFoundInTerminologyDatabase=ValueSet can not be found in terminology database: {0} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetPreExpansionInvalidated=ValueSet with URL "{0}" precaluclated expansion with {1} concept(s) has been invalidated +ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetCantInvalidateNotYetPrecalculated=ValueSet with URL "{0}" already has status: {1} # Core Library Messages @@ -146,14 +146,14 @@ ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider.unknownPath=Unable to find ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider.unknownType=Content in resource of type {0} at path {1} is not appropriate for binary storage: {2} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateCodeSystemUrl=Can not create multiple CodeSystem resources with CodeSystem.url "{0}", already have one with resource ID: {1} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateCodeSystemUrlAndVersion=Can not create multiple CodeSystem resources with CodeSystem.url "{0}" and CodeSystem.version "{1}", already have one with resource ID: {2} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateConceptMapUrlAndVersion=Can not create multiple ConceptMap resources with ConceptMap.url "{0}" and ConceptMap.version "{1}", already have one with resource ID: {2} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateValueSetUrl=Can not create multiple ValueSet resources with ValueSet.url "{0}", already have one with resource ID: {1} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted! -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateValueSetUrlAndVersion=Can not create multiple ValueSet resources with ValueSet.url "{0}" and ValueSet.version "{1}", already have one with resource ID: {2} -ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotUpdateUrlOrVersionForValueSetResource=Cannot update URL or version for ValueSet resource. Existing ValueSet resource with resource ID {0} found with ValueSet.url "{1}" and ValueSet.version "{2}" +ca.uhn.fhir.jpa.term.TermReadSvcImpl.cannotCreateDuplicateCodeSystemUrl=Can not create multiple CodeSystem resources with CodeSystem.url "{0}", already have one with resource ID: {1} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.cannotCreateDuplicateCodeSystemUrlAndVersion=Can not create multiple CodeSystem resources with CodeSystem.url "{0}" and CodeSystem.version "{1}", already have one with resource ID: {2} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.cannotCreateDuplicateConceptMapUrlAndVersion=Can not create multiple ConceptMap resources with ConceptMap.url "{0}" and ConceptMap.version "{1}", already have one with resource ID: {2} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.cannotCreateDuplicateValueSetUrl=Can not create multiple ValueSet resources with ValueSet.url "{0}", already have one with resource ID: {1} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted! +ca.uhn.fhir.jpa.term.TermReadSvcImpl.cannotCreateDuplicateValueSetUrlAndVersion=Can not create multiple ValueSet resources with ValueSet.url "{0}" and ValueSet.version "{1}", already have one with resource ID: {2} +ca.uhn.fhir.jpa.term.TermReadSvcImpl.cannotUpdateUrlOrVersionForValueSetResource=Cannot update URL or version for ValueSet resource. Existing ValueSet resource with resource ID {0} found with ValueSet.url "{1}" and ValueSet.version "{2}" ca.uhn.fhir.jpa.patch.JsonPatchUtils.failedToApplyPatch=Failed to apply JSON patch to {0}: {1} diff --git a/hapi-fhir-batch/pom.xml b/hapi-fhir-batch/pom.xml index e9f7882a621..319d18e5bc9 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 61f305004ab..1cd082147a4 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 031e1f7b5e9..c2f7cc50762 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 2782b76c2db..df287562c4d 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on-no-colour.xml b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on-no-colour.xml index be065ef56df..32e5a0e8b77 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on-no-colour.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on-no-colour.xml @@ -33,7 +33,7 @@ - + diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on.xml b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on.xml index 100d8b27402..8473d1ae9dc 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on.xml @@ -34,7 +34,7 @@ - + diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index f85f2a4a6df..3cae7776dfb 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 b5b6b6ca7e4..48a41966688 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index 41f62693b83..c1b1200e29a 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -34,11 +34,9 @@ import ca.uhn.fhir.jpa.delete.ThreadSafeResourceDeleterSvc; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; -import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; +import ca.uhn.fhir.jpa.provider.JpaSystemProvider; import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; -import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; -import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; @@ -103,17 +101,15 @@ public class JpaServerDemo extends RestfulServer { */ List systemProvider = new ArrayList(); if (fhirVersion == FhirVersionEnum.DSTU2) { - systemProvider.add(myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class)); } else if (fhirVersion == FhirVersionEnum.DSTU3) { - systemProvider.add(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class)); systemProvider.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); } else if (fhirVersion == FhirVersionEnum.R4) { - systemProvider.add(myAppCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class)); systemProvider.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); systemProvider.add(myAppCtx.getBean(JpaConfig.GRAPHQL_PROVIDER_NAME)); } else { throw new IllegalStateException(Msg.code(1533)); } + systemProvider.add(myAppCtx.getBean(JpaSystemProvider.class)); registerProviders(systemProvider); /* diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 5c8458b63ae..0bb8197d722 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 33ecf251907..db2b5fcb386 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 7300c5aa95a..959a0bccb9a 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 5ba6f99a74a..f7dc1dceb31 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java new file mode 100644 index 00000000000..cd7fc156ea6 --- /dev/null +++ b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java @@ -0,0 +1,447 @@ +package ca.uhn.hapi.converters.canonical; + +/*- + * #%L + * HAPI FHIR - Converter + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; +import ca.uhn.fhir.model.dstu2.composite.CodingDt; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_40; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_50; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_50; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_40; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; +import org.hl7.fhir.dstu2.model.Resource; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.ValueSet; +import org.hl7.fhir.r5.model.CapabilityStatement; + +import java.util.List; + +/** + * This class converts versions of various resources to/from a canonical version + * of the resource. The specific version that is considered canonical is arbitrary + * for historical reasons, generally it will be R4 or R5 but this varies by resource + * type. + *

+ * This class is an internal HAPI FHIR API and can change without notice at any time. + * Use with caution! + *

+ */ +public class VersionCanonicalizer { + + private static final BaseAdvisor_30_50 ADVISOR_30_50 = new BaseAdvisor_30_50(false); + private static final BaseAdvisor_30_40 ADVISOR_30_40 = new BaseAdvisor_30_40(false); + private static final BaseAdvisor_10_40 ADVISOR_10_40 = new BaseAdvisor_10_40(false); + private static final BaseAdvisor_10_50 ADVISOR_10_50 = new BaseAdvisor_10_50(false); + private static final BaseAdvisor_40_50 ADVISOR_40_50 = new BaseAdvisor_40_50(false); + private static final BaseAdvisor_43_50 ADVISOR_43_50 = new BaseAdvisor_43_50(false); + @SuppressWarnings("rawtypes") + private final IStrategy myStrategy; + + public VersionCanonicalizer(FhirContext theTargetContext) { + this(theTargetContext.getVersion().getVersion()); + } + + @SuppressWarnings("EnumSwitchStatementWhichMissesCases") + public VersionCanonicalizer(FhirVersionEnum theTargetVersion) { + switch (theTargetVersion) { + case DSTU2: + myStrategy = new Dstu2Strategy(); + break; + case DSTU3: + myStrategy = new Dstu3Strategy(); + break; + case R4: + myStrategy = new R4Strategy(); + break; + case R4B: + myStrategy = new R4BStrategy(); + break; + case R5: + myStrategy = new R5Strategy(); + break; + default: + throw new IllegalStateException(Msg.code(193) + "Can't handle version: " + theTargetVersion); + } + } + + + /** + * Canonical version: R5 + */ + public CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement) { + return myStrategy.capabilityStatementToCanonical(theCapabilityStatement); + } + + /** + * Canonical version: R4 + */ + public CodeableConcept codeableConceptToCanonical(IBaseDatatype theCodeableConcept) { + if (theCodeableConcept == null) { + return null; + } + return myStrategy.codeableConceptToCanonical(theCodeableConcept); + } + + /** + * Canonical version: R4 + */ + public Coding codingToCanonical(IBaseCoding theCodingToValidate) { + if (theCodingToValidate == null) { + return null; + } + return myStrategy.codingToCanonical(theCodingToValidate); + } + + /** + * Canonical version: R4 + */ + public ValueSet valueSetToCanonical(IBaseResource theValueSet) { + if (theValueSet == null) { + return null; + } + return myStrategy.valueSetToCanonical(theValueSet); + } + + /** + * Canonical version: R4 + */ + public CodeSystem codeSystemToCanonical(IBaseResource theCodeSystem) { + return myStrategy.codeSystemToCanonical(theCodeSystem); + } + + /** + * Canonical version: R4 + */ + public IBaseResource valueSetFromCanonical(ValueSet theValueSet) { + return myStrategy.valueSetFromCanonical(theValueSet); + } + + /** + * Canonical version: R4 + */ + public ConceptMap conceptMapToCanonical(IBaseResource theConceptMap) { + return myStrategy.conceptMapToCanonical(theConceptMap); + } + + private interface IStrategy { + + CapabilityStatement capabilityStatementToCanonical(T theCapabilityStatement); + + Coding codingToCanonical(IBaseCoding theCoding); + + CodeableConcept codeableConceptToCanonical(IBaseDatatype theCodeableConcept); + + ValueSet valueSetToCanonical(IBaseResource theValueSet); + + CodeSystem codeSystemToCanonical(IBaseResource theCodeSystem); + + IBaseResource valueSetFromCanonical(ValueSet theValueSet); + + ConceptMap conceptMapToCanonical(IBaseResource theConceptMap); + } + + private class Dstu2Strategy implements IStrategy { + + private final FhirContext myDstu2Hl7OrgContext = FhirContext.forDstu2Hl7OrgCached(); + + private final FhirContext myDstu2Context = FhirContext.forDstu2Cached(); + + @Override + public CapabilityStatement capabilityStatementToCanonical(ca.uhn.fhir.model.dstu2.resource.BaseResource theCapabilityStatement) { + org.hl7.fhir.dstu2.model.Resource reencoded = reencodeToHl7Org(theCapabilityStatement); + return (CapabilityStatement) VersionConvertorFactory_10_50.convertResource(reencoded, ADVISOR_10_50); + } + + @Override + public Coding codingToCanonical(IBaseCoding theCoding) { + CodingDt coding = (CodingDt) theCoding; + Coding retVal = new Coding(); + retVal.setCode(coding.getCode()); + retVal.setSystem(coding.getSystem()); + retVal.setDisplay(coding.getDisplay()); + retVal.setVersion(coding.getVersion()); + if (coding.getUserSelected() != null) { + retVal.setUserSelected(coding.getUserSelected()); + } + return retVal; + } + + @Override + public CodeableConcept codeableConceptToCanonical(IBaseDatatype theCodeableConcept) { + CodeableConceptDt codeableConcept = (CodeableConceptDt) theCodeableConcept; + + CodeableConcept retVal = new CodeableConcept(); + retVal.setText(codeableConcept.getText()); + for (CodingDt next : codeableConcept.getCoding()) { + retVal.addCoding(codingToCanonical(next)); + } + + return retVal; + } + + @Override + public ValueSet valueSetToCanonical(IBaseResource theValueSet) { + org.hl7.fhir.dstu2.model.Resource reencoded = reencodeToHl7Org(theValueSet); + return (ValueSet) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40); + } + + @Override + public CodeSystem codeSystemToCanonical(IBaseResource theCodeSystem) { + CodeSystem retVal = new CodeSystem(); + + ca.uhn.fhir.model.dstu2.resource.ValueSet input = (ca.uhn.fhir.model.dstu2.resource.ValueSet) theCodeSystem; + retVal.setUrl(input.getUrl()); + + for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept next : input.getCodeSystem().getConcept()) { + translateAndAddConcept(next, retVal.getConcept()); + } + + return retVal; + } + + private void translateAndAddConcept(ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept theSource, List theTarget) { + CodeSystem.ConceptDefinitionComponent targetConcept = new CodeSystem.ConceptDefinitionComponent(); + targetConcept.setCode(theSource.getCode()); + targetConcept.setDisplay(theSource.getDisplay()); + + for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConceptDesignation next : theSource.getDesignation()) { + CodeSystem.ConceptDefinitionDesignationComponent targetDesignation = targetConcept.addDesignation(); + targetDesignation.setLanguage(next.getLanguage()); + targetDesignation.setValue(next.getValue()); + if (next.getUse() != null) { + targetDesignation.setUse(codingToCanonical(next.getUse())); + } + } + + for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept nextChild : theSource.getConcept()) { + translateAndAddConcept(nextChild, targetConcept.getConcept()); + } + + theTarget.add(targetConcept); + } + + @Override + public IBaseResource valueSetFromCanonical(ValueSet theValueSet) { + Resource valueSetDstu2Hl7Org = VersionConvertorFactory_10_40.convertResource(theValueSet, ADVISOR_10_40); + return reencodeFromHl7Org(valueSetDstu2Hl7Org); + } + + @Override + public ConceptMap conceptMapToCanonical(IBaseResource theConceptMap) { + org.hl7.fhir.dstu2.model.Resource reencoded = reencodeToHl7Org(theConceptMap); + return (ConceptMap) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40); + } + + private Resource reencodeToHl7Org(IBaseResource theInput) { + return (Resource) myDstu2Hl7OrgContext.newJsonParser().parseResource(myDstu2Context.newJsonParser().encodeResourceToString(theInput)); + } + + private IBaseResource reencodeFromHl7Org(Resource theInput) { + return myDstu2Context.newJsonParser().parseResource(myDstu2Hl7OrgContext.newJsonParser().encodeResourceToString(theInput)); + } + + } + + private class Dstu3Strategy implements IStrategy { + + @Override + public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.dstu3.model.Resource theCapabilityStatement) { + return (CapabilityStatement) VersionConvertorFactory_30_50.convertResource(theCapabilityStatement, ADVISOR_30_50); + } + + @Override + public Coding codingToCanonical(IBaseCoding theCoding) { + return (org.hl7.fhir.r4.model.Coding) VersionConvertorFactory_30_40.convertType((org.hl7.fhir.dstu3.model.Coding) theCoding, ADVISOR_30_40); + } + + @Override + public CodeableConcept codeableConceptToCanonical(IBaseDatatype theCodeableConcept) { + return (org.hl7.fhir.r4.model.CodeableConcept) VersionConvertorFactory_30_40.convertType((org.hl7.fhir.dstu3.model.CodeableConcept) theCodeableConcept, ADVISOR_30_40); + } + + @Override + public ValueSet valueSetToCanonical(IBaseResource theValueSet) { + return (ValueSet) VersionConvertorFactory_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theValueSet, ADVISOR_30_40); + } + + @Override + public CodeSystem codeSystemToCanonical(IBaseResource theCodeSystem) { + return (CodeSystem) VersionConvertorFactory_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theCodeSystem, ADVISOR_30_40); + } + + @Override + public IBaseResource valueSetFromCanonical(ValueSet theValueSet) { + return VersionConvertorFactory_30_40.convertResource(theValueSet, ADVISOR_30_40); + } + + @Override + public ConceptMap conceptMapToCanonical(IBaseResource theConceptMap) { + return (ConceptMap) VersionConvertorFactory_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theConceptMap, ADVISOR_30_40); + } + } + + private class R4Strategy implements IStrategy { + @Override + public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.r4.model.Resource theCapabilityStatement) { + return (CapabilityStatement) VersionConvertorFactory_40_50.convertResource(theCapabilityStatement, ADVISOR_40_50); + } + + @Override + public Coding codingToCanonical(IBaseCoding theCoding) { + return (Coding) theCoding; + } + + @Override + public CodeableConcept codeableConceptToCanonical(IBaseDatatype theCodeableConcept) { + return (CodeableConcept) theCodeableConcept; + } + + @Override + public ValueSet valueSetToCanonical(IBaseResource theValueSet) { + return (ValueSet) theValueSet; + } + + @Override + public CodeSystem codeSystemToCanonical(IBaseResource theCodeSystem) { + return (CodeSystem) theCodeSystem; + } + + @Override + public IBaseResource valueSetFromCanonical(ValueSet theValueSet) { + return theValueSet; + } + + @Override + public ConceptMap conceptMapToCanonical(IBaseResource theConceptMap) { + return (ConceptMap) theConceptMap; + } + + } + + private class R4BStrategy implements IStrategy { + + @Override + public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.r4b.model.Resource theCapabilityStatement) { + return (CapabilityStatement) VersionConvertorFactory_43_50.convertResource(theCapabilityStatement, ADVISOR_43_50); + } + + @Override + public Coding codingToCanonical(IBaseCoding theCoding) { + org.hl7.fhir.r5.model.Coding r5coding = (org.hl7.fhir.r5.model.Coding) VersionConvertorFactory_43_50.convertType((org.hl7.fhir.r4b.model.Coding) theCoding, ADVISOR_43_50); + return (org.hl7.fhir.r4.model.Coding) VersionConvertorFactory_40_50.convertType(r5coding, ADVISOR_40_50); + } + + @Override + public CodeableConcept codeableConceptToCanonical(IBaseDatatype theCodeableConcept) { + org.hl7.fhir.r5.model.CodeableConcept r5coding = (org.hl7.fhir.r5.model.CodeableConcept) VersionConvertorFactory_43_50.convertType((org.hl7.fhir.r4b.model.CodeableConcept) theCodeableConcept, ADVISOR_43_50); + return (org.hl7.fhir.r4.model.CodeableConcept) VersionConvertorFactory_40_50.convertType(r5coding, ADVISOR_40_50); + } + + @Override + public ValueSet valueSetToCanonical(IBaseResource theValueSet) { + org.hl7.fhir.r5.model.ValueSet valueSetR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_43_50.convertResource((org.hl7.fhir.r4b.model.Resource) theValueSet, ADVISOR_43_50); + return (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSetR5, ADVISOR_40_50); + } + + @Override + public CodeSystem codeSystemToCanonical(IBaseResource theCodeSystem) { + org.hl7.fhir.r5.model.CodeSystem codeSystemR5 = (org.hl7.fhir.r5.model.CodeSystem) VersionConvertorFactory_43_50.convertResource((org.hl7.fhir.r4b.model.Resource) theCodeSystem, ADVISOR_43_50); + return (org.hl7.fhir.r4.model.CodeSystem) VersionConvertorFactory_40_50.convertResource(codeSystemR5, ADVISOR_40_50); + } + + @Override + public IBaseResource valueSetFromCanonical(ValueSet theValueSet) { + org.hl7.fhir.r5.model.ValueSet valueSetR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(theValueSet, ADVISOR_40_50); + return VersionConvertorFactory_43_50.convertResource(valueSetR5, ADVISOR_43_50); + } + + @Override + public ConceptMap conceptMapToCanonical(IBaseResource theConceptMap) { + org.hl7.fhir.r5.model.ConceptMap conceptMapR5 = (org.hl7.fhir.r5.model.ConceptMap) VersionConvertorFactory_43_50.convertResource((org.hl7.fhir.r4b.model.Resource) theConceptMap, ADVISOR_43_50); + return (ConceptMap) VersionConvertorFactory_40_50.convertResource(conceptMapR5, ADVISOR_40_50); + } + + } + + + private class R5Strategy implements IStrategy { + + @Override + public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.r5.model.Resource theCapabilityStatement) { + return (CapabilityStatement) theCapabilityStatement; + } + + @Override + public Coding codingToCanonical(IBaseCoding theCoding) { + return (org.hl7.fhir.r4.model.Coding) VersionConvertorFactory_40_50.convertType((org.hl7.fhir.r5.model.Coding) theCoding, ADVISOR_40_50); + } + + @Override + public CodeableConcept codeableConceptToCanonical(IBaseDatatype theCodeableConcept) { + return (org.hl7.fhir.r4.model.CodeableConcept) VersionConvertorFactory_40_50.convertType((org.hl7.fhir.r5.model.CodeableConcept) theCodeableConcept, ADVISOR_40_50); + } + + @Override + public ValueSet valueSetToCanonical(IBaseResource theValueSet) { + return (ValueSet) VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r5.model.ValueSet) theValueSet, ADVISOR_40_50); + } + + @Override + public CodeSystem codeSystemToCanonical(IBaseResource theCodeSystem) { + return (CodeSystem) VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem, ADVISOR_40_50); + } + + @Override + public IBaseResource valueSetFromCanonical(ValueSet theValueSet) { + return VersionConvertorFactory_40_50.convertResource(theValueSet, ADVISOR_40_50); + } + + @Override + public ConceptMap conceptMapToCanonical(IBaseResource theConceptMap) { + return (ConceptMap) VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r5.model.ConceptMap) theConceptMap, ADVISOR_40_50); + } + + } + + +} + + + + + + diff --git a/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java new file mode 100644 index 00000000000..ea34bc606fb --- /dev/null +++ b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java @@ -0,0 +1,23 @@ +package ca.uhn.hapi.converters.canonical; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.dstu2.composite.CodingDt; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.r4.model.Coding; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class VersionCanonicalizerTest { + + @Test + public void testToCanonicalCoding() { + VersionCanonicalizer canonicalizer = new VersionCanonicalizer(FhirVersionEnum.DSTU2); + IBaseCoding coding = new CodingDt("dstuSystem", "dstuCode"); + Coding convertedCoding = canonicalizer.codingToCanonical(coding); + assertEquals("dstuCode", convertedCoding.getCode()); + assertEquals("dstuSystem", convertedCoding.getSystem()); + } + + +} diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 323f892861d..5cdffe33d8a 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 2dca570a1bc..65d215271be 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/4150-add-r4b.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/4150-add-r4b.yaml new file mode 100644 index 00000000000..19756f9d9ab --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/4150-add-r4b.yaml @@ -0,0 +1,4 @@ +--- +issue: 4150 +type: add +title: "Support has been added for FHIR R4B (4.3.0). See the [R4B Documentation](/hapi-fhir/docs/getting_started/r4b.html) for more information on what this means." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties index fd2c3ef2f56..03eb67817e6 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties @@ -16,6 +16,7 @@ page.getting_started.introduction=Introduction page.getting_started.versions=FHIR and HAPI FHIR Versions page.getting_started.modules=HAPI FHIR Modules page.getting_started.downloading_and_importing=Downloading and Importing +page.getting_started.r4b=FHIR R4B Support section.model.title=Working With The FHIR Model page.model.working_with_resources=Working With Resources diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/getting_started/r4b.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/getting_started/r4b.md new file mode 100644 index 00000000000..8e355338f24 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/getting_started/r4b.md @@ -0,0 +1,20 @@ +# FHIR R4B + +FHIR R4B is an unusual FHIR release meant to help certain users take advantage of certain FHIR R5 models and features before the release and adoption of FHIR R5. Functionally, R4B is mainly identical to FHIR R4 but with specific resources backported from R5. + +As of HAPI FHIR 6.2.0, this version is now supported. See [FHIR and HAPI FHIR Versions](versions.html) for a complete table of FHIR specification version support in HAPI FHIR software versions. + +**Support for FHIR R4B is experimental** and may never be completely implemented. It has not been extensively tested and we are not currently aiming for feature parity with normal FHIR version support. + +The following notes outline current support and limitations. + +* **FHIR Parsers/Serializers** are well tested and should be fully functional, as there is no need for any specific R4B code in this part of the codebase. + +* **FHIR Client** is well tested and should be fully functional, as there is no need for any specific R4B code in this part of the codebase. + +* **Plain Server** is well tested and should be fully functional, as there is no need for any specific R4B code in this part of the codebase. + +* **Validator** does not currently work with R4B and may crash or cause unexpected results. Note that the underlying InstanceValidator is able to validate R4B resources, so this could be fixed in a future release if demand is there. The [FHIR Validator Site](https://validator.fhir.org/) can also be used. + +* **JPA Server** is able to store data, and supports all basic storage interactions (CRUD). Search also works, including built-in and custom search parameters. However most other functionality has not been implemented or tested, including Subscriptions, Terminology Services, etc. + diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/getting_started/versions.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/getting_started/versions.md index 402614d0d78..16c82e482d1 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/getting_started/versions.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/getting_started/versions.md @@ -21,10 +21,44 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR DSTU2.1 DSTU3 R4 + R4B R5 + + HAPI FHIR 6.2.0 + JDK11 + + 1.0.2 + 1.4.0 + 3.0.2 + 4.0.1 + 4.3.0 + 5.0.0-ballot
9a044604c1
+ + + HAPI FHIR 6.1.0 + JDK11 + + 1.0.2 + 1.4.0 + 3.0.2 + 4.0.1 + + 4.6.0
ba4c12bc30
+ + + HAPI FHIR 6.0.0 + JDK11 + + 1.0.2 + 1.4.0 + 3.0.2 + 4.0.1 + + 5.0.0-snapshot1
0394b96b14
+ HAPI FHIR 5.7.0 JDK8 @@ -33,6 +67,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 5.0.0-snapshot1
0394b96b14
@@ -43,6 +78,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 4.6.0
9b829d9714
@@ -53,6 +89,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 4.6.0
9b829d9714
@@ -63,6 +100,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 4.6.0
9b829d9714
@@ -73,6 +111,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 4.5.0
6cd0af3b8c
@@ -83,6 +122,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 4.5.0
6cd0af3b8c
@@ -93,6 +133,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 4.4.0
56b0acf73f
@@ -103,6 +144,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 4.4.0
56b0acf73f
@@ -113,6 +155,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 4.2.0
e0f3f5cc2c
@@ -123,6 +166,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.2 4.0.1 + 4.1.0
1a7623d866
@@ -133,6 +177,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0 3.0.1 4.0.0 + 4.1.0
e0e3caf9ba
@@ -144,6 +189,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 4.0.0 + HAPI FHIR 3.7.0 @@ -154,6 +200,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 4.0.0 + HAPI FHIR 3.6.0 @@ -164,6 +211,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 3.6.0
1202b2eed0f
+ HAPI FHIR 3.5.0 @@ -174,6 +222,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 3.4.0
13732
+ HAPI FHIR 3.4.0 @@ -184,6 +233,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 3.4.0
13732
+ HAPI FHIR 3.3.0 @@ -194,6 +244,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 3.2.0
13271
+ HAPI FHIR 3.2.0 @@ -204,6 +255,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 3.2.0
12917
+ HAPI FHIR 3.1.0 @@ -214,6 +266,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 3.1.0
12370
+ HAPI FHIR 3.0.0 @@ -224,6 +277,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 3.1.0
12370
+ HAPI FHIR 2.5 @@ -234,6 +288,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 + HAPI FHIR 2.4 @@ -244,6 +299,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 3.0.1 + HAPI FHIR 2.3 @@ -254,6 +310,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.9.0
11501
+ HAPI FHIR 2.2 @@ -264,6 +321,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.8.0
10528
+ HAPI FHIR 2.1 @@ -274,6 +332,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.7.0
10129
+ HAPI FHIR 2.0 @@ -284,6 +343,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.6.0
9663
+ HAPI FHIR 1.6 @@ -294,6 +354,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0
8636
+ HAPI FHIR 1.5 @@ -304,6 +365,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.4.0
8138
+ HAPI FHIR 1.4 @@ -314,6 +376,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR 1.3.0
7602
+ HAPI FHIR 1.3 @@ -324,6 +387,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR + HAPI FHIR 1.2 @@ -334,6 +398,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR + HAPI FHIR 1.1 @@ -344,6 +409,7 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR + diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index a5635730652..b079f4950ef 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -111,6 +111,11 @@ hapi-fhir-jpaserver-test-r4 ${project.version} + + ca.uhn.hapi.fhir + hapi-fhir-jpaserver-test-r4b + ${project.version} + ca.uhn.hapi.fhir hapi-fhir-jpaserver-test-r5 diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 244a42f449b..67af275eea7 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 502d961e6d8..2e7f7f8011a 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 54df4ce63c0..2e7cce21bc6 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -97,6 +97,11 @@ hapi-fhir-structures-r4 ${project.version} + + ca.uhn.hapi.fhir + hapi-fhir-structures-r4b + ${project.version} + ca.uhn.hapi.fhir hapi-fhir-structures-r5 @@ -495,6 +500,22 @@ + + build_r4b + + generate-jparest-server + + + r4b + ca.uhn.fhir.jpa.config + ca.uhn.fhir.jpa.rp.r4b + hapi-fhir-server-resourceproviders-r4b.xml + + + + + + build_r5 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 a031ccf8301..e38742ae454 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 @@ -111,8 +111,10 @@ 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.api.ITermConceptMappingSvc; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.config.TermCodeSystemConfig; import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.validation.ResourceLoaderImpl; @@ -125,6 +127,7 @@ import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationInterce import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc; 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.utilities.graphql.IGraphQLStorageServices; import org.hl7.fhir.utilities.npm.PackageClient; @@ -707,12 +710,6 @@ public class JpaConfig { return new UnknownCodeSystemWarningValidationSupport(theFhirContext); } - @Lazy - @Bean - public MemberMatcherR4Helper memberMatcherR4Helper(FhirContext theFhirContext) { - return new MemberMatcherR4Helper(theFhirContext); - } - @Lazy @Bean public NicknameInterceptor nicknameInterceptor() throws IOException { @@ -723,4 +720,17 @@ public class JpaConfig { public ISynchronousSearchSvc synchronousSearchSvc(){ return new SynchronousSearchSvcImpl(); } + + + @Bean + public VersionCanonicalizer versionCanonicalizer(FhirContext theFhirContext) { + return new VersionCanonicalizer(theFhirContext); + } + + @Bean + public ITermReadSvc terminologyService() { + return new TermReadSvcImpl(); + } + + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaDstu2Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaDstu2Config.java index 36b59ff75d8..be82f6c6c26 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaDstu2Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaDstu2Config.java @@ -4,9 +4,8 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter; import ca.uhn.fhir.jpa.dao.TransactionProcessorVersionAdapterDstu2; -import ca.uhn.fhir.jpa.term.TermReadSvcDstu2; +import ca.uhn.fhir.jpa.provider.JpaSystemProvider; import ca.uhn.fhir.jpa.term.TermVersionAdapterSvcDstu2; -import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; @@ -60,15 +59,11 @@ public class JpaDstu2Config { } @Bean(name = "mySystemProviderDstu2") - public ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2 systemProviderDstu2(FhirContext theFhirContext) { - ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2 retVal = new ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2(); + public JpaSystemProvider systemProviderDstu2(FhirContext theFhirContext) { + JpaSystemProvider retVal = new ca.uhn.fhir.jpa.provider.JpaSystemProvider<>(); retVal.setDao(systemDaoDstu2()); retVal.setContext(theFhirContext); return retVal; } - @Bean - public ITermReadSvc terminologyService() { - return new TermReadSvcDstu2(); - } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/JpaDstu3Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/JpaDstu3Config.java index 101ceb40c37..271f288fc00 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/JpaDstu3Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/JpaDstu3Config.java @@ -11,13 +11,12 @@ import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter; import ca.uhn.fhir.jpa.dao.dstu3.TransactionProcessorVersionAdapterDstu3; 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.term.TermLoaderSvcImpl; -import ca.uhn.fhir.jpa.term.TermReadSvcDstu3; import ca.uhn.fhir.jpa.term.TermVersionAdapterSvcDstu3; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3; import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import org.hl7.fhir.dstu3.model.Bundle; @@ -80,8 +79,8 @@ public class JpaDstu3Config { } @Bean(name = "mySystemProviderDstu3") - public ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3 systemProviderDstu3(FhirContext theFhirContext) { - ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3 retVal = new ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3(); + public JpaSystemProvider systemProviderDstu3(FhirContext theFhirContext) { + JpaSystemProvider retVal = new JpaSystemProvider<>(); retVal.setContext(theFhirContext); retVal.setDao(systemDaoDstu3()); return retVal; @@ -92,9 +91,4 @@ public class JpaDstu3Config { return new TermLoaderSvcImpl(theDeferredStorageSvc, theCodeSystemStorageSvc); } - @Bean - public ITermReadSvcDstu3 terminologyService() { - return new TermReadSvcDstu3(); - } - } 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 4c4872e904f..978514440a2 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 @@ -11,14 +11,16 @@ import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter; 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.MemberMatchR4ResourceProvider; +import ca.uhn.fhir.jpa.provider.r4.MemberMatcherR4Helper; import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl; -import ca.uhn.fhir.jpa.term.TermReadSvcR4; import ca.uhn.fhir.jpa.term.TermVersionAdapterSvcR4; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; 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.Meta; @@ -72,7 +74,7 @@ public class JpaR4Config { @Bean(name = JpaConfig.GRAPHQL_PROVIDER_NAME) @Lazy public GraphQLProvider graphQLProvider(FhirContext theFhirContext, IGraphQLStorageServices theGraphqlStorageServices, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry, IDaoRegistry theDaoRegistry) { - return new GraphQLProviderWithIntrospection(theFhirContext, theValidationSupport, theGraphqlStorageServices, theSearchParamRegistry, theDaoRegistry); + return new GraphQLProviderWithIntrospection(theFhirContext, theValidationSupport, theGraphqlStorageServices, theSearchParamRegistry, theDaoRegistry); } @Bean(name = "mySystemDaoR4") @@ -82,8 +84,8 @@ public class JpaR4Config { } @Bean(name = "mySystemProviderR4") - public ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4 systemProviderR4(FhirContext theFhirContext) { - ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4 retVal = new ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4(); + public JpaSystemProvider systemProviderR4(FhirContext theFhirContext) { + JpaSystemProvider retVal = new JpaSystemProvider<>(); retVal.setContext(theFhirContext); retVal.setDao(systemDaoR4()); return retVal; @@ -95,8 +97,27 @@ public class JpaR4Config { } @Bean - public ITermReadSvcR4 terminologyService() { - return new TermReadSvcR4(); + public MemberMatcherR4Helper memberMatcherR4Helper(FhirContext theFhirContext) { + return new MemberMatcherR4Helper(theFhirContext); } + @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/config/r4b/FhirContextR4BConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4b/FhirContextR4BConfig.java new file mode 100644 index 00000000000..33b1696f5ee --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4b/FhirContextR4BConfig.java @@ -0,0 +1,43 @@ +package ca.uhn.fhir.jpa.config.r4b; + +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.ParserOptions; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +public class FhirContextR4BConfig { + + public static final String DEFAULT_PRESERVE_VERSION_REFS = "AuditEvent.entity.what"; + + @Bean(name = "primaryFhirContext") + @Primary + public FhirContext fhirContextR4B() { + FhirContext retVal = FhirContext.forR4B(); + + // Don't strip versions in some places + ParserOptions parserOptions = retVal.getParserOptions(); + parserOptions.setDontStripVersionsFromReferencesAtPaths(DEFAULT_PRESERVE_VERSION_REFS); + + return retVal; + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4b/JpaR4BConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4b/JpaR4BConfig.java new file mode 100644 index 00000000000..4ccd56a15bd --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4b/JpaR4BConfig.java @@ -0,0 +1,96 @@ +package ca.uhn.fhir.jpa.config.r4b; + +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.IFhirSystemDao; +import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigR4B; +import ca.uhn.fhir.jpa.config.JpaConfig; +import ca.uhn.fhir.jpa.config.SharedConfigDstu3Plus; +import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter; +import ca.uhn.fhir.jpa.dao.r4b.TransactionProcessorVersionAdapterR4B; +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.term.TermLoaderSvcImpl; +import ca.uhn.fhir.jpa.term.TermVersionAdapterSvcR4B; +import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; +import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.Meta; +import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Lazy; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +@Configuration +@EnableTransactionManagement +@Import({ + FhirContextR4BConfig.class, + GeneratedDaoAndResourceProviderConfigR4B.class, + SharedConfigDstu3Plus.class, + JpaConfig.class +}) +public class JpaR4BConfig { + + @Bean + public ITermVersionAdapterSvc terminologyVersionAdapterSvc() { + return new TermVersionAdapterSvcR4B(); + } + + @Bean + public ITransactionProcessorVersionAdapter transactionProcessorVersionFacade() { + return new TransactionProcessorVersionAdapterR4B(); + } + + @Bean(name = JpaConfig.GRAPHQL_PROVIDER_NAME) + @Lazy + public GraphQLProvider graphQLProvider(FhirContext theFhirContext, IGraphQLStorageServices theGraphqlStorageServices, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry, IDaoRegistry theDaoRegistry) { + return new GraphQLProviderWithIntrospection(theFhirContext, theValidationSupport, theGraphqlStorageServices, theSearchParamRegistry, theDaoRegistry); + } + + @Bean(name = "mySystemDaoR4B") + public IFhirSystemDao systemDaoR4() { + ca.uhn.fhir.jpa.dao.r4b.FhirSystemDaoR4B retVal = new ca.uhn.fhir.jpa.dao.r4b.FhirSystemDaoR4B(); + return retVal; + } + + @Bean(name = "mySystemProviderR4B") + public JpaSystemProvider systemProviderR4(FhirContext theFhirContext) { + JpaSystemProvider retVal = new JpaSystemProvider<>(); + retVal.setContext(theFhirContext); + retVal.setDao(systemDaoR4()); + return retVal; + } + + @Bean + public ITermLoaderSvc termLoaderService(ITermDeferredStorageSvc theDeferredStorageSvc, ITermCodeSystemStorageSvc theCodeSystemStorageSvc) { + return new TermLoaderSvcImpl(theDeferredStorageSvc, theCodeSystemStorageSvc); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r5/JpaR5Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r5/JpaR5Config.java index 34e2ff07aeb..d2a316e19fd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r5/JpaR5Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r5/JpaR5Config.java @@ -11,13 +11,12 @@ import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter; import ca.uhn.fhir.jpa.dao.r5.TransactionProcessorVersionAdapterR5; 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.term.TermLoaderSvcImpl; -import ca.uhn.fhir.jpa.term.TermReadSvcR5; import ca.uhn.fhir.jpa.term.TermVersionAdapterSvcR5; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR5; import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import org.hl7.fhir.r5.model.Bundle; @@ -73,7 +72,7 @@ public class JpaR5Config { @Bean(name = JpaConfig.GRAPHQL_PROVIDER_NAME) @Lazy public GraphQLProvider graphQLProvider(FhirContext theFhirContext, IGraphQLStorageServices theGraphqlStorageServices, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry, IDaoRegistry theDaoRegistry) { - return new GraphQLProviderWithIntrospection(theFhirContext, theValidationSupport, theGraphqlStorageServices, theSearchParamRegistry, theDaoRegistry); + return new GraphQLProviderWithIntrospection(theFhirContext, theValidationSupport, theGraphqlStorageServices, theSearchParamRegistry, theDaoRegistry); } @Bean(name = "mySystemDaoR5") @@ -83,8 +82,8 @@ public class JpaR5Config { } @Bean(name = "mySystemProviderR5") - public ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5 systemProviderR5(FhirContext theFhirContext) { - ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5 retVal = new ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5(); + public JpaSystemProvider systemProviderR5(FhirContext theFhirContext) { + JpaSystemProvider retVal = new JpaSystemProvider<>(); retVal.setContext(theFhirContext); retVal.setDao(systemDaoR5()); return retVal; @@ -95,9 +94,4 @@ public class JpaR5Config { return new TermLoaderSvcImpl(theDeferredStorageSvc, theCodeSystemStorageSvc); } - @Bean - public ITermReadSvcR5 terminologyService() { - return new TermReadSvcR5(); - } - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoEncounterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoEncounterDstu2.java deleted file mode 100644 index f9b504e82db..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoEncounterDstu2.java +++ /dev/null @@ -1,68 +0,0 @@ -package ca.uhn.fhir.jpa.dao; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoEncounter; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu2.resource.Encounter; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.StringParam; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; - -import javax.servlet.http.HttpServletRequest; -import java.util.Collections; - -public class FhirResourceDaoEncounterDstu2 extends BaseHapiFhirResourceDaoimplements IFhirResourceDaoEncounter { - - @Override - public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { - SearchParameterMap paramMap = new SearchParameterMap(); - if (theCount != null) { - paramMap.setCount(theCount.getValue()); - } - if (theOffset != null) { - throw new IllegalArgumentException(Msg.code(944) + "Everything operation does not support offset searching"); - } - -// paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); - paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); - paramMap.setEverythingMode(theId != null ? EverythingModeEnum.ENCOUNTER_INSTANCE : EverythingModeEnum.ENCOUNTER_TYPE); - paramMap.setSort(theSort); - paramMap.setLastUpdated(theLastUpdated); - if (theId != null) { - paramMap.add("_id", new StringParam(theId.getIdPart())); - } - IBundleProvider retVal = search(paramMap); - return retVal; - } - - @Override - public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { - return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java deleted file mode 100644 index 09154a3d98a..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java +++ /dev/null @@ -1,105 +0,0 @@ -package ca.uhn.fhir.jpa.dao; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; -import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu2.resource.Patient; -import ca.uhn.fhir.rest.api.CacheControlDirective; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.StringAndListParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -import javax.servlet.http.HttpServletRequest; -import java.util.Collections; - -public class FhirResourceDaoPatientDstu2 extends BaseHapiFhirResourceDao implements IFhirResourceDaoPatient { - - @Autowired - private IRequestPartitionHelperSvc myPartitionHelperSvc; - - public FhirResourceDaoPatientDstu2() { - super(); - } - - private IBundleProvider doEverythingOperation(TokenOrListParam theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, StringAndListParam theTypes, RequestDetails theRequest) { - SearchParameterMap paramMap = new SearchParameterMap(); - if (theCount != null) { - paramMap.setCount(theCount.getValue()); - } - if (theOffset != null) { - throw new IllegalArgumentException(Msg.code(954) + "Everything operation does not support offset searching"); - } - if (theContent != null) { - paramMap.add(Constants.PARAM_CONTENT, theContent); - } - if (theNarrative != null) { - paramMap.add(Constants.PARAM_TEXT, theNarrative); - } - if (theTypes != null) { - paramMap.add(Constants.PARAM_TYPE, theTypes); - } - paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); - paramMap.setEverythingMode(theId != null ? EverythingModeEnum.PATIENT_INSTANCE : EverythingModeEnum.PATIENT_TYPE); - paramMap.setSort(theSort); - paramMap.setLastUpdated(theLastUpdated); - if (theId != null) { - paramMap.add("_id", theId); - } - - if (!isPagingProviderDatabaseBacked(theRequest)) { - paramMap.setLoadSynchronous(true); - } - - RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), paramMap, null); - return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName(), new CacheControlDirective().parse(theRequest.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequest, requestPartitionId); - } - - @Override - @Transactional(propagation = Propagation.SUPPORTS) - public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, RequestDetails theRequestDetails, PatientEverythingParameters theQueryParams, IIdType theId) { - TokenOrListParam id = new TokenOrListParam().add(new TokenParam(theId.getIdPart())); - return doEverythingOperation(id, theQueryParams.getCount(), theQueryParams.getOffset(), theQueryParams.getLastUpdated(), theQueryParams.getSort(), theQueryParams.getContent(), theQueryParams.getNarrative(), theQueryParams.getFilter(), theQueryParams.getTypes(), theRequestDetails); - } - - @Override - @Transactional(propagation = Propagation.SUPPORTS) - public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, RequestDetails theRequestDetails, PatientEverythingParameters theQueryParams, TokenOrListParam theIds) { - return doEverythingOperation(theIds, theQueryParams.getCount(), theQueryParams.getOffset(), theQueryParams.getLastUpdated(), theQueryParams.getSort(), theQueryParams.getContent(), theQueryParams.getNarrative(), theQueryParams.getFilter(), theQueryParams.getTypes(), theRequestDetails); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoQuestionnaireResponseDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoQuestionnaireResponseDstu2.java deleted file mode 100644 index 39da775d3bd..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoQuestionnaireResponseDstu2.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.uhn.fhir.jpa.dao; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse; - -public class FhirResourceDaoQuestionnaireResponseDstu2 extends BaseHapiFhirResourceDao { - // nothing -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoBundleDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoBundle.java similarity index 78% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoBundleDstu2.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoBundle.java index 2d5b9afe9c4..336a2efc72b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoBundleDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoBundle.java @@ -20,27 +20,29 @@ package ca.uhn.fhir.jpa.dao; * #L% */ +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; -import java.util.Set; - import static org.apache.commons.lang3.StringUtils.defaultString; -public class FhirResourceDaoBundleDstu2 extends BaseHapiFhirResourceDao { +public class JpaResourceDaoBundle extends BaseHapiFhirResourceDao { @Override protected void preProcessResourceForStorage(IBaseResource theResource, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails, boolean thePerformIndexing) { super.preProcessResourceForStorage(theResource, theRequestDetails, theTransactionDetails, thePerformIndexing); - for (Entry next : ((Bundle)theResource).getEntry()) { - next.setFullUrl((String) null); + if (getContext().getVersion().getVersion() == FhirVersionEnum.DSTU2) { + for (Entry next : ((Bundle) theResource).getEntry()) { + next.setFullUrl((String) null); + } } } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoConceptMapR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoConceptMap.java similarity index 83% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoConceptMapR4.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoConceptMap.java index a2ea81c5061..807790e7405 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoConceptMapR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoConceptMap.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.dao.r4; +package ca.uhn.fhir.jpa.dao; /* * #%L @@ -24,29 +24,25 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap; import ca.uhn.fhir.jpa.api.model.TranslationRequest; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseCoding; +import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ConceptMap; import org.springframework.beans.factory.annotation.Autowired; -import java.util.Collections; import java.util.Date; -import java.util.List; -public class FhirResourceDaoConceptMapR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoConceptMap { +public class JpaResourceDaoConceptMap extends JpaResourceDao implements IFhirResourceDaoConceptMap { @Autowired private ITermConceptMappingSvc myTermConceptMappingSvc; @Autowired private IValidationSupport myValidationSupport; + @Autowired + private VersionCanonicalizer myVersionCanonicalizer; @Override public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) { @@ -61,7 +57,7 @@ public class FhirResourceDaoConceptMapR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoEncounter { +public class JpaResourceDaoEncounter extends BaseHapiFhirResourceDao implements IFhirResourceDaoEncounter { @Override public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoPatientR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java similarity index 92% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoPatientR4.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java index 7952eb21e1e..36aa7c70fdd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoPatientR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.dao.r4; +package ca.uhn.fhir.jpa.dao; /* * #%L @@ -22,9 +22,8 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; +import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; @@ -34,10 +33,13 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r4.model.Patient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -46,7 +48,7 @@ import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Collections; -public class FhirResourceDaoPatientR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoPatient { +public class JpaResourceDaoPatient extends BaseHapiFhirResourceDao implements IFhirResourceDaoPatient { @Autowired private IRequestPartitionHelperSvc myPartitionHelperSvc; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoBundleDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoBundleDstu3.java deleted file mode 100644 index 1278567b591..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoBundleDstu3.java +++ /dev/null @@ -1,37 +0,0 @@ -package ca.uhn.fhir.jpa.dao.dstu3; - -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import org.hl7.fhir.dstu3.model.Bundle; -import org.hl7.fhir.dstu3.model.Bundle.BundleType; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; - -import java.util.Set; - -import static org.apache.commons.lang3.StringUtils.defaultString; - -public class FhirResourceDaoBundleDstu3 extends BaseHapiFhirResourceDao { - - // nothing - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 085deed6b0b..d043f831095 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -54,6 +54,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; import static org.apache.commons.lang3.StringUtils.isNotBlank; @Transactional @@ -172,7 +173,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCodeSystemUrl, IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { - throw new UnsupportedOperationException(Msg.code(1077)); + return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoEncounterDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoEncounterDstu3.java deleted file mode 100644 index dfcd6d93d55..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoEncounterDstu3.java +++ /dev/null @@ -1,69 +0,0 @@ -package ca.uhn.fhir.jpa.dao.dstu3; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoEncounter; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.StringParam; -import org.hl7.fhir.dstu3.model.Encounter; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; - -import javax.servlet.http.HttpServletRequest; -import java.util.Collections; - -public class FhirResourceDaoEncounterDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoEncounter { - - @Override - public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { - SearchParameterMap paramMap = new SearchParameterMap(); - if (theCount != null) { - paramMap.setCount(theCount.getValue()); - } - if (theOffset != null) { - throw new IllegalArgumentException(Msg.code(1081) + "Everything operation does not support offset searching"); - } - -// paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); - paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); - paramMap.setEverythingMode(theId != null ? EverythingModeEnum.ENCOUNTER_INSTANCE : EverythingModeEnum.ENCOUNTER_TYPE); - paramMap.setSort(theSort); - paramMap.setLastUpdated(theLastUpdated); - if (theId != null) { - paramMap.add("_id", new StringParam(theId.getIdPart())); - } - IBundleProvider retVal = search(paramMap); - return retVal; - } - - @Override - public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { - return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java deleted file mode 100644 index c41b7add5b2..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java +++ /dev/null @@ -1,104 +0,0 @@ -package ca.uhn.fhir.jpa.dao.dstu3; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.rest.api.CacheControlDirective; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.*; -import org.hl7.fhir.dstu3.model.Patient; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -import javax.servlet.http.HttpServletRequest; -import java.util.Arrays; -import java.util.Collections; - -public class FhirResourceDaoPatientDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoPatient { - - @Autowired - private IRequestPartitionHelperSvc myPartitionHelperSvc; - - private IBundleProvider doEverythingOperation(TokenOrListParam theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, StringAndListParam theTypes, RequestDetails theRequest) { - SearchParameterMap paramMap = new SearchParameterMap(); - if (theCount != null) { - paramMap.setCount(theCount.getValue()); - } - if (theOffset != null) { - throw new IllegalArgumentException(Msg.code(1082) + "Everything operation does not support offset searching"); - } - if (theContent != null) { - paramMap.add(Constants.PARAM_CONTENT, theContent); - } - if (theNarrative != null) { - paramMap.add(Constants.PARAM_TEXT, theNarrative); - } - if (theTypes != null) { - paramMap.add(Constants.PARAM_TYPE, theTypes); - } - paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); - paramMap.setEverythingMode(theId != null ? EverythingModeEnum.PATIENT_INSTANCE : EverythingModeEnum.PATIENT_TYPE); - paramMap.setSort(theSort); - paramMap.setLastUpdated(theLastUpdated); - if (theId != null) { - if (theRequest.getParameters().containsKey("_mdm")) { - String[] paramVal = theRequest.getParameters().get("_mdm"); - if (Arrays.asList(paramVal).contains("true")) { - theId.getValuesAsQueryTokens().stream().forEach(param -> param.setMdmExpand(true)); - } - } - paramMap.add("_id", theId); - } - - if (!isPagingProviderDatabaseBacked(theRequest)) { - paramMap.setLoadSynchronous(true); - } - - RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), paramMap, null); - return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName(), new CacheControlDirective().parse(theRequest.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequest, requestPartitionId); - } - - @Override - @Transactional(propagation = Propagation.SUPPORTS) - public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, RequestDetails theRequestDetails, PatientEverythingParameters theQueryParams, IIdType theId) { - TokenOrListParam id = new TokenOrListParam().addOr(new TokenParam(theId.getIdPart())); - return doEverythingOperation(id, theQueryParams.getCount(), theQueryParams.getOffset(), theQueryParams.getLastUpdated(), theQueryParams.getSort(), theQueryParams.getContent(), theQueryParams.getNarrative(), theQueryParams.getFilter(), theQueryParams.getTypes(), theRequestDetails); - } - - @Override - public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, RequestDetails theRequestDetails, PatientEverythingParameters theQueryParams, TokenOrListParam theId) { - return doEverythingOperation(theId, theQueryParams.getCount(), theQueryParams.getOffset(), theQueryParams.getLastUpdated(), theQueryParams.getSort(), theQueryParams.getContent(), theQueryParams.getNarrative(), theQueryParams.getFilter(), theQueryParams.getTypes(), theRequestDetails); - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoQuestionnaireResponseDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoQuestionnaireResponseDstu3.java deleted file mode 100644 index ccc45f6402d..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoQuestionnaireResponseDstu3.java +++ /dev/null @@ -1,87 +0,0 @@ -package ca.uhn.fhir.jpa.dao.dstu3; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import org.hl7.fhir.dstu3.model.QuestionnaireResponse; - -public class FhirResourceDaoQuestionnaireResponseDstu3 extends BaseHapiFhirResourceDao { - - -// @Override -// protected void validateResourceForStorage(QuestionnaireResponse theResource, ResourceTable theEntityToSave, RequestDetails theRequestDetails) { -// super.validateResourceForStorage(theResource, theEntityToSave, theRequestDetails); -// if (!myValidateResponses) { -// return; -// } -// -// if (theResource == null || theResource.getQuestionnaire() == null || theResource.getQuestionnaire().getReference() == null || theResource.getQuestionnaire().getReference().isEmpty()) { -// return; -// } -// -// FhirValidator val = getContext().newValidator(); -// val.setValidateAgainstStandardSchema(false); -// val.setValidateAgainstStandardSchematron(false); -// -// val.registerValidatorModule(myQuestionnaireResponseValidatorDstu3); -// -// ValidationResult result = val.validateWithResult(getContext().newJsonParser().parseResource(getContext().newJsonParser().encodeResourceToString(theResource))); -// if (!result.isSuccessful()) { -// IBaseOperationOutcome oo = getContext().newJsonParser().parseResource(OperationOutcome.class, getContext().newJsonParser().encodeResourceToString(result.toOperationOutcome())); -// throw new UnprocessableEntityException(Msg.code(1078) + getContext(), oo); -// } -// } -// -// public class ResourceLoaderImpl implements IResourceLoader { -// -// private RequestDetails myRequestDetails; -// -// public ResourceLoaderImpl(RequestDetails theRequestDetails) { -// super(); -// myRequestDetails = theRequestDetails; -// } -// -// @Override -// public T load(Class theType, IIdType theId) throws ResourceNotFoundException { -// -// /* -// * The QuestionnaireResponse validator uses RI structures, so for now we need to convert between that and HAPI -// * structures. This is a bit hackish, but hopefully it will go away at some point. -// */ -// if ("ValueSet".equals(theType.getSimpleName())) { -// IFhirResourceDao dao = getDao(ValueSet.class); -// ValueSet in = dao.read(theId, myRequestDetails); -// return (T) in; -// } else if ("Questionnaire".equals(theType.getSimpleName())) { -// IFhirResourceDao dao = getDao(Questionnaire.class); -// Questionnaire vs = dao.read(theId, myRequestDetails); -// return (T) vs; -// } else { -// // Should not happen, validator will only ask for these two -// throw new IllegalStateException(Msg.code(1079) + "Unexpected request to load resource of type " + theType); -// } -// -// } -// -// } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoBundleR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoBundleR4.java deleted file mode 100644 index 041a5d23880..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoBundleR4.java +++ /dev/null @@ -1,38 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r4; - -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Bundle.BundleType; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; - -import java.util.Set; -import java.util.TreeSet; - -import static org.apache.commons.lang3.StringUtils.defaultString; - -public class FhirResourceDaoBundleR4 extends BaseHapiFhirResourceDao { - - // nothing for now - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoEncounterR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoEncounterR4.java deleted file mode 100644 index 1981971f18a..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoEncounterR4.java +++ /dev/null @@ -1,69 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r4; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoEncounter; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.StringParam; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r4.model.Encounter; - -import javax.servlet.http.HttpServletRequest; -import java.util.Collections; - -public class FhirResourceDaoEncounterR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoEncounter { - - @Override - public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { - SearchParameterMap paramMap = new SearchParameterMap(); - if (theCount != null) { - paramMap.setCount(theCount.getValue()); - } - if (theOffset != null) { - throw new IllegalArgumentException(Msg.code(1107) + "Everything operation does not support offset searching"); - } - -// paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); - paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive())); - paramMap.setEverythingMode(theId != null ? EverythingModeEnum.ENCOUNTER_INSTANCE : EverythingModeEnum.ENCOUNTER_TYPE); - paramMap.setSort(theSort); - paramMap.setLastUpdated(theLastUpdated); - if (theId != null) { - paramMap.add("_id", new StringParam(theId.getIdPart())); - } - IBundleProvider retVal = search(paramMap); - return retVal; - } - - @Override - public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { - return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoQuestionnaireResponseR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoQuestionnaireResponseR4.java deleted file mode 100644 index 39b1034b506..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoQuestionnaireResponseR4.java +++ /dev/null @@ -1,87 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r4; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import org.hl7.fhir.r4.model.QuestionnaireResponse; - -public class FhirResourceDaoQuestionnaireResponseR4 extends BaseHapiFhirResourceDao { - - -// @Override -// protected void validateResourceForStorage(QuestionnaireResponse theResource, ResourceTable theEntityToSave, RequestDetails theRequestDetails) { -// super.validateResourceForStorage(theResource, theEntityToSave, theRequestDetails); -// if (!myValidateResponses) { -// return; -// } -// -// if (theResource == null || theResource.getQuestionnaire() == null || theResource.getQuestionnaire().getReference() == null || theResource.getQuestionnaire().getReference().isEmpty()) { -// return; -// } -// -// FhirValidator val = getContext().newValidator(); -// val.setValidateAgainstStandardSchema(false); -// val.setValidateAgainstStandardSchematron(false); -// -// val.registerValidatorModule(myQuestionnaireResponseValidatorR4); -// -// ValidationResult result = val.validateWithResult(getContext().newJsonParser().parseResource(getContext().newJsonParser().encodeResourceToString(theResource))); -// if (!result.isSuccessful()) { -// IBaseOperationOutcome oo = getContext().newJsonParser().parseResource(OperationOutcome.class, getContext().newJsonParser().encodeResourceToString(result.toOperationOutcome())); -// throw new UnprocessableEntityException(Msg.code(1104) + getContext(), oo); -// } -// } -// -// public class ResourceLoaderImpl implements IResourceLoader { -// -// private RequestDetails myRequestDetails; -// -// public ResourceLoaderImpl(RequestDetails theRequestDetails) { -// super(); -// myRequestDetails = theRequestDetails; -// } -// -// @Override -// public T load(Class theType, IIdType theId) throws ResourceNotFoundException { -// -// /* -// * The QuestionnaireResponse validator uses RI structures, so for now we need to convert between that and HAPI -// * structures. This is a bit hackish, but hopefully it will go away at some point. -// */ -// if ("ValueSet".equals(theType.getSimpleName())) { -// IFhirResourceDao dao = getDao(ValueSet.class); -// ValueSet in = dao.read(theId, myRequestDetails); -// return (T) in; -// } else if ("Questionnaire".equals(theType.getSimpleName())) { -// IFhirResourceDao dao = getDao(Questionnaire.class); -// Questionnaire vs = dao.read(theId, myRequestDetails); -// return (T) vs; -// } else { -// // Should not happen, validator will only ask for these two -// throw new IllegalStateException(Msg.code(1105) + "Unexpected request to load resource of type " + theType); -// } -// -// } -// -// } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java_1852830804362782 b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoCodeSystemR4B.java similarity index 68% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java_1852830804362782 rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoCodeSystemR4B.java index 740fd8b1492..65f8e968a51 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java_1852830804362782 +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoCodeSystemR4B.java @@ -1,10 +1,10 @@ -package ca.uhn.fhir.jpa.dao.r5; +package ca.uhn.fhir.jpa.dao.r4b; /* * #%L * HAPI FHIR JPA Server * %% - * Copyright (C) 2014 - 2021 Smile CDR, Inc. + * Copyright (C) 2014 - 2022 Smile CDR, Inc. * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,58 +22,61 @@ package ca.uhn.fhir.jpa.dao.r5; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; +import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; +import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; -import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; -import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.util.LogicUtil; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; +import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r5.model.CodeSystem; -import org.hl7.fhir.r5.model.CodeableConcept; -import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r4b.model.CodeSystem; +import org.hl7.fhir.r4b.model.CodeableConcept; +import org.hl7.fhir.r4b.model.Coding; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.Set; import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; -import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static org.apache.commons.lang3.StringUtils.isNotBlank; -public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoCodeSystem { +public class FhirResourceDaoCodeSystemR4B extends BaseHapiFhirResourceDao implements IFhirResourceDaoCodeSystem { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR5.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR4B.class); @Autowired protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc; @Autowired - protected IdHelperService myIdHelperService; + protected IIdHelperService myIdHelperService; + @Autowired + protected ITermDeferredStorageSvc myTermDeferredStorageSvc; @Autowired private IValidationSupport myValidationSupport; @Autowired private FhirContext myFhirContext; - @Autowired - protected ITermDeferredStorageSvc myTermDeferredStorageSvc; @Override public List findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) { List valueSetIds; - Set ids = searchForIds(new SearchParameterMap(CodeSystem.SP_CODE, new TokenParam(theSystem, theCode)), theRequest); + List ids = searchForIds(new SearchParameterMap(org.hl7.fhir.r4.model.CodeSystem.SP_CODE, new TokenParam(theSystem, theCode)), theRequest); valueSetIds = new ArrayList<>(); for (ResourcePersistentId next : ids) { IIdType id = myIdHelperService.translatePidIdToForcedId(myFhirContext, "CodeSystem", next); @@ -85,15 +88,22 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { + return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); + } + + @Nonnull + @Override + public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, IPrimitiveType theDisplayLanguage, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; + boolean haveDisplayLanguage = theDisplayLanguage != null && theDisplayLanguage.isEmpty() == false; if (!haveCoding && !(haveSystem && haveCode)) { - throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate"); + throw new InvalidRequestException(Msg.code(2148) + "No code, coding, or codeableConcept provided to validate"); } if (!LogicUtil.multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) { - throw new InvalidRequestException("$lookup can only validate (system AND code) OR (coding.system AND coding.code)"); + throw new InvalidRequestException(Msg.code(2149) + "$lookup can only validate (system AND code) OR (coding.system AND coding.code)"); } String code; @@ -110,12 +120,17 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCodeSystemUrl, - IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, - Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, + Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoCompositionR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoCompositionR4B.java new file mode 100644 index 00000000000..6fccd75ec9a --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoCompositionR4B.java @@ -0,0 +1,59 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition; +import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.StringParam; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4b.model.Composition; + +import javax.servlet.http.HttpServletRequest; +import java.util.Collections; + +public class FhirResourceDaoCompositionR4B extends BaseHapiFhirResourceDao implements IFhirResourceDaoComposition { + + @Override + public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) { + SearchParameterMap paramMap = new SearchParameterMap(); + if (theCount != null) { + paramMap.setCount(theCount.getValue()); + } + if (theOffset != null) { + paramMap.setOffset(theOffset.getValue()); + } + paramMap.setIncludes(Collections.singleton(IBaseResource.INCLUDE_ALL.asRecursive())); + paramMap.setSort(theSort); + paramMap.setLastUpdated(theLastUpdate); + if (theId != null) { + paramMap.add("_id", new StringParam(theId.getIdPart())); + } + return search(paramMap, theRequestDetails); + } +} + diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoConceptMapR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoConceptMapR4B.java new file mode 100644 index 00000000000..a026298677c --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoConceptMapR4B.java @@ -0,0 +1,45 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.TranslateConceptResults; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap; +import ca.uhn.fhir.jpa.api.model.TranslationRequest; +import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; +import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.hl7.fhir.r4b.model.ConceptMap; +import org.springframework.beans.factory.annotation.Autowired; + +public class FhirResourceDaoConceptMapR4B extends BaseHapiFhirResourceDao implements IFhirResourceDaoConceptMap { + @Autowired + private ITermConceptMappingSvc myTermConceptMappingSvc; + @Autowired + private IValidationSupport myValidationSupport; + + @Override + public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) { + IValidationSupport.TranslateCodeRequest translateCodeRequest = theTranslationRequest.asTranslateCodeRequest(); + return myValidationSupport.translateConcept(translateCodeRequest); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoBundleR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoMessageHeaderR4B.java similarity index 68% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoBundleR5.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoMessageHeaderR4B.java index 8c7fd734dd6..ed8d3b18187 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoBundleR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoMessageHeaderR4B.java @@ -1,12 +1,4 @@ -package ca.uhn.fhir.jpa.dao.r5; - -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import org.hl7.fhir.r5.model.Bundle; - -import java.util.Set; - -import static org.apache.commons.lang3.StringUtils.defaultString; +package ca.uhn.fhir.jpa.dao.r4b; /* * #%L @@ -28,8 +20,10 @@ import static org.apache.commons.lang3.StringUtils.defaultString; * #L% */ -public class FhirResourceDaoBundleR5 extends BaseHapiFhirResourceDao { - - // nothing +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoMessageHeader; +import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; +import org.hl7.fhir.r4b.model.MessageHeader; +public class FhirResourceDaoMessageHeaderR4B extends BaseHapiFhirResourceDao implements IFhirResourceDaoMessageHeader { + // nothing right now } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoObservationR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoObservationR4B.java new file mode 100644 index 00000000000..9ed236f9b63 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoObservationR4B.java @@ -0,0 +1,83 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDaoObservation; +import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.CacheControlDirective; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4b.model.Observation; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.http.HttpServletResponse; +import java.util.Date; + +public class FhirResourceDaoObservationR4B extends BaseHapiFhirResourceDaoObservation { + + @Autowired + private IRequestPartitionHelperSvc myPartitionHelperSvc; + + @Override + public IBundleProvider observationsLastN(SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) { + + updateSearchParamsForLastn(theSearchParameterMap, theRequestDetails); + + RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap, null); + return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails, requestPartitionId); + } + + @Override + protected String getEffectiveParamName() { + return org.hl7.fhir.r4.model.Observation.SP_DATE; + } + + @Override + protected String getCodeParamName() { + return org.hl7.fhir.r4.model.Observation.SP_CODE; + } + + @Override + protected String getSubjectParamName() { + return org.hl7.fhir.r4.model.Observation.SP_SUBJECT; + } + + @Override + protected String getPatientParamName() { + return org.hl7.fhir.r4.model.Observation.SP_PATIENT; + } + + @Override + public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, + boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + return updateObservationEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, + thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, + theCreateNewHistoryEntry); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoSearchParameterR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoSearchParameterR4B.java new file mode 100644 index 00000000000..b905f1d8738 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoSearchParameterR4B.java @@ -0,0 +1,83 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter; +import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; +import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; +import org.hl7.fhir.r4b.model.CodeType; +import org.hl7.fhir.r4b.model.SearchParameter; +import org.hl7.fhir.r5.model.Resource; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.stream.Collectors; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class FhirResourceDaoSearchParameterR4B extends BaseHapiFhirResourceDao implements IFhirResourceDaoSearchParameter { + + @Autowired + private ISearchParamExtractor mySearchParamExtractor; + + protected void refactorAffectedResources(SearchParameter theResource, RequestDetails theRequestDetails) { + Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null; + String expression = theResource != null ? theResource.getExpression() : null; + List base = theResource != null ? theResource.getBase().stream().map(CodeType::getCode).collect(Collectors.toList()) : null; + requestReindexForRelatedResources(reindex, base, theRequestDetails); + } + + + @Override + protected void postPersist(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) { + super.postPersist(theEntity, theResource, theRequestDetails); + refactorAffectedResources(theResource, theRequestDetails); + } + + @Override + protected void postUpdate(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) { + super.postUpdate(theEntity, theResource, theRequestDetails); + refactorAffectedResources(theResource, theRequestDetails); + } + + @Override + protected void preDelete(SearchParameter theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) { + super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails); + refactorAffectedResources(theResourceToDelete, theRequestDetails); + } + + @Override + protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) { + super.validateResourceForStorage(theResource, theEntityToSave); + + Resource resR5 = VersionConvertorFactory_43_50.convertResource(theResource, new BaseAdvisor_43_50(false)); + FhirResourceDaoSearchParameterR4.validateSearchParam( + (org.hl7.fhir.r4.model.SearchParameter) VersionConvertorFactory_40_50.convertResource(resR5, new BaseAdvisor_40_50(false)), + getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor); + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoStructureDefinitionR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoStructureDefinitionR4B.java new file mode 100644 index 00000000000..dc9e7e13e9b --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoStructureDefinitionR4B.java @@ -0,0 +1,43 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoStructureDefinition; +import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; +import org.apache.commons.lang3.Validate; +import org.hl7.fhir.r4b.model.StructureDefinition; +import org.springframework.beans.factory.annotation.Autowired; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class FhirResourceDaoStructureDefinitionR4B extends BaseHapiFhirResourceDao implements IFhirResourceDaoStructureDefinition { + + @Autowired + private IValidationSupport myValidationSupport; + + @Override + public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theName) { + StructureDefinition output = (StructureDefinition) myValidationSupport.generateSnapshot(new ValidationSupportContext(myValidationSupport), theInput, theUrl, theWebUrl, theName); + Validate.notNull(output); + return output; + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoSubscriptionR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoSubscriptionR4B.java new file mode 100644 index 00000000000..5c1d23cf912 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoSubscriptionR4B.java @@ -0,0 +1,38 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSubscription; +import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4b.model.Subscription; + +public class FhirResourceDaoSubscriptionR4B extends BaseHapiFhirResourceDao implements IFhirResourceDaoSubscription { + + @Override + public Long getSubscriptionTablePidForSubscriptionResource(IIdType theId, RequestDetails theRequest, TransactionDetails theTransactionDetails) { + throw new UnsupportedOperationException(Msg.code(2150)); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoValueSetR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoValueSetR4B.java new file mode 100644 index 00000000000..f06c030019f --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirResourceDaoValueSetR4B.java @@ -0,0 +1,102 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.ValueSetExpansionOptions; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; +import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; +import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4b.model.CodeableConcept; +import org.hl7.fhir.r4b.model.Coding; +import org.hl7.fhir.r4b.model.ValueSet; + +import java.util.Date; + +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; + +public class FhirResourceDaoValueSetR4B extends BaseHapiFhirResourceDao implements IFhirResourceDaoValueSet { + + @Override + public ValueSet expand(IIdType theId, ValueSetExpansionOptions theOptions, RequestDetails theRequestDetails) { + ValueSet source = read(theId, theRequestDetails); + return expand(source, theOptions); + } + + @Override + public ValueSet expandByIdentifier(String theUri, ValueSetExpansionOptions theOptions) { + org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(theOptions, theUri); + org.hl7.fhir.r5.model.ValueSet valueSetR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(canonicalOutput, new BaseAdvisor_40_50(false)); + return (ValueSet) VersionConvertorFactory_43_50.convertResource(valueSetR5, new BaseAdvisor_43_50(false)); + } + + @Override + public ValueSet expand(ValueSet theSource, ValueSetExpansionOptions theOptions) { + org.hl7.fhir.r5.model.ValueSet canonicalInputR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_43_50.convertResource(theSource, new BaseAdvisor_43_50(false)); + org.hl7.fhir.r4.model.ValueSet canonicalInput = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(canonicalInputR5, new BaseAdvisor_40_50(false)); + org.hl7.fhir.r4.model.ValueSet canonicalOutput = myTerminologySvc.expandValueSet(theOptions, canonicalInput); + org.hl7.fhir.r5.model.ValueSet outputR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(canonicalOutput, new BaseAdvisor_40_50(false)); + return (ValueSet) VersionConvertorFactory_43_50.convertResource(outputR5, new BaseAdvisor_43_50(false)); + } + + @Override + public IValidationSupport.CodeValidationResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, + IPrimitiveType theSystem, IPrimitiveType theDisplay, Coding theCoding, + CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + return myTerminologySvc.validateCode(vsValidateCodeOptions(), theId, toStringOrNull(theValueSetIdentifier), toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + } + + @Override + public void purgeCaches() { + // nothing + } + + @Override + public ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, + boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry); + + if (getConfig().isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) { + if (retVal.getDeleted() == null) { + ValueSet valueSet = (ValueSet) theResource; + org.hl7.fhir.r5.model.ValueSet valueSetR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_43_50.convertResource(valueSet, new BaseAdvisor_43_50(false)); + org.hl7.fhir.r4.model.ValueSet valueSetR4 = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSetR5, new BaseAdvisor_40_50(false)); + myTerminologySvc.storeTermValueSet(retVal, valueSetR4); + } else { + myTerminologySvc.deleteValueSetAndChildren(retVal); + } + } + + return retVal; + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java new file mode 100644 index 00000000000..9e95a71d852 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java @@ -0,0 +1,73 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; +import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2; +import ca.uhn.fhir.jpa.model.entity.TagDefinition; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.Meta; + +import javax.persistence.TypedQuery; +import java.util.Collection; +import java.util.List; + +public class FhirSystemDaoR4B extends BaseHapiFhirSystemDao { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoR4B.class); + + @Override + public Meta metaGetOperation(RequestDetails theRequestDetails) { + String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)"; + TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class); + List tagDefinitions = q.getResultList(); + + return toMeta(tagDefinitions); + } + + @Override + public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) { + return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented(); + } + + + protected Meta toMeta(Collection tagDefinitions) { + Meta retVal = new Meta(); + for (TagDefinition next : tagDefinitions) { + switch (next.getTagType()) { + case PROFILE: + retVal.addProfile(next.getCode()); + break; + case SECURITY_LABEL: + retVal.addSecurity().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); + break; + case TAG: + retVal.addTag().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); + break; + } + } + return retVal; + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/TransactionProcessorVersionAdapterR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/TransactionProcessorVersionAdapterR4B.java new file mode 100644 index 00000000000..e2143929082 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/TransactionProcessorVersionAdapterR4B.java @@ -0,0 +1,174 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.OperationOutcome; +import org.hl7.fhir.r4b.model.Resource; + +import java.util.Date; +import java.util.List; + +public class TransactionProcessorVersionAdapterR4B implements ITransactionProcessorVersionAdapter { + @Override + public void setResponseStatus(Bundle.BundleEntryComponent theBundleEntry, String theStatus) { + theBundleEntry.getResponse().setStatus(theStatus); + } + + @Override + public void setResponseLastModified(Bundle.BundleEntryComponent theBundleEntry, Date theLastModified) { + theBundleEntry.getResponse().setLastModified(theLastModified); + } + + @Override + public void setResource(Bundle.BundleEntryComponent theBundleEntry, IBaseResource theResource) { + theBundleEntry.setResource((Resource) theResource); + } + + @Override + public IBaseResource getResource(Bundle.BundleEntryComponent theBundleEntry) { + return theBundleEntry.getResource(); + } + + @Override + public String getBundleType(Bundle theRequest) { + if (theRequest.getType() == null) { + return null; + } + return theRequest.getTypeElement().getValue().toCode(); + } + + @Override + public void populateEntryWithOperationOutcome(BaseServerResponseException theCaughtEx, Bundle.BundleEntryComponent theEntry) { + OperationOutcome oo = new OperationOutcome(); + oo.addIssue() + .setSeverity(OperationOutcome.IssueSeverity.ERROR) + .setDiagnostics(theCaughtEx.getMessage()) + .setCode(OperationOutcome.IssueType.EXCEPTION); + theEntry.getResponse().setOutcome(oo); + } + + @Override + public Bundle createBundle(String theBundleType) { + Bundle resp = new Bundle(); + try { + resp.setType(Bundle.BundleType.fromCode(theBundleType)); + } catch (FHIRException theE) { + throw new InternalErrorException(Msg.code(2151) + "Unknown bundle type: " + theBundleType); + } + return resp; + } + + @Override + public List getEntries(Bundle theRequest) { + return theRequest.getEntry(); + } + + @Override + public void addEntry(Bundle theBundle, Bundle.BundleEntryComponent theEntry) { + theBundle.addEntry(theEntry); + } + + @Override + public Bundle.BundleEntryComponent addEntry(Bundle theBundle) { + return theBundle.addEntry(); + } + + @Override + public String getEntryRequestVerb(FhirContext theContext, Bundle.BundleEntryComponent theEntry) { + String retVal = null; + Bundle.HTTPVerb value = theEntry.getRequest().getMethodElement().getValue(); + if (value != null) { + retVal = value.toCode(); + } + return retVal; + } + + @Override + public String getFullUrl(Bundle.BundleEntryComponent theEntry) { + return theEntry.getFullUrl(); + } + + + @Override + public void setFullUrl(Bundle.BundleEntryComponent theEntry, String theFullUrl) { + theEntry.setFullUrl(theFullUrl); + } + + @Override + public String getEntryIfNoneExist(Bundle.BundleEntryComponent theEntry) { + return theEntry.getRequest().getIfNoneExist(); + } + + @Override + public String getEntryRequestUrl(Bundle.BundleEntryComponent theEntry) { + return theEntry.getRequest().getUrl(); + } + + @Override + public void setResponseLocation(Bundle.BundleEntryComponent theEntry, String theResponseLocation) { + theEntry.getResponse().setLocation(theResponseLocation); + } + + @Override + public void setResponseETag(Bundle.BundleEntryComponent theEntry, String theEtag) { + theEntry.getResponse().setEtag(theEtag); + } + + @Override + public String getEntryRequestIfMatch(Bundle.BundleEntryComponent theEntry) { + return theEntry.getRequest().getIfMatch(); + } + + @Override + public String getEntryRequestIfNoneExist(Bundle.BundleEntryComponent theEntry) { + return theEntry.getRequest().getIfNoneExist(); + } + + @Override + public String getEntryRequestIfNoneMatch(Bundle.BundleEntryComponent theEntry) { + return theEntry.getRequest().getIfNoneMatch(); + } + + @Override + public void setResponseOutcome(Bundle.BundleEntryComponent theEntry, IBaseOperationOutcome theOperationOutcome) { + theEntry.getResponse().setOutcome((Resource) theOperationOutcome); + } + + @Override + public void setRequestVerb(Bundle.BundleEntryComponent theEntry, String theVerb) { + theEntry.getRequest().setMethod(Bundle.HTTPVerb.fromCode(theVerb)); + } + + @Override + public void setRequestUrl(Bundle.BundleEntryComponent theEntry, String theUrl) { + theEntry.getRequest().setUrl(theUrl); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java deleted file mode 100644 index 0910544209c..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java +++ /dev/null @@ -1,101 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r5; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; -import ca.uhn.fhir.rest.api.CacheControlDirective; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.*; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r5.model.Patient; -import org.springframework.beans.factory.annotation.Autowired; - -import javax.servlet.http.HttpServletRequest; -import java.util.Arrays; -import java.util.Collections; - -public class FhirResourceDaoPatientR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoPatient { - - @Autowired - private IRequestPartitionHelperSvc myPartitionHelperSvc; - - private IBundleProvider doEverythingOperation(TokenOrListParam theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theTypes, RequestDetails theRequest) { - SearchParameterMap paramMap = new SearchParameterMap(); - if (theCount != null) { - paramMap.setCount(theCount.getValue()); - } - if (theOffset != null) { - throw new IllegalArgumentException(Msg.code(1122) + "Everything operation does not support offset searching"); - } - if (theContent != null) { - paramMap.add(Constants.PARAM_CONTENT, theContent); - } - if (theNarrative != null) { - paramMap.add(Constants.PARAM_TEXT, theNarrative); - } - if (theTypes != null) { - paramMap.add(Constants.PARAM_TYPE, theTypes); - } - paramMap.setIncludes(Collections.singleton(IBaseResource.INCLUDE_ALL.asRecursive())); - paramMap.setEverythingMode(theId != null ? EverythingModeEnum.PATIENT_INSTANCE : EverythingModeEnum.PATIENT_TYPE); - paramMap.setSort(theSort); - paramMap.setLastUpdated(theLastUpdated); - if (theId != null) { - if (theRequest.getParameters().containsKey("_mdm")) { - String[] paramVal = theRequest.getParameters().get("_mdm"); - if (Arrays.asList(paramVal).contains("true")) { - theId.getValuesAsQueryTokens().stream().forEach(param -> param.setMdmExpand(true)); - } - } - paramMap.add("_id", theId); - } - - if (!isPagingProviderDatabaseBacked(theRequest)) { - paramMap.setLoadSynchronous(true); - } - - RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequest, getResourceName(), paramMap, null); - return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName(), new CacheControlDirective().parse(theRequest.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequest, requestPartitionId); - } - - @Override - public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, RequestDetails theRequestDetails, PatientEverythingParameters theQueryParams, IIdType theId) { - TokenOrListParam id = new TokenOrListParam().add(new TokenParam(theId.getIdPart())); - return doEverythingOperation(id, theQueryParams.getCount(), theQueryParams.getOffset(), theQueryParams.getLastUpdated(), theQueryParams.getSort(), theQueryParams.getContent(), theQueryParams.getNarrative(), theQueryParams.getTypes(), theRequestDetails); - } - - @Override - public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, RequestDetails theRequestDetails, PatientEverythingParameters theQueryParams, TokenOrListParam theId) { - return doEverythingOperation(theId, theQueryParams.getCount(), theQueryParams.getOffset(), theQueryParams.getLastUpdated(), theQueryParams.getSort(), theQueryParams.getContent(), theQueryParams.getNarrative(), theQueryParams.getTypes(), theRequestDetails); - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoQuestionnaireResponseR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoQuestionnaireResponseR5.java deleted file mode 100644 index 5a655e4e928..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoQuestionnaireResponseR5.java +++ /dev/null @@ -1,87 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r5; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import org.hl7.fhir.r5.model.QuestionnaireResponse; - -public class FhirResourceDaoQuestionnaireResponseR5 extends BaseHapiFhirResourceDao { - - -// @Override -// protected void validateResourceForStorage(QuestionnaireResponse theResource, ResourceTable theEntityToSave, RequestDetails theRequestDetails) { -// super.validateResourceForStorage(theResource, theEntityToSave, theRequestDetails); -// if (!myValidateResponses) { -// return; -// } -// -// if (theResource == null || theResource.getQuestionnaire() == null || theResource.getQuestionnaire().getReference() == null || theResource.getQuestionnaire().getReference().isEmpty()) { -// return; -// } -// -// FhirValidator val = getContext().newValidator(); -// val.setValidateAgainstStandardSchema(false); -// val.setValidateAgainstStandardSchematron(false); -// -// val.registerValidatorModule(myQuestionnaireResponseValidatorR5); -// -// ValidationResult result = val.validateWithResult(getContext().newJsonParser().parseResource(getContext().newJsonParser().encodeResourceToString(theResource))); -// if (!result.isSuccessful()) { -// IBaseOperationOutcome oo = getContext().newJsonParser().parseResource(OperationOutcome.class, getContext().newJsonParser().encodeResourceToString(result.toOperationOutcome())); -// throw new UnprocessableEntityException(Msg.code(1123) + getContext(), oo); -// } -// } -// -// public class ResourceLoaderImpl implements IResourceLoader { -// -// private RequestDetails myRequestDetails; -// -// public ResourceLoaderImpl(RequestDetails theRequestDetails) { -// super(); -// myRequestDetails = theRequestDetails; -// } -// -// @Override -// public T load(Class theType, IIdType theId) throws ResourceNotFoundException { -// -// /* -// * The QuestionnaireResponse validator uses RI structures, so for now we need to convert between that and HAPI -// * structures. This is a bit hackish, but hopefully it will go away at some point. -// */ -// if ("ValueSet".equals(theType.getSimpleName())) { -// IFhirResourceDao dao = getDao(ValueSet.class); -// ValueSet in = dao.read(theId, myRequestDetails); -// return (T) in; -// } else if ("Questionnaire".equals(theType.getSimpleName())) { -// IFhirResourceDao dao = getDao(Questionnaire.class); -// Questionnaire vs = dao.read(theId, myRequestDetails); -// return (T) vs; -// } else { -// // Should not happen, validator will only ask for these two -// throw new IllegalStateException(Msg.code(1124) + "Unexpected request to load resource of type " + theType); -// } -// -// } -// -// } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvc.java index 9309f2a42d5..47fd70c0c83 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvc.java @@ -43,6 +43,7 @@ import org.springframework.retry.backoff.FixedBackOffPolicy; import org.springframework.retry.policy.SimpleRetryPolicy; import org.springframework.retry.support.RetryTemplate; import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.support.TransactionTemplate; import java.util.Collections; @@ -54,21 +55,29 @@ import java.util.List; * Specifically, this class spawns an inner transaction for each {@link DeleteConflictList}. This class is meant to handle any potential delete collisions (ex {@link ResourceGoneException} or {@link ResourceVersionConflictException}. In the former case, we swallow the Exception in the inner transaction then continue. In the latter case, we retry according to the RETRY_BACKOFF_PERIOD and RETRY_MAX_ATTEMPTS before giving up. */ public class ThreadSafeResourceDeleterSvc { + + private static final String REQ_DET_KEY_IN_NEW_TRANSACTION = ThreadSafeResourceDeleterSvc.class.getName() + "REQ_DET_KEY_IN_NEW_TRANSACTION"; + public static final long RETRY_BACKOFF_PERIOD = 100L; public static final int RETRY_MAX_ATTEMPTS = 4; private static final Logger ourLog = LoggerFactory.getLogger(ThreadSafeResourceDeleterSvc.class); private final DaoRegistry myDaoRegistry; private final IInterceptorBroadcaster myInterceptorBroadcaster; - private final TransactionTemplate myTxTemplate; + private final TransactionTemplate myTxTemplateRequired; + private final TransactionTemplate myTxTemplateRequiresNew; private final RetryTemplate myRetryTemplate = getRetryTemplate(); public ThreadSafeResourceDeleterSvc(DaoRegistry theDaoRegistry, IInterceptorBroadcaster theInterceptorBroadcaster, PlatformTransactionManager thePlatformTransactionManager) { myDaoRegistry = theDaoRegistry; myInterceptorBroadcaster = theInterceptorBroadcaster; - myTxTemplate = new TransactionTemplate(thePlatformTransactionManager); - myTxTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); + + myTxTemplateRequired = new TransactionTemplate(thePlatformTransactionManager); + myTxTemplateRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + + myTxTemplateRequiresNew = new TransactionTemplate(thePlatformTransactionManager); + myTxTemplateRequiresNew.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); } /** @@ -99,15 +108,36 @@ public class ThreadSafeResourceDeleterSvc { // We will retry deletes on any occurrence of ResourceVersionConflictException up to RETRY_MAX_ATTEMPTS return myRetryTemplate.execute(retryContext -> { + + String previousNewTransactionValue = null; + if (theRequest != null) { + previousNewTransactionValue = (String) theRequest.getUserData().get(REQ_DET_KEY_IN_NEW_TRANSACTION); + } + try { if (retryContext.getRetryCount() > 0) { ourLog.info("Retrying delete of {} - Attempt #{}", nextSourceId, retryContext.getRetryCount()); } - myTxTemplate.execute(s -> doDelete(theRequest, theConflictList, theTransactionDetails, nextSource, dao)); + + // Avoid nesting multiple new transactions deep. This can easily cause + // thread pools to get exhausted. + if (theRequest == null || previousNewTransactionValue != null) { + myTxTemplateRequired.execute(s -> doDelete(theRequest, theConflictList, theTransactionDetails, nextSource, dao)); + } else { + theRequest.getUserData().put(REQ_DET_KEY_IN_NEW_TRANSACTION, REQ_DET_KEY_IN_NEW_TRANSACTION); + myTxTemplateRequiresNew.execute(s -> doDelete(theRequest, theConflictList, theTransactionDetails, nextSource, dao)); + } + return 1; } catch (ResourceGoneException exception) { ourLog.info("{} is already deleted. Skipping cascade delete of this resource", nextSourceId); + } finally { + if (theRequest != null) { + theRequest.getUserData().put(REQ_DET_KEY_IN_NEW_TRANSACTION, previousNewTransactionValue); + } + } + return 0; }); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java new file mode 100644 index 00000000000..c336c8a040e --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java @@ -0,0 +1,179 @@ +package ca.uhn.fhir.jpa.provider; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.support.ConceptValidationOptions; +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseCoding; +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.instance.model.api.IPrimitiveType; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public abstract class BaseJpaResourceProviderCodeSystem extends BaseJpaResourceProvider { + + @Autowired + private JpaValidationSupportChain myValidationSupportChain; + + /** + * $lookup operation + */ + @SuppressWarnings("unchecked") + @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters = { + @OperationParam(name = "name", typeName = "string", min = 1), + @OperationParam(name = "version", typeName = "string", min = 0), + @OperationParam(name = "display", typeName = "string", min = 1), + @OperationParam(name = "abstract", typeName = "boolean", min = 1), + }) + public IBaseParameters lookup( + HttpServletRequest theServletRequest, + @OperationParam(name = "code", min = 0, max = 1, typeName = "code") IPrimitiveType theCode, + @OperationParam(name = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType theSystem, + @OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding, + @OperationParam(name = "version", min = 0, max = 1, typeName = "string") IPrimitiveType theVersion, + @OperationParam(name = "displayLanguage", min = 0, max = 1, typeName = "code") IPrimitiveType theDisplayLanguage, + @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code") List> theProperties, + RequestDetails theRequestDetails + ) { + + startRequest(theServletRequest); + try { + IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); + IValidationSupport.LookupCodeResult result; + applyVersionToSystem(theSystem, theVersion); + result = dao.lookupCode(theCode, theSystem, theCoding, theDisplayLanguage, theRequestDetails); + result.throwNotFoundIfAppropriate(); + return result.toParameters(theRequestDetails.getFhirContext(), theProperties); + } finally { + endRequest(theServletRequest); + } + } + + + /** + * $subsumes operation + */ + @Operation(name = JpaConstants.OPERATION_SUBSUMES, idempotent = true, returnParameters = { + @OperationParam(name = "outcome", typeName = "code", min = 1), + }) + public IBaseParameters subsumes( + HttpServletRequest theServletRequest, + @OperationParam(name = "codeA", min = 0, max = 1, typeName = "code") IPrimitiveType theCodeA, + @OperationParam(name = "codeB", min = 0, max = 1, typeName = "code") IPrimitiveType theCodeB, + @OperationParam(name = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType theSystem, + @OperationParam(name = "codingA", min = 0, max = 1, typeName = "Coding") IBaseCoding theCodingA, + @OperationParam(name = "codingB", min = 0, max = 1, typeName = "Coding") IBaseCoding theCodingB, + @OperationParam(name = "version", min = 0, max = 1, typeName = "string") IPrimitiveType theVersion, + RequestDetails theRequestDetails + ) { + + startRequest(theServletRequest); + try { + IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); + IFhirResourceDaoCodeSystem.SubsumesResult result; + applyVersionToSystem(theSystem, theVersion); + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + return result.toParameters(theRequestDetails.getFhirContext()); + } finally { + endRequest(theServletRequest); + } + } + + private static void applyVersionToSystem(IPrimitiveType theSystem, IPrimitiveType theVersion) { + if (theVersion != null && isNotBlank(theVersion.getValueAsString()) && theSystem != null) { + theSystem.setValue(theSystem.getValueAsString() + "|" + theVersion.getValueAsString()); + } + } + + /** + * $validate-code operation + */ + @SuppressWarnings("unchecked") + @Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = { + @OperationParam(name = "result", typeName = "boolean", min = 1), + @OperationParam(name = "message", typeName = "string"), + @OperationParam(name = "display", typeName = "string") + }) + public IBaseParameters validateCode( + HttpServletRequest theServletRequest, + @IdParam(optional = true) IIdType theId, + @OperationParam(name = "url", min = 0, max = 1, typeName = "uri") IPrimitiveType theCodeSystemUrl, + @OperationParam(name = "version", min = 0, max = 1, typeName = "string") IPrimitiveType theVersion, + @OperationParam(name = "code", min = 0, max = 1, typeName = "code") IPrimitiveType theCode, + @OperationParam(name = "display", min = 0, max = 1, typeName = "string") IPrimitiveType theDisplay, + @OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding, + @OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept") IBase theCodeableConcept, + RequestDetails theRequestDetails + ) { + + IValidationSupport.CodeValidationResult result = null; + startRequest(theServletRequest); + try { + // TODO: JA why not just always just the chain here? and we can then get rid of the corresponding DAO method entirely + // If a Remote Terminology Server has been configured, use it + if (myValidationSupportChain.isRemoteTerminologyServiceConfigured()) { + String codeSystemUrl = (theCodeSystemUrl != null && theCodeSystemUrl.hasValue()) ? + theCodeSystemUrl.getValueAsString() : null; + + if (theCoding != null) { + if (isNotBlank(theCoding.getSystem())) { + if (codeSystemUrl != null && !codeSystemUrl.equalsIgnoreCase(theCoding.getSystem())) { + throw new InvalidRequestException(Msg.code(1160) + "Coding.system '" + theCoding.getSystem() + "' does not equal param url '" + theCodeSystemUrl + "'. Unable to validate-code."); + } + codeSystemUrl = theCoding.getSystem(); + String code = theCoding.getCode(); + String display = theCoding.getDisplay(); + + result = myValidationSupportChain.validateCode( + new ValidationSupportContext(myValidationSupportChain), new ConceptValidationOptions(), + codeSystemUrl, code, display, null); + } + } + } else { + // Otherwise, use the local DAO layer to validate the code + IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); + result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails); + } + return BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result); + } finally { + endRequest(theServletRequest); + } + + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCompositionR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java similarity index 59% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCompositionR5.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java index 5905bf9cf67..89cd69e209a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCompositionR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.provider.r5; +package ca.uhn.fhir.jpa.provider; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition; import ca.uhn.fhir.jpa.model.util.JpaConstants; @@ -13,9 +13,10 @@ import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.DateRangeParam; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r5.model.Composition; -import org.hl7.fhir.r5.model.IdType; -import org.hl7.fhir.r5.model.UnsignedIntType; /* @@ -38,36 +39,36 @@ import org.hl7.fhir.r5.model.UnsignedIntType; * #L% */ -public abstract class BaseJpaResourceProviderCompositionR5 extends JpaResourceProviderR5 { +public abstract class BaseJpaResourceProviderComposition extends BaseJpaResourceProvider { /** * Composition/123/$document */ - @Operation(name = JpaConstants.OPERATION_DOCUMENT, idempotent = true, bundleType=BundleTypeEnum.DOCUMENT) + @Operation(name = JpaConstants.OPERATION_DOCUMENT, idempotent = true, bundleType = BundleTypeEnum.DOCUMENT) public IBundleProvider getDocumentForComposition( - javax.servlet.http.HttpServletRequest theServletRequest, + javax.servlet.http.HttpServletRequest theServletRequest, - @IdParam - IdType theId, + @IdParam + IIdType theId, - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, + @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") + @OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt") + IPrimitiveType theCount, - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, + @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") + @OperationParam(name = Constants.PARAM_OFFSET, typeName = "unsignedInt") + IPrimitiveType theOffset, - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, + @Description(shortDefinition = "Only return resources which were last updated as specified by the given range") + @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) + DateRangeParam theLastUpdated, - @Sort - SortSpec theSortSpec, + @Sort + SortSpec theSortSpec, - RequestDetails theRequestDetails - ) { + RequestDetails theRequestDetails + ) { startRequest(theServletRequest); try { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCompositionDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCompositionDstu2.java deleted file mode 100644 index a9817e39975..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCompositionDstu2.java +++ /dev/null @@ -1,49 +0,0 @@ -package ca.uhn.fhir.jpa.provider; - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.dstu2.resource.Composition; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.api.server.IBundleProvider; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderCompositionDstu2 extends JpaResourceProviderDstu2 { - - /** - * Composition/123/$document - */ - //@formatter:off - @Operation(name = JpaConstants.OPERATION_DOCUMENT, idempotent = true, bundleType=BundleTypeEnum.DOCUMENT) - public IBundleProvider getDocumentForComposition( - - javax.servlet.http.HttpServletRequest theServletRequest) { - //@formatter:on - - startRequest(theServletRequest); - try { - return ((IFhirResourceDaoComposition)getDao()).getDocumentForComposition(theServletRequest, null, null, null, null, null, null); - } finally { - endRequest(theServletRequest); - } - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounter.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounter.java new file mode 100644 index 00000000000..0a092163629 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounter.java @@ -0,0 +1,109 @@ +package ca.uhn.fhir.jpa.provider; + +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoEncounter; +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.model.valueset.BundleTypeEnum; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.DateRangeParam; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public abstract class BaseJpaResourceProviderEncounter extends BaseJpaResourceProvider { + + /** + * Encounter/123/$everything + */ + @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) + public IBundleProvider EncounterInstanceEverything( + + javax.servlet.http.HttpServletRequest theServletRequest, + + @IdParam + IIdType theId, + + @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") + @OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt") + IPrimitiveType theCount, + + @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") + @OperationParam(name = Constants.PARAM_OFFSET, typeName = "unsignedInt") + IPrimitiveType theOffset, + + @Description(shortDefinition = "Only return resources which were last updated as specified by the given range") + @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) + DateRangeParam theLastUpdated, + + @Sort + SortSpec theSortSpec + ) { + + startRequest(theServletRequest); + try { + return ((IFhirResourceDaoEncounter) getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec); + } finally { + endRequest(theServletRequest); + } + } + + /** + * /Encounter/$everything + */ + @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) + public IBundleProvider EncounterTypeEverything( + + javax.servlet.http.HttpServletRequest theServletRequest, + + @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") + @OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt") + IPrimitiveType theCount, + + @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") + @OperationParam(name = Constants.PARAM_OFFSET, typeName = "unsignedInt") + IPrimitiveType theOffset, + + @Description(shortDefinition = "Only return resources which were last updated as specified by the given range") + @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) + DateRangeParam theLastUpdated, + + @Sort + SortSpec theSortSpec + ) { + + startRequest(theServletRequest); + try { + return ((IFhirResourceDaoEncounter) getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec); + } finally { + endRequest(theServletRequest); + } + + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderMessageHeaderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderMessageHeaderDstu2.java deleted file mode 100644 index 5beba18c129..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderMessageHeaderDstu2.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.uhn.fhir.jpa.provider; - -import ca.uhn.fhir.model.dstu2.resource.MessageHeader; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderMessageHeaderDstu2 extends JpaResourceProviderDstu2 { - // nothing now -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java similarity index 89% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java index 59aa188db57..3da80fe2ea8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.provider.r5; +package ca.uhn.fhir.jpa.provider; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation; import ca.uhn.fhir.jpa.model.util.JpaConstants; @@ -13,9 +13,8 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.DateAndListParam; import ca.uhn.fhir.rest.param.ReferenceAndListParam; import ca.uhn.fhir.rest.param.TokenAndListParam; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r5.model.Observation; -import org.hl7.fhir.r5.model.UnsignedIntType; import java.util.List; import java.util.Map; @@ -40,7 +39,7 @@ import java.util.Map; * #L% */ -public abstract class BaseJpaResourceProviderObservationR5 extends JpaResourceProviderR5 { +public abstract class BaseJpaResourceProviderObservation extends BaseJpaResourceProvider { /** * Observation/$lastn @@ -54,8 +53,8 @@ public abstract class BaseJpaResourceProviderObservationR5 extends JpaResourcePr ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails, @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, + @OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt") + IPrimitiveType theCount, @Description(shortDefinition="The classification of the type of observation") @OperationParam(name="category") @@ -105,7 +104,7 @@ public abstract class BaseJpaResourceProviderObservationR5 extends JpaResourcePr getDao().translateRawParameters(theAdditionalRawParams, paramMap); - return ((IFhirResourceDaoObservation) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); + return ((IFhirResourceDaoObservation) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderPatientR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatient.java similarity index 70% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderPatientR5.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatient.java index c8516afe925..c72361ed1b9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderPatientR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatient.java @@ -1,9 +1,10 @@ -package ca.uhn.fhir.jpa.provider.r5; +package ca.uhn.fhir.jpa.provider; -import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; +import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; @@ -19,10 +20,9 @@ import ca.uhn.fhir.rest.param.StringOrListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; -import org.hl7.fhir.r5.model.IdType; -import org.hl7.fhir.r5.model.Patient; -import org.hl7.fhir.r5.model.StringType; -import org.hl7.fhir.r5.model.UnsignedIntType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Arrays; import java.util.List; @@ -49,46 +49,46 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * #L% */ -public abstract class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5 { +public abstract class BaseJpaResourceProviderPatient extends BaseJpaResourceProvider { /** * Patient/123/$everything */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) + @Operation(name = JpaConstants.OPERATION_EVERYTHING, canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything", idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider patientInstanceEverything( javax.servlet.http.HttpServletRequest theServletRequest, @IdParam - IdType theId, + IIdType theId, - @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, + @Description(shortDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") + @OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt") + IPrimitiveType theCount, - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, + @Description(shortDefinition = "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") + @OperationParam(name = Constants.PARAM_OFFSET, typeName = "unsignedInt") + IPrimitiveType theOffset, @Description(shortDefinition = "Only return resources which were last updated as specified by the given range") @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) DateRangeParam theLastUpdated, @Description(shortDefinition = "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theContent, + @OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") + List> theContent, @Description(shortDefinition = "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theNarrative, + @OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") + List> theNarrative, @Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED) - List theFilter, + @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") + List> theFilter, @Description(shortDefinition = "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED) - List theTypes, + @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") + List> theTypes, @Sort SortSpec theSortSpec, @@ -108,7 +108,7 @@ public abstract class BaseJpaResourceProviderPatientR5 extends JpaResourceProvid everythingParams.setFilter(toStringAndList(theFilter)); everythingParams.setTypes(toStringAndList(theTypes)); - return ((IFhirResourceDaoPatient) getDao()).patientInstanceEverything(theServletRequest, theRequestDetails, everythingParams, theId); + return ((IFhirResourceDaoPatient) getDao()).patientInstanceEverything(theServletRequest, theRequestDetails, everythingParams, theId); } finally { endRequest(theServletRequest); } @@ -117,43 +117,43 @@ public abstract class BaseJpaResourceProviderPatientR5 extends JpaResourceProvid /** * /Patient/$everything */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) + @Operation(name = JpaConstants.OPERATION_EVERYTHING, canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything", idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider patientTypeEverything( javax.servlet.http.HttpServletRequest theServletRequest, - @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, + @Description(shortDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") + @OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt") + IPrimitiveType theCount, - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, + @Description(shortDefinition = "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") + @OperationParam(name = Constants.PARAM_OFFSET, typeName = "unsignedInt") + IPrimitiveType theOffset, @Description(shortDefinition = "Only return resources which were last updated as specified by the given range") @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) DateRangeParam theLastUpdated, @Description(shortDefinition = "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theContent, + @OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") + List> theContent, @Description(shortDefinition = "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theNarrative, + @OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") + List> theNarrative, @Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED) - List theFilter, + @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") + List> theFilter, @Description(shortDefinition = "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED) - List theTypes, + @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") + List> theTypes, @Description(shortDefinition = "Filter the resources to return based on the patient ids provided.") - @OperationParam(name = Constants.PARAM_ID, min = 0, max = OperationParam.MAX_UNLIMITED) - List theId, + @OperationParam(name = Constants.PARAM_ID, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "id") + List theId, @Sort SortSpec theSortSpec, @@ -173,7 +173,7 @@ public abstract class BaseJpaResourceProviderPatientR5 extends JpaResourceProvid everythingParams.setFilter(toStringAndList(theFilter)); everythingParams.setTypes(toStringAndList(theTypes)); - return ((IFhirResourceDaoPatient) getDao()).patientTypeEverything(theServletRequest, theRequestDetails, everythingParams, toFlattenedPatientIdTokenParamList(theId)); + return ((IFhirResourceDaoPatient) getDao()).patientTypeEverything(theServletRequest, theRequestDetails, everythingParams, toFlattenedPatientIdTokenParamList(theId)); } finally { endRequest(theServletRequest); } @@ -183,13 +183,13 @@ public abstract class BaseJpaResourceProviderPatientR5 extends JpaResourceProvid /** * Given a list of string types, return only the ID portions of any parameters passed in. */ - private TokenOrListParam toFlattenedPatientIdTokenParamList(List theId) { + private TokenOrListParam toFlattenedPatientIdTokenParamList(List theId) { TokenOrListParam retVal = new TokenOrListParam(); if (theId != null) { - for (IdType next: theId) { + for (IIdType next : theId) { if (isNotBlank(next.getValue())) { String[] split = next.getValueAsString().split(","); - Arrays.stream(split).map(IdType::new).forEach(id -> { + Arrays.stream(split).map(IdDt::new).forEach(id -> { retVal.addOr(new TokenParam(id.getIdPart())); }); } @@ -199,10 +199,10 @@ public abstract class BaseJpaResourceProviderPatientR5 extends JpaResourceProvid return retVal.getValuesAsQueryTokens().isEmpty() ? null: retVal; } - private StringAndListParam toStringAndList(List theNarrative) { + private StringAndListParam toStringAndList(List> theNarrative) { StringAndListParam retVal = new StringAndListParam(); if (theNarrative != null) { - for (StringType next : theNarrative) { + for (IPrimitiveType next : theNarrative) { if (isNotBlank(next.getValue())) { retVal.addAnd(new StringOrListParam().addOr(new StringParam(next.getValue()))); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java deleted file mode 100644 index 212e90fc88d..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java +++ /dev/null @@ -1,214 +0,0 @@ -package ca.uhn.fhir.jpa.provider; - -import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.dstu2.resource.Patient; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.StringAndListParam; -import ca.uhn.fhir.rest.param.StringOrListParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; - -import java.util.Arrays; -import java.util.List; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu2 { - - /** - * Patient/123/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET) - public IBundleProvider patientInstanceEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @IdParam - ca.uhn.fhir.model.primitive.IdDt theId, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - ca.uhn.fhir.model.primitive.UnsignedIntDt theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - ca.uhn.fhir.model.primitive.UnsignedIntDt theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Description(shortDefinition="Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_CONTENT, min=0, max=OperationParam.MAX_UNLIMITED) - List theContent, - - @Description(shortDefinition="Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TEXT, min=0, max=OperationParam.MAX_UNLIMITED) - List theNarrative, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED) - List theFilter, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED) - List theTypes, - - @Sort - SortSpec theSortSpec, - - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - PatientEverythingParameters everythingParams = new PatientEverythingParameters(); - everythingParams.setCount(theCount); - everythingParams.setOffset(theOffset); - everythingParams.setLastUpdated(theLastUpdated); - everythingParams.setSort(theSortSpec); - everythingParams.setContent(toStringAndList(theContent)); - everythingParams.setNarrative(toStringAndList(theNarrative)); - everythingParams.setFilter(toStringAndList(theFilter)); - everythingParams.setTypes(toStringAndList(theTypes)); - - return ((IFhirResourceDaoPatient) getDao()).patientInstanceEverything(theServletRequest, theRequestDetails, everythingParams, theId); - } finally { - endRequest(theServletRequest); - } - } - - /** - * /Patient/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET) - public IBundleProvider patientTypeEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - ca.uhn.fhir.model.primitive.UnsignedIntDt theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - ca.uhn.fhir.model.primitive.UnsignedIntDt theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Description(shortDefinition="Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_CONTENT, min=0, max=OperationParam.MAX_UNLIMITED) - List theContent, - - @Description(shortDefinition="Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TEXT, min=0, max=OperationParam.MAX_UNLIMITED) - List theNarrative, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED) - List theFilter, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED) - List theTypes, - - @Description(shortDefinition = "Filter the resources to return based on the patient ids provided.") - @OperationParam(name = Constants.PARAM_ID, min = 0, max = OperationParam.MAX_UNLIMITED) - List theId, - - @Sort - SortSpec theSortSpec, - - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - PatientEverythingParameters everythingParams = new PatientEverythingParameters(); - everythingParams.setCount(theCount); - everythingParams.setOffset(theOffset); - everythingParams.setLastUpdated(theLastUpdated); - everythingParams.setSort(theSortSpec); - everythingParams.setContent(toStringAndList(theContent)); - everythingParams.setNarrative(toStringAndList(theNarrative)); - everythingParams.setFilter(toStringAndList(theFilter)); - everythingParams.setTypes(toStringAndList(theTypes)); - - return ((IFhirResourceDaoPatient) getDao()).patientTypeEverything(theServletRequest, theRequestDetails, everythingParams, toFlattenedPatientIdTokenParamList(theId)); - } finally { - endRequest(theServletRequest); - } - - } - - /** - * Given a list of string types, return only the ID portions of any parameters passed in. - */ - private TokenOrListParam toFlattenedPatientIdTokenParamList(List theId) { - TokenOrListParam retVal = new TokenOrListParam(); - if (theId != null) { - for (IdDt next : theId) { - if (isNotBlank(next.getValue())) { - String[] split = next.getValueAsString().split(","); - Arrays.stream(split).map(IdDt::new).forEach(id -> { - retVal.addOr(new TokenParam(id.getIdPart())); - }); - } - } - } - return retVal.getValuesAsQueryTokens().isEmpty() ? null: retVal; - } - - private StringAndListParam toStringAndList(List theNarrative) { - StringAndListParam retVal = new StringAndListParam(); - if (theNarrative != null) { - for (StringDt next : theNarrative) { - if (isNotBlank(next.getValue())) { - retVal.addAnd(new StringOrListParam().addOr(new StringParam(next.getValue()))); - } - } - } - if (retVal.getValuesAsQueryTokens().isEmpty()) { - return null; - } - return retVal; - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderStructureDefinitionR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderStructureDefinition.java similarity index 71% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderStructureDefinitionR5.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderStructureDefinition.java index 9f32190dc0b..8eb4b373cf3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderStructureDefinitionR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderStructureDefinition.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.provider.r5; +package ca.uhn.fhir.jpa.provider; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoStructureDefinition; @@ -12,9 +12,9 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.ValidateUtil; -import org.hl7.fhir.r5.model.IdType; -import org.hl7.fhir.r5.model.StringType; -import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; /* * #%L @@ -36,16 +36,16 @@ import org.hl7.fhir.r5.model.StructureDefinition; * #L% */ -public abstract class BaseJpaResourceProviderStructureDefinitionR5 extends JpaResourceProviderR5 { +public abstract class BaseJpaResourceProviderStructureDefinition extends BaseJpaResourceProvider { /** * $snapshot operation */ @Operation(name=JpaConstants.OPERATION_SNAPSHOT, idempotent = true) - public StructureDefinition snapshot( - @IdParam(optional = true) IdType theId, - @OperationParam(name = "definition") StructureDefinition theStructureDefinition, - @OperationParam(name = "url") StringType theUrl, + public IBaseResource snapshot( + @IdParam(optional = true) IIdType theId, + @OperationParam(name = "definition", typeName = "StructureDefinition") IBaseResource theStructureDefinition, + @OperationParam(name = "url", typeName = "string") IPrimitiveType theUrl, RequestDetails theRequestDetails) { ValidateUtil.exactlyOneNotNullOrThrowInvalidRequestException( @@ -53,30 +53,31 @@ public abstract class BaseJpaResourceProviderStructureDefinitionR5 extends JpaRe "Must supply either an ID or a StructureDefinition or a URL (but not more than one of these things)" ); - StructureDefinition sd; + IBaseResource sd; + IFhirResourceDaoStructureDefinition dao = getDao(); if (theId == null && theStructureDefinition != null && theUrl == null) { sd = theStructureDefinition; } else if (theId != null && theStructureDefinition == null) { - sd = getDao().read(theId, theRequestDetails); + sd = dao.read(theId, theRequestDetails); } else { SearchParameterMap map = new SearchParameterMap(); map.setLoadSynchronousUpTo(2); map.add(org.hl7.fhir.r4.model.StructureDefinition.SP_URL, new UriParam(theUrl.getValue())); - IBundleProvider outcome = getDao().search(map, theRequestDetails); + IBundleProvider outcome = dao.search(map, theRequestDetails); Integer numResults = outcome.size(); assert numResults != null; if (numResults == 0) { throw new ResourceNotFoundException(Msg.code(1162) + "No StructureDefiniton found with url = '" + theUrl.getValue() + "'"); } - sd = (StructureDefinition) outcome.getResources(0, 1).get(0); + sd = outcome.getResources(0, 1).get(0); } - return getDao().generateSnapshot(sd, null, null, null); + return dao.generateSnapshot(sd, null, null, null); } @Override - public IFhirResourceDaoStructureDefinition getDao() { - return (IFhirResourceDaoStructureDefinition) super.getDao(); + public IFhirResourceDaoStructureDefinition getDao() { + return (IFhirResourceDaoStructureDefinition) super.getDao(); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProviderDstu2Plus.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProvider.java similarity index 62% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProviderDstu2Plus.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProvider.java index 1cbd66446f3..2a15d40edc5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProviderDstu2Plus.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProvider.java @@ -21,23 +21,33 @@ package ca.uhn.fhir.jpa.provider; */ import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; +import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Transaction; +import ca.uhn.fhir.rest.annotation.TransactionParam; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ParametersUtil; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.Meta; import javax.servlet.http.HttpServletRequest; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isNotBlank; -public abstract class BaseJpaSystemProviderDstu2Plus extends BaseJpaSystemProvider { +public final class JpaSystemProvider extends BaseJpaSystemProvider { @Description("Marks all currently existing resources of a given type, or all resources of all types, for reindexing.") @@ -50,7 +60,7 @@ public abstract class BaseJpaSystemProviderDstu2Plus extends BaseJpaSyste */ @Deprecated public IBaseResource markAllResourcesForReindexing( - @OperationParam(name="type", min = 0, max = 1, typeName = "code") IPrimitiveType theType + @OperationParam(name = "type", min = 0, max = 1, typeName = "code") IPrimitiveType theType ) { if (theType != null && isNotBlank(theType.getValueAsString())) { @@ -103,7 +113,7 @@ public abstract class BaseJpaSystemProviderDstu2Plus extends BaseJpaSyste @OperationParam(name = "content", min = 1, max = 1, typeName = "Bundle") @Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)") - IBaseBundle theMessageToProcess + IBaseBundle theMessageToProcess ) { startRequest(theServletRequest); @@ -115,5 +125,41 @@ public abstract class BaseJpaSystemProviderDstu2Plus extends BaseJpaSyste } + @Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true) + @Description(shortDefinition = "Provides the number of resources currently stored on the server, broken down by resource type") + public IBaseParameters getResourceCounts() { + IBaseParameters retVal = ParametersUtil.newInstance(getContext()); + + Map counts = getDao().getResourceCountsFromCache(); + counts = defaultIfNull(counts, Collections.emptyMap()); + counts = new TreeMap<>(counts); + for (Map.Entry nextEntry : counts.entrySet()) { + ParametersUtil.addParameterToParametersInteger(getContext(), retVal, nextEntry.getKey(), nextEntry.getValue().intValue()); + } + + return retVal; + } + + @Operation(name = ProviderConstants.OPERATION_META, idempotent = true, returnParameters = { + @OperationParam(name = "return", typeName = "Meta") + }) + public IBaseParameters meta(RequestDetails theRequestDetails) { + IBaseParameters retVal = ParametersUtil.newInstance(getContext()); + ParametersUtil.addParameterToParameters(getContext(), retVal, "return", getDao().metaGetOperation(theRequestDetails)); + return retVal; + } + + @SuppressWarnings("unchecked") + @Transaction + public IBaseBundle transaction(RequestDetails theRequestDetails, @TransactionParam IBaseBundle theResources) { + startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); + try { + IFhirSystemDao dao = getDao(); + return (IBaseBundle) dao.transaction(theRequestDetails, (T) theResources); + } finally { + endRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); + } + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java deleted file mode 100644 index 69f89d69f69..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java +++ /dev/null @@ -1,192 +0,0 @@ -package ca.uhn.fhir.jpa.provider; - -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.dstu2.composite.MetaDt; -import ca.uhn.fhir.model.dstu2.resource.Bundle; -import ca.uhn.fhir.model.dstu2.resource.Parameters; -import ca.uhn.fhir.model.dstu2.resource.Parameters.Parameter; -import ca.uhn.fhir.model.primitive.IntegerDt; -import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Transaction; -import ca.uhn.fhir.rest.annotation.TransactionParam; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.provider.ProviderConstants; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import org.hl7.fhir.r4.model.IntegerType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; - -import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus { - - @Autowired() - @Qualifier("mySystemDaoDstu2") - private IFhirSystemDao mySystemDao; - - //@formatter:off - // This is generated by hand: - // ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerDt.class, min=0, max=1),/" - @Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true, returnParameters = { - @OperationParam(name = "AllergyIntolerance", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Appointment", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "AppointmentResponse", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "AuditEvent", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Basic", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Binary", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "BodySite", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Bundle", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "CarePlan", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "CarePlan2", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Claim", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ClaimResponse", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ClinicalImpression", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Communication", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "CommunicationRequest", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Composition", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ConceptMap", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Condition", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Conformance", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Contract", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Contraindication", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Coverage", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "DataElement", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Device", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "DeviceComponent", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "DeviceMetric", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "DeviceUseRequest", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "DeviceUseStatement", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "DiagnosticOrder", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "DiagnosticReport", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "DocumentManifest", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "DocumentReference", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "EligibilityRequest", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "EligibilityResponse", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Encounter", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "EnrollmentRequest", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "EnrollmentResponse", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "EpisodeOfCare", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ExplanationOfBenefit", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "FamilyMemberHistory", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Flag", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Goal", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Group", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "HealthcareService", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ImagingObjectSelection", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ImagingStudy", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Immunization", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ImmunizationRecommendation", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ListResource", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Location", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Media", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Medication", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "MedicationAdministration", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "MedicationDispense", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "MedicationPrescription", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "MedicationStatement", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "MessageHeader", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "NamingSystem", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "NutritionOrder", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Observation", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "OperationDefinition", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "OperationOutcome", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Order", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "OrderResponse", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Organization", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Parameters", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Patient", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "PaymentNotice", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "PaymentReconciliation", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Person", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Practitioner", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Procedure", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ProcedureRequest", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ProcessRequest", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ProcessResponse", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Provenance", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Questionnaire", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "QuestionnaireAnswers", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ReferralRequest", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "RelatedPerson", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "RiskAssessment", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Schedule", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "SearchParameter", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Slot", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Specimen", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "StructureDefinition", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Subscription", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Substance", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "Supply", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "ValueSet", type = IntegerDt.class, min = 0, max = 1), - @OperationParam(name = "VisionPrescription", type = IntegerDt.class, min = 0, max = 1) - }) - @Description(shortDefinition = "Provides the number of resources currently stored on the server, broken down by resource type") - //@formatter:on - public Parameters getResourceCounts() { - Parameters retVal = new Parameters(); - - Map counts = mySystemDao.getResourceCountsFromCache(); - counts = defaultIfNull(counts, Collections.emptyMap()); - counts = new TreeMap<>(counts); - for (Entry nextEntry : counts.entrySet()) { - retVal.addParameter().setName(new StringDt(nextEntry.getKey())).setValue(new IntegerDt(nextEntry.getValue().intValue())); - } - - return retVal; - } - - @Operation(name = ProviderConstants.OPERATION_META, idempotent = true, returnParameters = { - @OperationParam(name = "return", type = MetaDt.class) - }) - public Parameters meta(RequestDetails theRequestDetails) { - Parameters parameters = new Parameters(); - parameters.addParameter().setName("return").setValue(getDao().metaGetOperation(theRequestDetails)); - return parameters; - } - - @Transaction - public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) { - startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); - try { - return getDao().transaction(theRequestDetails, theResources); - } finally { - endRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); - } - } - - public static Parameters toExpungeResponse(org.hl7.fhir.r4.model.Parameters theRetVal) { - Integer count = ((IntegerType) theRetVal.getParameterFirstRep().getValue()).getValue(); - return new Parameters() - .addParameter(new Parameter().setName("count").setValue(new IntegerDt(count))); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java deleted file mode 100644 index c75e1b4a8c7..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ /dev/null @@ -1,113 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import org.hl7.fhir.dstu3.model.BooleanType; -import org.hl7.fhir.dstu3.model.CodeSystem; -import org.hl7.fhir.dstu3.model.CodeType; -import org.hl7.fhir.dstu3.model.CodeableConcept; -import org.hl7.fhir.dstu3.model.Coding; -import org.hl7.fhir.dstu3.model.Parameters; -import org.hl7.fhir.dstu3.model.StringType; -import org.hl7.fhir.dstu3.model.UriType; -import org.hl7.fhir.exceptions.FHIRException; - -import javax.servlet.http.HttpServletRequest; -import java.util.List; - -public abstract class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderDstu3 { - - @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters = { - @OperationParam(name = "name", type = StringType.class, min = 1), - @OperationParam(name = "version", type = StringType.class, min = 0), - @OperationParam(name = "display", type = StringType.class, min = 1), - @OperationParam(name = "abstract", type = BooleanType.class, min = 1), - }) - public Parameters lookup( - HttpServletRequest theServletRequest, - @OperationParam(name = "code", min = 0, max = 1) CodeType theCode, - @OperationParam(name = "system", min = 0, max = 1) UriType theSystem, - @OperationParam(name = "coding", min = 0, max = 1) Coding theCoding, - @OperationParam(name = "version", min=0, max=1) StringType theVersion, - @OperationParam(name = "displayLanguage", min=0, max=1) CodeType theDisplayLanguage, - @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties, - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IValidationSupport.LookupCodeResult result; - if (theVersion != null) { - result = dao.lookupCode(theCode, new UriType(theSystem.getValue() + "|" + theVersion), theCoding, theDisplayLanguage, theRequestDetails); - } else { - result = dao.lookupCode(theCode, theSystem, theCoding, theDisplayLanguage, theRequestDetails); - } - result.throwNotFoundIfAppropriate(); - return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); - } catch (FHIRException e) { - throw new InternalErrorException(Msg.code(1153) + e); - } finally { - endRequest(theServletRequest); - } - } - - - /** - * $subsumes operation - */ - @Operation(name = JpaConstants.OPERATION_SUBSUMES, idempotent = true, returnParameters = { - @OperationParam(name = "outcome", type = CodeType.class, min = 1), - }) - public Parameters subsumes( - HttpServletRequest theServletRequest, - @OperationParam(name = "codeA", min = 0, max = 1) CodeType theCodeA, - @OperationParam(name = "codeB", min = 0, max = 1) CodeType theCodeB, - @OperationParam(name = "system", min = 0, max = 1) UriType theSystem, - @OperationParam(name = "codingA", min = 0, max = 1) Coding theCodingA, - @OperationParam(name = "codingB", min = 0, max = 1) Coding theCodingB, - @OperationParam(name = "version", min = 0, max = 1) StringType theVersion, - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IFhirResourceDaoCodeSystem.SubsumesResult result; - if (theVersion != null) { - theSystem = new UriType(theSystem.asStringValue() + "|" + theVersion.toString()); - } - result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); - return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); - } finally { - endRequest(theServletRequest); - } - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCompositionDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCompositionDstu3.java deleted file mode 100644 index f0d9c3dcb12..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCompositionDstu3.java +++ /dev/null @@ -1,83 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.DateRangeParam; -import org.hl7.fhir.dstu3.model.Composition; -import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.UnsignedIntType; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderCompositionDstu3 extends JpaResourceProviderDstu3 { - - /** - * Composition/123/$document - * - * @param theRequestDetails - */ - //@formatter:off - @Operation(name = JpaConstants.OPERATION_DOCUMENT, idempotent = true, bundleType=BundleTypeEnum.DOCUMENT) - public IBundleProvider getDocumentForComposition( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @IdParam - IdType theId, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Sort - SortSpec theSortSpec, - - RequestDetails theRequestDetails - ) { - //@formatter:on - - startRequest(theServletRequest); - try { - IBundleProvider bundleProvider = ((IFhirResourceDaoComposition) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec, theRequestDetails); - return bundleProvider; - } finally { - endRequest(theServletRequest); - } - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java index b1f753e539f..1421e95203d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap; import ca.uhn.fhir.jpa.api.model.TranslationRequest; import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; @@ -47,7 +48,7 @@ import org.hl7.fhir.exceptions.FHIRException; import javax.servlet.http.HttpServletRequest; -public abstract class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderDstu3 { +public abstract class BaseJpaResourceProviderConceptMapDstu3 extends BaseJpaResourceProvider { @Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1), @OperationParam(name = "message", type = StringType.class, min = 0, max = 1), diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderEncounterDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderEncounterDstu3.java deleted file mode 100644 index 9f82ce38bac..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderEncounterDstu3.java +++ /dev/null @@ -1,108 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoEncounter; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.DateRangeParam; -import org.hl7.fhir.dstu3.model.Encounter; -import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.UnsignedIntType; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderEncounterDstu3 extends JpaResourceProviderDstu3 { - - /** - * Encounter/123/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET) - public IBundleProvider EncounterInstanceEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @IdParam - IdType theId, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Sort - SortSpec theSortSpec - ) { - - startRequest(theServletRequest); - try { - return ((IFhirResourceDaoEncounter)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset,theLastUpdated, theSortSpec); - } finally { - endRequest(theServletRequest); - }} - - /** - * /Encounter/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET) - public IBundleProvider EncounterTypeEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Sort - SortSpec theSortSpec - ) { - - startRequest(theServletRequest); - try { - return ((IFhirResourceDaoEncounter)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec); - } finally { - endRequest(theServletRequest); - } - - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java deleted file mode 100644 index 13e8fc72e32..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java +++ /dev/null @@ -1,114 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.RawParam; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.DateAndListParam; -import ca.uhn.fhir.rest.param.ReferenceAndListParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import org.hl7.fhir.dstu3.model.Observation; -import org.hl7.fhir.dstu3.model.UnsignedIntType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; - -import java.util.List; -import java.util.Map; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProviderDstu3 { - - /** - * Observation/$lastn - */ - @Operation(name = JpaConstants.OPERATION_LASTN, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) - public IBundleProvider observationLastN( - - javax.servlet.http.HttpServletRequest theServletRequest, - javax.servlet.http.HttpServletResponse theServletResponse, - - ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails, - - @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(shortDefinition="The classification of the type of observation") - @OperationParam(name="category") - TokenAndListParam theCategory, - - @Description(shortDefinition="The code of the observation type") - @OperationParam(name="code") - TokenAndListParam theCode, - - @Description(shortDefinition="The effective date of the observation") - @OperationParam(name="date") - DateAndListParam theDate, - - @Description(shortDefinition="The subject that the observation is about (if patient)") - @OperationParam(name="patient") - ReferenceAndListParam thePatient, - - @Description(shortDefinition="The subject that the observation is about") - @OperationParam(name="subject" ) - ReferenceAndListParam theSubject, - - @Description(shortDefinition="The maximum number of observations to return for each observation code") - @OperationParam(name = "max", typeName = "integer", min = 0, max = 1) - IPrimitiveType theMax, - - @RawParam - Map> theAdditionalRawParams - ) { - startRequest(theServletRequest); - try { - SearchParameterMap paramMap = new SearchParameterMap(); - paramMap.add(Observation.SP_CATEGORY, theCategory); - paramMap.add(Observation.SP_CODE, theCode); - paramMap.add(Observation.SP_DATE, theDate); - if (thePatient != null) { - paramMap.add(Observation.SP_PATIENT, thePatient); - } - if (theSubject != null) { - paramMap.add(Observation.SP_SUBJECT, theSubject); - } - if (theMax != null) { - paramMap.setLastNMax(theMax.getValue()); - } - if (theCount != null) { - paramMap.setCount(theCount.getValue()); - } - - getDao().translateRawParameters(theAdditionalRawParams, paramMap); - - return ((IFhirResourceDaoObservation) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); - } finally { - endRequest(theServletRequest); - } - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderPatientDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderPatientDstu3.java deleted file mode 100644 index c85a8948062..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderPatientDstu3.java +++ /dev/null @@ -1,214 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.StringAndListParam; -import ca.uhn.fhir.rest.param.StringOrListParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.Patient; -import org.hl7.fhir.dstu3.model.StringType; -import org.hl7.fhir.dstu3.model.UnsignedIntType; - -import java.util.Arrays; -import java.util.List; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderPatientDstu3 extends JpaResourceProviderDstu3 { - - /** - * Patient/123/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) - public IBundleProvider patientInstanceEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @IdParam - IdType theId, - - @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition = "Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) - DateRangeParam theLastUpdated, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theContent, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theNarrative, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED) - List theFilter, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED) - List theTypes, - - @Sort - SortSpec theSortSpec, - - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - PatientEverythingParameters everythingParams = new PatientEverythingParameters(); - everythingParams.setCount(theCount); - everythingParams.setOffset(theOffset); - everythingParams.setLastUpdated(theLastUpdated); - everythingParams.setSort(theSortSpec); - everythingParams.setContent(toStringAndList(theContent)); - everythingParams.setNarrative(toStringAndList(theNarrative)); - everythingParams.setFilter(toStringAndList(theFilter)); - everythingParams.setTypes(toStringAndList(theTypes)); - - return ((IFhirResourceDaoPatient) getDao()).patientInstanceEverything(theServletRequest, theRequestDetails, everythingParams, theId); - } finally { - endRequest(theServletRequest); - } - } - - /** - * /Patient/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) - public IBundleProvider patientTypeEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition = "Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) - DateRangeParam theLastUpdated, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theContent, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theNarrative, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED) - List theFilter, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED) - List theTypes, - - @Description(shortDefinition = "Filter the resources to return based on the patient ids provided.") - @OperationParam(name = Constants.PARAM_ID, min = 0, max = OperationParam.MAX_UNLIMITED) - List theId, - - @Sort - SortSpec theSortSpec, - - RequestDetails theRequestDetails - ) { - startRequest(theServletRequest); - try { - PatientEverythingParameters everythingParams = new PatientEverythingParameters(); - everythingParams.setCount(theCount); - everythingParams.setOffset(theOffset); - everythingParams.setLastUpdated(theLastUpdated); - everythingParams.setSort(theSortSpec); - everythingParams.setContent(toStringAndList(theContent)); - everythingParams.setNarrative(toStringAndList(theNarrative)); - everythingParams.setFilter(toStringAndList(theFilter)); - everythingParams.setTypes(toStringAndList(theTypes)); - - return ((IFhirResourceDaoPatient) getDao()).patientTypeEverything(theServletRequest, theRequestDetails, everythingParams, toFlattenedPatientIdTokenParamList(theId)); - } finally { - endRequest(theServletRequest); - } - } - - /** - * Given a list of string types, return only the ID portions of any parameters passed in. - */ - private TokenOrListParam toFlattenedPatientIdTokenParamList(List theId) { - TokenOrListParam retVal = new TokenOrListParam(); - if (theId != null) { - for (IdType next: theId) { - if (isNotBlank(next.getValue())) { - String[] split = next.getValueAsString().split(","); - Arrays.stream(split).map(IdType::new).forEach(id -> { - retVal.addOr(new TokenParam(id.getIdPart())); - }); - } - } - } - return retVal.getValuesAsQueryTokens().isEmpty() ? null: retVal; - } - - - private StringAndListParam toStringAndList(List theNarrative) { - StringAndListParam retVal = new StringAndListParam(); - if (theNarrative != null) { - for (StringType next : theNarrative) { - if (isNotBlank(next.getValue())) { - retVal.addAnd(new StringOrListParam().addOr(new StringParam(next.getValue()))); - } - } - } - if (retVal.getValuesAsQueryTokens().isEmpty()) { - return null; - } - return retVal; - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderStructureDefinitionDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderStructureDefinitionDstu3.java deleted file mode 100644 index 31d00370c67..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderStructureDefinitionDstu3.java +++ /dev/null @@ -1,80 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoStructureDefinition; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.UriParam; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.util.ValidateUtil; -import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.StringType; -import org.hl7.fhir.dstu3.model.StructureDefinition; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderStructureDefinitionDstu3 extends JpaResourceProviderDstu3 { - - /** - * $snapshot operation - */ - @Operation(name=JpaConstants.OPERATION_SNAPSHOT, idempotent = true) - public StructureDefinition snapshot( - @IdParam(optional = true) IdType theId, - @OperationParam(name = "definition") StructureDefinition theStructureDefinition, - @OperationParam(name = "url") StringType theUrl, - RequestDetails theRequestDetails) { - - ValidateUtil.exactlyOneNotNullOrThrowInvalidRequestException( - new Object[]{ theId, theStructureDefinition, theUrl }, - "Must supply either an ID or a StructureDefinition or a URL (but not more than one of these things)" - ); - - StructureDefinition sd; - if (theId == null && theStructureDefinition != null && theUrl == null) { - sd = theStructureDefinition; - } else if (theId != null && theStructureDefinition == null) { - sd = getDao().read(theId, theRequestDetails); - } else { - SearchParameterMap map = new SearchParameterMap(); - map.setLoadSynchronousUpTo(2); - map.add(StructureDefinition.SP_URL, new UriParam(theUrl.getValue())); - IBundleProvider outcome = getDao().search(map, theRequestDetails); - if (outcome.size() == 0) { - throw new ResourceNotFoundException(Msg.code(1152) + "No StructureDefiniton found with url = '" + theUrl.getValue() + "'"); - } - sd = (StructureDefinition) outcome.getResources(0, 1).get(0); - } - - return getDao().generateSnapshot(sd, null, null, null); - } - - @Override - public IFhirResourceDaoStructureDefinition getDao() { - return (IFhirResourceDaoStructureDefinition) super.getDao(); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java index f6a4be9e1f1..fbd09c49e50 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java @@ -20,8 +20,9 @@ package ca.uhn.fhir.jpa.provider.dstu3; * #L% */ +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import org.hl7.fhir.dstu3.model.ValueSet; -public abstract class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDstu3 { +public abstract class BaseJpaResourceProviderValueSetDstu3 extends BaseJpaResourceProvider { } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java deleted file mode 100644 index 189463df381..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java +++ /dev/null @@ -1,37 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; -import org.hl7.fhir.instance.model.api.IAnyResource; - -public class JpaResourceProviderDstu3 extends BaseJpaResourceProvider { - - public JpaResourceProviderDstu3() { - // nothing - } - - public JpaResourceProviderDstu3(IFhirResourceDao theDao) { - super(theDao); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java deleted file mode 100644 index 86d5830e62b..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java +++ /dev/null @@ -1,182 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Transaction; -import ca.uhn.fhir.rest.annotation.TransactionParam; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.provider.ProviderConstants; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import org.hl7.fhir.dstu3.model.Bundle; -import org.hl7.fhir.dstu3.model.IntegerType; -import org.hl7.fhir.dstu3.model.Meta; -import org.hl7.fhir.dstu3.model.Parameters; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; - -import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus { - - @Autowired() - @Qualifier("mySystemDaoDstu3") - private IFhirSystemDao mySystemDao; - - // This is generated by hand: - // ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/" - @Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true, returnParameters = { - @OperationParam(name = "AllergyIntolerance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Appointment", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "AppointmentResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "AuditEvent", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Basic", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Binary", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "BodySite", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Bundle", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "CarePlan", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "CarePlan2", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Claim", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ClaimResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ClinicalImpression", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Communication", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "CommunicationRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Composition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ConceptMap", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Condition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Conformance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Contract", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Contraindication", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Coverage", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DataElement", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Device", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceComponent", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceMetric", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceUseRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceUseStatement", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DiagnosticOrder", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DiagnosticReport", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DocumentManifest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DocumentReference", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EligibilityRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EligibilityResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Encounter", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EnrollmentRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EnrollmentResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EpisodeOfCare", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ExplanationOfBenefit", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "FamilyMemberHistory", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Flag", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Goal", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Group", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "HealthcareService", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ImagingObjectSelection", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ImagingStudy", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Immunization", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ImmunizationRecommendation", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ListResource", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Location", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Media", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Medication", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationAdministration", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationDispense", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationPrescription", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationStatement", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MessageHeader", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "NamingSystem", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "NutritionOrder", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Observation", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "OperationDefinition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "OperationOutcome", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Order", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "OrderResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Organization", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Parameters", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Patient", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "PaymentNotice", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "PaymentReconciliation", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Person", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Practitioner", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Procedure", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ProcedureRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ProcessRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ProcessResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Provenance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Questionnaire", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "QuestionnaireAnswers", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ReferralRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "RelatedPerson", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "RiskAssessment", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Schedule", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "SearchParameter", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Slot", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Specimen", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "StructureDefinition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Subscription", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Substance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Supply", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ValueSet", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "VisionPrescription", type = IntegerType.class, min = 0, max = 1) - }) - @Description(shortDefinition = "Provides the number of resources currently stored on the server, broken down by resource type") - public Parameters getResourceCounts() { - Parameters retVal = new Parameters(); - - Map counts = mySystemDao.getResourceCountsFromCache(); - counts = defaultIfNull(counts, Collections.emptyMap()); - counts = new TreeMap<>(counts); - for (Entry nextEntry : counts.entrySet()) { - retVal.addParameter().setName((nextEntry.getKey())).setValue(new IntegerType(nextEntry.getValue().intValue())); - } - - return retVal; - } - - @Operation(name = ProviderConstants.OPERATION_META, idempotent = true, returnParameters = { - @OperationParam(name = "return", type = Meta.class) - }) - public Parameters meta(RequestDetails theRequestDetails) { - Parameters parameters = new Parameters(); - parameters.addParameter().setName("return").setValue(getDao().metaGetOperation(theRequestDetails)); - return parameters; - } - - @Transaction - public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) { - startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); - try { - return getDao().transaction(theRequestDetails, theResources); - } finally { - endRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); - } - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java deleted file mode 100644 index 2fe5703c63f..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java +++ /dev/null @@ -1,186 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.context.support.ConceptValidationOptions; -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.context.support.ValidationSupportContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.config.JpaConfig; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderValueSetDstu2; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; -import org.hl7.fhir.r4.model.BooleanType; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.CodeType; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.UriType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - -import javax.servlet.http.HttpServletRequest; -import java.util.List; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 { - - @Autowired - @Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN) - private ValidationSupportChain myValidationSupportChain; - - @Autowired - protected ITermReadSvcR4 myTermSvc; - - /** - * $lookup operation - */ - @SuppressWarnings("unchecked") - @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= { - @OperationParam(name="name", type=StringType.class, min=1), - @OperationParam(name="version", type=StringType.class, min=0), - @OperationParam(name="display", type=StringType.class, min=1), - @OperationParam(name="abstract", type=BooleanType.class, min=1), - }) - public Parameters lookup( - HttpServletRequest theServletRequest, - @OperationParam(name="code", min=0, max=1) CodeType theCode, - @OperationParam(name="system", min=0, max=1) UriType theSystem, - @OperationParam(name="coding", min=0, max=1) Coding theCoding, - @OperationParam(name="version", min=0, max=1) StringType theVersion, - @OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage, - @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties, - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IValidationSupport.LookupCodeResult result; - if (theVersion != null) { - result = dao.lookupCode(theCode, new UriType(theSystem.getValue() + "|" + theVersion), theCoding, theDisplayLanguage, theRequestDetails); - } else { - result = dao.lookupCode(theCode, theSystem, theCoding, theDisplayLanguage, theRequestDetails); - } - result.throwNotFoundIfAppropriate(); - return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); - } finally { - endRequest(theServletRequest); - } - } - - - /** - * $subsumes operation - */ - @Operation(name = JpaConstants.OPERATION_SUBSUMES, idempotent = true, returnParameters= { - @OperationParam(name="outcome", type=CodeType.class, min=1), - }) - public Parameters subsumes( - HttpServletRequest theServletRequest, - @OperationParam(name="codeA", min=0, max=1) CodeType theCodeA, - @OperationParam(name="codeB", min=0, max=1) CodeType theCodeB, - @OperationParam(name="system", min=0, max=1) UriType theSystem, - @OperationParam(name="codingA", min=0, max=1) Coding theCodingA, - @OperationParam(name="codingB", min=0, max=1) Coding theCodingB, - @OperationParam(name="version", min=0, max=1) StringType theVersion, - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IFhirResourceDaoCodeSystem.SubsumesResult result; - if (theVersion != null) { - theSystem = new UriType(theSystem.asStringValue() + "|" + theVersion.toString()); - } - result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); - return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); - } finally { - endRequest(theServletRequest); - } - } - - /** - * $validate-code operation - */ - @SuppressWarnings("unchecked") - @Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = { - @OperationParam(name = "result", type = BooleanType.class, min = 1), - @OperationParam(name = "message", type = StringType.class), - @OperationParam(name = "display", type = StringType.class) - }) - public Parameters validateCode( - HttpServletRequest theServletRequest, - @IdParam(optional = true) IdType theId, - @OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl, - @OperationParam(name = "version", min = 0, max = 1) StringType theVersion, - @OperationParam(name = "code", min = 0, max = 1) CodeType theCode, - @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay, - @OperationParam(name = "coding", min = 0, max = 1) Coding theCoding, - @OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theCodeableConcept, - RequestDetails theRequestDetails - ) { - - IValidationSupport.CodeValidationResult result = null; - startRequest(theServletRequest); - try { - // If a Remote Terminology Server has been configured, use it - if (myValidationSupportChain.isRemoteTerminologyServiceConfigured()) { - String codeSystemUrl = (theCodeSystemUrl != null && theCodeSystemUrl.hasValue()) ? - theCodeSystemUrl.asStringValue() : null; - String code = (theCode != null && theCode.hasValue()) ? theCode.asStringValue() : null; - String display = (theDisplay != null && theDisplay.hasValue()) ? theDisplay.asStringValue() : null; - - if (theCoding != null) { - if (theCoding.hasSystem()) { - if (codeSystemUrl != null && !codeSystemUrl.equalsIgnoreCase(theCoding.getSystem())) { - throw new InvalidRequestException(Msg.code(1160) + "Coding.system '" + theCoding.getSystem() + "' does not equal param url '" + theCodeSystemUrl + "'. Unable to validate-code."); - } - codeSystemUrl = theCoding.getSystem(); - code = theCoding.getCode(); - display = theCoding.getDisplay(); - - result = myValidationSupportChain.validateCode( - new ValidationSupportContext(myValidationSupportChain), new ConceptValidationOptions(), - codeSystemUrl, code, display, null); - } - } - } else { - // Otherwise, use the local DAO layer to validate the code - IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails); - } - return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result); - } finally { - endRequest(theServletRequest); - } - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCompositionR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCompositionR4.java deleted file mode 100644 index 72f1c4899f5..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCompositionR4.java +++ /dev/null @@ -1,81 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.DateRangeParam; -import org.hl7.fhir.r4.model.Composition; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.UnsignedIntType; - - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderCompositionR4 extends JpaResourceProviderR4 { - - /** - * Composition/123/$document - */ - @Operation(name = JpaConstants.OPERATION_DOCUMENT, idempotent = true, bundleType=BundleTypeEnum.DOCUMENT) -// public IBaseBundle getDocumentForComposition( - public IBundleProvider getDocumentForComposition( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @IdParam - IdType theId, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Sort - SortSpec theSortSpec, - - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - IBundleProvider bundleProvider = ((IFhirResourceDaoComposition) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec, theRequestDetails); - return bundleProvider; - } finally { - endRequest(theServletRequest); - } - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderConceptMapR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderConceptMapR4.java index c823627f84b..8dbbc6ab345 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderConceptMapR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderConceptMapR4.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap; import ca.uhn.fhir.jpa.api.model.TranslationRequest; import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; @@ -43,7 +44,7 @@ import org.hl7.fhir.r4.model.UriType; import javax.servlet.http.HttpServletRequest; -public abstract class BaseJpaResourceProviderConceptMapR4 extends JpaResourceProviderR4 { +public abstract class BaseJpaResourceProviderConceptMapR4 extends BaseJpaResourceProvider { @Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1), @OperationParam(name = "message", type = StringType.class, min = 0, max = 1), diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderEncounterR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderEncounterR4.java deleted file mode 100644 index 154ba480fba..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderEncounterR4.java +++ /dev/null @@ -1,108 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoEncounter; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.DateRangeParam; -import org.hl7.fhir.r4.model.Encounter; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.UnsignedIntType; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderEncounterR4 extends JpaResourceProviderR4 { - - /** - * Encounter/123/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET) - public IBundleProvider EncounterInstanceEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @IdParam - IdType theId, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Sort - SortSpec theSortSpec - ) { - - startRequest(theServletRequest); - try { - return ((IFhirResourceDaoEncounter)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec); - } finally { - endRequest(theServletRequest); - }} - - /** - * /Encounter/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET) - public IBundleProvider EncounterTypeEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Sort - SortSpec theSortSpec - ) { - - startRequest(theServletRequest); - try { - return ((IFhirResourceDaoEncounter)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec); - } finally { - endRequest(theServletRequest); - } - - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderMessageHeaderR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderMessageHeaderR4.java deleted file mode 100644 index 89d127bef21..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderMessageHeaderR4.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import org.hl7.fhir.r4.model.MessageHeader; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderMessageHeaderR4 extends JpaResourceProviderR4 { - // nothing right now -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java deleted file mode 100644 index 20eacc9421d..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java +++ /dev/null @@ -1,115 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.RawParam; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.DateAndListParam; -import ca.uhn.fhir.rest.param.ReferenceAndListParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r4.model.Observation; -import org.hl7.fhir.r4.model.UnsignedIntType; - -import java.util.List; -import java.util.Map; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderObservationR4 extends JpaResourceProviderR4 { - - /** - * Observation/$lastn - */ - @Operation(name = JpaConstants.OPERATION_LASTN, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) - public IBundleProvider observationLastN( - - javax.servlet.http.HttpServletRequest theServletRequest, - javax.servlet.http.HttpServletResponse theServletResponse, - - ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails, - - @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(shortDefinition="The classification of the type of observation") - @OperationParam(name="category") - TokenAndListParam theCategory, - - @Description(shortDefinition="The code of the observation type") - @OperationParam(name="code") - TokenAndListParam theCode, - - @Description(shortDefinition="The effective date of the observation") - @OperationParam(name="date") - DateAndListParam theDate, - - @Description(shortDefinition="The subject that the observation is about (if patient)") - @OperationParam(name="patient") - ReferenceAndListParam thePatient, - - @Description(shortDefinition="The subject that the observation is about") - @OperationParam(name="subject" ) - ReferenceAndListParam theSubject, - - @Description(shortDefinition="The maximum number of observations to return for each observation code") - @OperationParam(name = "max", typeName = "integer", min = 0, max = 1) - IPrimitiveType theMax, - - @RawParam - Map> theAdditionalRawParams - ) { - startRequest(theServletRequest); - try { - SearchParameterMap paramMap = new SearchParameterMap(); - paramMap.add(Observation.SP_CATEGORY, theCategory); - paramMap.add(Observation.SP_CODE, theCode); - paramMap.add(Observation.SP_DATE, theDate); - if (thePatient != null) { - paramMap.add(Observation.SP_PATIENT, thePatient); - } - if (theSubject != null) { - paramMap.add(Observation.SP_SUBJECT, theSubject); - } - if(theMax != null) { - paramMap.setLastNMax(theMax.getValue()); - } - if (theCount != null) { - paramMap.setCount(theCount.getValue()); - } - - getDao().translateRawParameters(theAdditionalRawParams, paramMap); - - return ((IFhirResourceDaoObservation) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); - } finally { - endRequest(theServletRequest); - } - } - - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderPatientR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderPatientR4.java deleted file mode 100644 index a50d3be2e0d..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderPatientR4.java +++ /dev/null @@ -1,320 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.PatientEverythingParameters; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.StringAndListParam; -import ca.uhn.fhir.rest.param.StringOrListParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.rest.server.provider.ProviderConstants; -import org.hl7.fhir.r4.model.Coverage; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.UnsignedIntType; -import org.springframework.beans.factory.annotation.Autowired; - -import javax.servlet.http.HttpServletRequest; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4 { - - @Autowired - private MemberMatcherR4Helper myMemberMatcherR4Helper; - - - /** - * Patient/123/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything", idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) - public IBundleProvider patientInstanceEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @IdParam - IdType theId, - - @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition = "Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) - DateRangeParam theLastUpdated, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theContent, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theNarrative, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED) - List theFilter, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED) - List theTypes, - - @Sort - SortSpec theSortSpec, - - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - PatientEverythingParameters everythingParams = new PatientEverythingParameters(); - everythingParams.setCount(theCount); - everythingParams.setOffset(theOffset); - everythingParams.setLastUpdated(theLastUpdated); - everythingParams.setSort(theSortSpec); - everythingParams.setContent(toStringAndList(theContent)); - everythingParams.setNarrative(toStringAndList(theNarrative)); - everythingParams.setFilter(toStringAndList(theFilter)); - everythingParams.setTypes(toStringAndList(theTypes)); - - return ((IFhirResourceDaoPatient) getDao()).patientInstanceEverything(theServletRequest, theRequestDetails, everythingParams, theId); - } finally { - endRequest(theServletRequest); - } - } - - /** - * /Patient/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything", idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) - public IBundleProvider patientTypeEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition = "Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1) - DateRangeParam theLastUpdated, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theContent, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED) - List theNarrative, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _filter filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED) - List theFilter, - - @Description(shortDefinition = "Filter the resources to return only resources matching the given _type filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)") - @OperationParam(name = Constants.PARAM_TYPE, min = 0, max = OperationParam.MAX_UNLIMITED) - List theTypes, - - @Description(shortDefinition = "Filter the resources to return based on the patient ids provided.") - @OperationParam(name = Constants.PARAM_ID, min = 0, max = OperationParam.MAX_UNLIMITED) - List theId, - - @Sort - SortSpec theSortSpec, - - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - PatientEverythingParameters everythingParams = new PatientEverythingParameters(); - everythingParams.setCount(theCount); - everythingParams.setOffset(theOffset); - everythingParams.setLastUpdated(theLastUpdated); - everythingParams.setSort(theSortSpec); - everythingParams.setContent(toStringAndList(theContent)); - everythingParams.setNarrative(toStringAndList(theNarrative)); - everythingParams.setFilter(toStringAndList(theFilter)); - everythingParams.setTypes(toStringAndList(theTypes)); - - return ((IFhirResourceDaoPatient) getDao()).patientTypeEverything(theServletRequest, theRequestDetails, everythingParams, toFlattenedPatientIdTokenParamList(theId)); - } finally { - endRequest(theServletRequest); - } - - } - - - /** - * /Patient/$member-match operation - * Basic implementation matching by coverage id or by coverage identifier. Matching by - * Beneficiary (Patient) demographics on family name and birthdate in this version - */ - @Operation(name = ProviderConstants.OPERATION_MEMBER_MATCH, canonicalUrl = "http://hl7.org/fhir/us/davinci-hrex/OperationDefinition/member-match", idempotent = false, returnParameters = { - @OperationParam(name = "MemberIdentifier", typeName = "string") - }) - public Parameters patientMemberMatch( - javax.servlet.http.HttpServletRequest theServletRequest, - - @Description(shortDefinition = "The target of the operation. Will be returned with Identifier for matched coverage added.") - @OperationParam(name = Constants.PARAM_MEMBER_PATIENT, min = 1, max = 1) - Patient theMemberPatient, - - @Description(shortDefinition = "Old coverage information as extracted from beneficiary's card.") - @OperationParam(name = Constants.PARAM_OLD_COVERAGE, min = 1, max = 1) - Coverage oldCoverage, - - @Description(shortDefinition = "New Coverage information. Provided as a reference. Optionally returned unmodified.") - @OperationParam(name = Constants.PARAM_NEW_COVERAGE, min = 1, max = 1) - Coverage newCoverage, - - RequestDetails theRequestDetails - ) { - return doMemberMatchOperation(theServletRequest, theMemberPatient, oldCoverage, newCoverage, theRequestDetails); - } - - - private Parameters doMemberMatchOperation(HttpServletRequest theServletRequest, Patient theMemberPatient, - Coverage theCoverageToMatch, Coverage theCoverageToLink, RequestDetails theRequestDetails) { - - validateParams(theMemberPatient, theCoverageToMatch, theCoverageToLink); - - Optional coverageOpt = myMemberMatcherR4Helper.findMatchingCoverage(theCoverageToMatch); - if ( ! coverageOpt.isPresent()) { - String i18nMessage = getContext().getLocalizer().getMessage( - "operation.member.match.error.coverage.not.found"); - throw new UnprocessableEntityException(Msg.code(1155) + i18nMessage); - } - Coverage coverage = coverageOpt.get(); - - Optional patientOpt = myMemberMatcherR4Helper.getBeneficiaryPatient(coverage); - if (! patientOpt.isPresent()) { - String i18nMessage = getContext().getLocalizer().getMessage( - "operation.member.match.error.beneficiary.not.found"); - throw new UnprocessableEntityException(Msg.code(1156) + i18nMessage); - } - - Patient patient = patientOpt.get(); - if (!myMemberMatcherR4Helper.validPatientMember(patient, theMemberPatient)) { - String i18nMessage = getContext().getLocalizer().getMessage( - "operation.member.match.error.patient.not.found"); - throw new UnprocessableEntityException(Msg.code(2146) + i18nMessage); - } - - if (patient.getIdentifier().isEmpty()) { - String i18nMessage = getContext().getLocalizer().getMessage( - "operation.member.match.error.beneficiary.without.identifier"); - throw new UnprocessableEntityException(Msg.code(1157) + i18nMessage); - } - - myMemberMatcherR4Helper.addMemberIdentifierToMemberPatient(theMemberPatient, patient.getIdentifierFirstRep()); - - return myMemberMatcherR4Helper.buildSuccessReturnParameters(theMemberPatient, theCoverageToLink); - } - - - private void validateParams(Patient theMemberPatient, Coverage theOldCoverage, Coverage theNewCoverage) { - validateParam(theMemberPatient, Constants.PARAM_MEMBER_PATIENT); - validateParam(theOldCoverage, Constants.PARAM_OLD_COVERAGE); - validateParam(theNewCoverage, Constants.PARAM_NEW_COVERAGE); - validateMemberPatientParam(theMemberPatient); - } - - private void validateParam(Object theParam, String theParamName) { - if (theParam == null) { - String i18nMessage = getContext().getLocalizer().getMessage( - "operation.member.match.error.missing.parameter", theParamName); - throw new UnprocessableEntityException(Msg.code(1158) + i18nMessage); - } - } - - private void validateMemberPatientParam(Patient theMemberPatient) { - if (theMemberPatient.getName().isEmpty()) { - validateParam(null, Constants.PARAM_MEMBER_PATIENT_NAME); - } - - validateParam(theMemberPatient.getName().get(0).getFamily(), Constants.PARAM_MEMBER_PATIENT_NAME); - validateParam(theMemberPatient.getBirthDate(), Constants.PARAM_MEMBER_PATIENT_BIRTHDATE); - } - - /** - * Given a list of string types, return only the ID portions of any parameters passed in. - */ - private TokenOrListParam toFlattenedPatientIdTokenParamList(List theId) { - TokenOrListParam retVal = new TokenOrListParam(); - if (theId != null) { - for (IdType next: theId) { - if (isNotBlank(next.getValue())) { - String[] split = next.getValueAsString().split(","); - Arrays.stream(split).map(IdType::new).forEach(id -> { - retVal.addOr(new TokenParam(id.getIdPart())); - }); - } - } - } - return retVal.getValuesAsQueryTokens().isEmpty() ? null: retVal; - } - - private StringAndListParam toStringAndList(List theNarrative) { - StringAndListParam retVal = new StringAndListParam(); - if (theNarrative != null) { - for (StringType next : theNarrative) { - if (isNotBlank(next.getValue())) { - retVal.addAnd(new StringOrListParam().addOr(new StringParam(next.getValue()))); - } - } - } - if (retVal.getValuesAsQueryTokens().isEmpty()) { - return null; - } - return retVal; - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderStructureDefinitionR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderStructureDefinitionR4.java deleted file mode 100644 index f72ff266bb9..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderStructureDefinitionR4.java +++ /dev/null @@ -1,82 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoStructureDefinition; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.UriParam; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.util.ValidateUtil; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.StructureDefinition; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderStructureDefinitionR4 extends JpaResourceProviderR4 { - - /** - * $snapshot operation - */ - @Operation(name=JpaConstants.OPERATION_SNAPSHOT, idempotent = true) - public StructureDefinition snapshot( - @IdParam(optional = true) IdType theId, - @OperationParam(name = "definition") StructureDefinition theStructureDefinition, - @OperationParam(name = "url") StringType theUrl, - RequestDetails theRequestDetails) { - - ValidateUtil.exactlyOneNotNullOrThrowInvalidRequestException( - new Object[]{ theId, theStructureDefinition, theUrl }, - "Must supply either an ID or a StructureDefinition or a URL (but not more than one of these things)" - ); - - StructureDefinition sd; - if (theId == null && theStructureDefinition != null && theUrl == null) { - sd = theStructureDefinition; - } else if (theId != null && theStructureDefinition == null) { - sd = getDao().read(theId, theRequestDetails); - } else { - SearchParameterMap map = new SearchParameterMap(); - map.setLoadSynchronousUpTo(2); - map.add(StructureDefinition.SP_URL, new UriParam(theUrl.getValue())); - IBundleProvider outcome = getDao().search(map, theRequestDetails); - Integer numResults = outcome.size(); - assert numResults != null; - if (numResults == 0) { - throw new ResourceNotFoundException(Msg.code(1159) + "No StructureDefiniton found with url = '" + theUrl.getValue() + "'"); - } - sd = (StructureDefinition) outcome.getResources(0, 1).get(0); - } - - return getDao().generateSnapshot(sd, null, null, null); - } - - @Override - public IFhirResourceDaoStructureDefinition getDao() { - return (IFhirResourceDaoStructureDefinition) super.getDao(); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java index 1d6ba41b178..48a44abb95b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java @@ -20,8 +20,9 @@ package ca.uhn.fhir.jpa.provider.r4; * #L% */ +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import org.hl7.fhir.r4.model.ValueSet; -public abstract class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4 { +public abstract class BaseJpaResourceProviderValueSetR4 extends BaseJpaResourceProvider { } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaResourceProviderR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaResourceProviderR4.java deleted file mode 100644 index bc491f16245..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaResourceProviderR4.java +++ /dev/null @@ -1,37 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; -import org.hl7.fhir.instance.model.api.IAnyResource; - -public class JpaResourceProviderR4 extends BaseJpaResourceProvider { - - public JpaResourceProviderR4() { - // nothing - } - - public JpaResourceProviderR4(IFhirResourceDao theDao) { - super(theDao); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaSystemProviderR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaSystemProviderR4.java deleted file mode 100644 index 344834dc682..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/JpaSystemProviderR4.java +++ /dev/null @@ -1,182 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Transaction; -import ca.uhn.fhir.rest.annotation.TransactionParam; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.provider.ProviderConstants; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.IntegerType; -import org.hl7.fhir.r4.model.Meta; -import org.hl7.fhir.r4.model.Parameters; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; - -import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public class JpaSystemProviderR4 extends BaseJpaSystemProviderDstu2Plus { - - @Autowired() - @Qualifier("mySystemDaoR4") - private IFhirSystemDao mySystemDao; - - // This is generated by hand: - // ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/" - @Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true, returnParameters = { - @OperationParam(name = "AllergyIntolerance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Appointment", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "AppointmentResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "AuditEvent", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Basic", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Binary", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "BodySite", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Bundle", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "CarePlan", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "CarePlan2", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Claim", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ClaimResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ClinicalImpression", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Communication", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "CommunicationRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Composition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ConceptMap", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Condition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Conformance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Contract", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Contraindication", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Coverage", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DataElement", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Device", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceComponent", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceMetric", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceUseRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceUseStatement", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DiagnosticOrder", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DiagnosticReport", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DocumentManifest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DocumentReference", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EligibilityRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EligibilityResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Encounter", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EnrollmentRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EnrollmentResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EpisodeOfCare", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ExplanationOfBenefit", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "FamilyMemberHistory", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Flag", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Goal", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Group", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "HealthcareService", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ImagingObjectSelection", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ImagingStudy", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Immunization", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ImmunizationRecommendation", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ListResource", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Location", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Media", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Medication", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationAdministration", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationDispense", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationPrescription", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationStatement", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MessageHeader", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "NamingSystem", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "NutritionOrder", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Observation", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "OperationDefinition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "OperationOutcome", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Order", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "OrderResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Organization", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Parameters", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Patient", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "PaymentNotice", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "PaymentReconciliation", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Person", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Practitioner", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Procedure", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ProcedureRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ProcessRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ProcessResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Provenance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Questionnaire", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "QuestionnaireAnswers", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ReferralRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "RelatedPerson", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "RiskAssessment", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Schedule", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "SearchParameter", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Slot", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Specimen", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "StructureDefinition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Subscription", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Substance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Supply", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ValueSet", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "VisionPrescription", type = IntegerType.class, min = 0, max = 1) - }) - @Description(shortDefinition = "Provides the number of resources currently stored on the server, broken down by resource type") - public Parameters getResourceCounts() { - Parameters retVal = new Parameters(); - - Map counts = mySystemDao.getResourceCountsFromCache(); - counts = defaultIfNull(counts, Collections.emptyMap()); - counts = new TreeMap<>(counts); - for (Entry nextEntry : counts.entrySet()) { - retVal.addParameter().setName((nextEntry.getKey())).setValue(new IntegerType(nextEntry.getValue().intValue())); - } - - return retVal; - } - - @Operation(name = ProviderConstants.OPERATION_META, idempotent = true, returnParameters = { - @OperationParam(name = "return", type = Meta.class) - }) - public Parameters meta(RequestDetails theRequestDetails) { - Parameters parameters = new Parameters(); - parameters.addParameter().setName("return").setValue(getDao().metaGetOperation(theRequestDetails)); - return parameters; - } - - @Transaction - public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) { - startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); - try { - return getDao().transaction(theRequestDetails, theResources); - } finally { - endRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); - } - } - -} 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 new file mode 100644 index 00000000000..ae0157ae2c6 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java @@ -0,0 +1,139 @@ +package ca.uhn.fhir.jpa.provider.r4; + +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +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 javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +public class MemberMatchR4ResourceProvider { + + private final MemberMatcherR4Helper myMemberMatcherR4Helper; + private final FhirContext myFhirContext; + + public MemberMatchR4ResourceProvider(FhirContext theFhirContext, MemberMatcherR4Helper theMemberMatcherR4Helper) { + myFhirContext = theFhirContext; + myMemberMatcherR4Helper = theMemberMatcherR4Helper; + } + + /** + * /Patient/$member-match operation + * Basic implementation matching by coverage id or by coverage identifier. Matching by + * Beneficiary (Patient) demographics on family name and birthdate in this version + */ + @Operation(name = ProviderConstants.OPERATION_MEMBER_MATCH, typeName = "Patient", canonicalUrl = "http://hl7.org/fhir/us/davinci-hrex/OperationDefinition/member-match", idempotent = false, returnParameters = { + @OperationParam(name = "MemberIdentifier", typeName = "string") + }) + public Parameters patientMemberMatch( + javax.servlet.http.HttpServletRequest theServletRequest, + + @Description(shortDefinition = "The target of the operation. Will be returned with Identifier for matched coverage added.") + @OperationParam(name = Constants.PARAM_MEMBER_PATIENT, min = 1, max = 1) + Patient theMemberPatient, + + @Description(shortDefinition = "Old coverage information as extracted from beneficiary's card.") + @OperationParam(name = Constants.PARAM_OLD_COVERAGE, min = 1, max = 1) + Coverage oldCoverage, + + @Description(shortDefinition = "New Coverage information. Provided as a reference. Optionally returned unmodified.") + @OperationParam(name = Constants.PARAM_NEW_COVERAGE, min = 1, max = 1) + Coverage newCoverage, + + RequestDetails theRequestDetails + ) { + return doMemberMatchOperation(theServletRequest, theMemberPatient, oldCoverage, newCoverage, theRequestDetails); + } + + + private Parameters doMemberMatchOperation(HttpServletRequest theServletRequest, Patient theMemberPatient, + Coverage theCoverageToMatch, Coverage theCoverageToLink, RequestDetails theRequestDetails) { + + validateParams(theMemberPatient, theCoverageToMatch, theCoverageToLink); + + Optional coverageOpt = myMemberMatcherR4Helper.findMatchingCoverage(theCoverageToMatch); + if ( ! coverageOpt.isPresent()) { + String i18nMessage = myFhirContext.getLocalizer().getMessage( + "operation.member.match.error.coverage.not.found"); + throw new UnprocessableEntityException(Msg.code(1155) + i18nMessage); + } + Coverage coverage = coverageOpt.get(); + + Optional patientOpt = myMemberMatcherR4Helper.getBeneficiaryPatient(coverage); + if (! patientOpt.isPresent()) { + String i18nMessage = myFhirContext.getLocalizer().getMessage( + "operation.member.match.error.beneficiary.not.found"); + throw new UnprocessableEntityException(Msg.code(1156) + i18nMessage); + } + + Patient patient = patientOpt.get(); + if (!myMemberMatcherR4Helper.validPatientMember(patient, theMemberPatient)) { + String i18nMessage = myFhirContext.getLocalizer().getMessage( + "operation.member.match.error.patient.not.found"); + throw new UnprocessableEntityException(Msg.code(2146) + i18nMessage); + } + + if (patient.getIdentifier().isEmpty()) { + String i18nMessage = myFhirContext.getLocalizer().getMessage( + "operation.member.match.error.beneficiary.without.identifier"); + throw new UnprocessableEntityException(Msg.code(1157) + i18nMessage); + } + + myMemberMatcherR4Helper.addMemberIdentifierToMemberPatient(theMemberPatient, patient.getIdentifierFirstRep()); + + return myMemberMatcherR4Helper.buildSuccessReturnParameters(theMemberPatient, theCoverageToLink); + } + + private void validateParams(Patient theMemberPatient, Coverage theOldCoverage, Coverage theNewCoverage) { + validateParam(theMemberPatient, Constants.PARAM_MEMBER_PATIENT); + validateParam(theOldCoverage, Constants.PARAM_OLD_COVERAGE); + validateParam(theNewCoverage, Constants.PARAM_NEW_COVERAGE); + validateMemberPatientParam(theMemberPatient); + } + + private void validateParam(Object theParam, String theParamName) { + if (theParam == null) { + String i18nMessage = myFhirContext.getLocalizer().getMessage( + "operation.member.match.error.missing.parameter", theParamName); + throw new UnprocessableEntityException(Msg.code(1158) + i18nMessage); + } + } + + private void validateMemberPatientParam(Patient theMemberPatient) { + if (theMemberPatient.getName().isEmpty()) { + validateParam(null, Constants.PARAM_MEMBER_PATIENT_NAME); + } + + validateParam(theMemberPatient.getName().get(0).getFamily(), Constants.PARAM_MEMBER_PATIENT_NAME); + validateParam(theMemberPatient.getBirthDate(), Constants.PARAM_MEMBER_PATIENT_BIRTHDATE); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderStructureDefinitionDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4b/BaseJpaResourceProviderConceptMapR4B.java similarity index 73% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderStructureDefinitionDstu2.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4b/BaseJpaResourceProviderConceptMapR4B.java index 6026d5bea8f..d33f15093ed 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderStructureDefinitionDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4b/BaseJpaResourceProviderConceptMapR4B.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.provider; +package ca.uhn.fhir.jpa.provider.r4b; /* * #%L @@ -20,10 +20,8 @@ package ca.uhn.fhir.jpa.provider; * #L% */ -import ca.uhn.fhir.model.dstu2.resource.StructureDefinition; - -public abstract class BaseJpaResourceProviderStructureDefinitionDstu2 extends JpaResourceProviderDstu2 { - - // nothing yet +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; +import org.hl7.fhir.r4b.model.ConceptMap; +public abstract class BaseJpaResourceProviderConceptMapR4B extends BaseJpaResourceProvider { } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderMessageHeaderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4b/BaseJpaResourceProviderValueSetR4B.java similarity index 73% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderMessageHeaderDstu3.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4b/BaseJpaResourceProviderValueSetR4B.java index 0b174155f83..9272c02784f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderMessageHeaderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4b/BaseJpaResourceProviderValueSetR4B.java @@ -1,6 +1,4 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import org.hl7.fhir.dstu3.model.MessageHeader; +package ca.uhn.fhir.jpa.provider.r4b; /* * #%L @@ -22,6 +20,9 @@ import org.hl7.fhir.dstu3.model.MessageHeader; * #L% */ -public abstract class BaseJpaResourceProviderMessageHeaderDstu3 extends JpaResourceProviderDstu3 { - // nothing now +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; +import org.hl7.fhir.r4b.model.ValueSet; + +public abstract class BaseJpaResourceProviderValueSetR4B extends BaseJpaResourceProvider { + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java deleted file mode 100644 index 1a5860cd1a5..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ /dev/null @@ -1,146 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r5; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderValueSetDstu2; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import org.hl7.fhir.r5.model.BooleanType; -import org.hl7.fhir.r5.model.CodeSystem; -import org.hl7.fhir.r5.model.CodeType; -import org.hl7.fhir.r5.model.CodeableConcept; -import org.hl7.fhir.r5.model.Coding; -import org.hl7.fhir.r5.model.IdType; -import org.hl7.fhir.r5.model.Parameters; -import org.hl7.fhir.r5.model.StringType; -import org.hl7.fhir.r5.model.UriType; - -import javax.servlet.http.HttpServletRequest; -import java.util.List; - -public abstract class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 { - - /** - * $lookup operation - */ - @SuppressWarnings("unchecked") - @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= { - @OperationParam(name="name", type=StringType.class, min=1), - @OperationParam(name="version", type=StringType.class, min=0), - @OperationParam(name="display", type=StringType.class, min=1), - @OperationParam(name="abstract", type=BooleanType.class, min=1), - }) - public Parameters lookup( - HttpServletRequest theServletRequest, - @OperationParam(name="code", min=0, max=1) CodeType theCode, - @OperationParam(name="system", min=0, max=1) UriType theSystem, - @OperationParam(name="coding", min=0, max=1) Coding theCoding, - @OperationParam(name="version", min=0, max=1) StringType theVersion, - @OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage, - @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties, - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IValidationSupport.LookupCodeResult result; - if (theVersion != null) { - result = dao.lookupCode(theCode, new UriType(theSystem.getValue() + "|" + theVersion), theCoding, theDisplayLanguage, theRequestDetails); - } else { - result = dao.lookupCode(theCode, theSystem, theCoding, theDisplayLanguage, theRequestDetails); - } - result.throwNotFoundIfAppropriate(); - return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); - } finally { - endRequest(theServletRequest); - } - } - - - /** - * $subsumes operation - */ - @Operation(name = JpaConstants.OPERATION_SUBSUMES, idempotent = true, returnParameters= { - @OperationParam(name="outcome", type=CodeType.class, min=1), - }) - public Parameters subsumes( - HttpServletRequest theServletRequest, - @OperationParam(name="codeA", min=0, max=1) CodeType theCodeA, - @OperationParam(name="codeB", min=0, max=1) CodeType theCodeB, - @OperationParam(name="system", min=0, max=1) UriType theSystem, - @OperationParam(name="codingA", min=0, max=1) Coding theCodingA, - @OperationParam(name="codingB", min=0, max=1) Coding theCodingB, - @OperationParam(name="version", min=0, max=1) StringType theVersion, - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IFhirResourceDaoCodeSystem.SubsumesResult result; - if (theVersion != null) { - theSystem = new UriType(theSystem.asStringValue() + "|" + theVersion.toString()); - } - result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); - return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); - } finally { - endRequest(theServletRequest); - } - } - - /** - * $validate-code operation - */ - @SuppressWarnings("unchecked") - @Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = { - @OperationParam(name = "result", type = BooleanType.class, min = 1), - @OperationParam(name = "message", type = StringType.class), - @OperationParam(name = "display", type = StringType.class) - }) - public Parameters validateCode( - HttpServletRequest theServletRequest, - @IdParam(optional = true) IdType theId, - @OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl, - @OperationParam(name = "version", min = 0, max = 1) StringType theVersion, - @OperationParam(name = "code", min = 0, max = 1) CodeType theCode, - @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay, - @OperationParam(name = "coding", min = 0, max = 1) Coding theCoding, - @OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theCodeableConcept, - RequestDetails theRequestDetails - ) { - - startRequest(theServletRequest); - try { - IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - - IValidationSupport.CodeValidationResult result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails); - return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result); - } finally { - endRequest(theServletRequest); - } - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderConceptMapR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderConceptMapR5.java index 085fb7c27f3..7fb88361ff5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderConceptMapR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderConceptMapR5.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap; import ca.uhn.fhir.jpa.api.model.TranslationRequest; import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; @@ -45,7 +46,7 @@ import org.hl7.fhir.r5.model.UriType; import javax.servlet.http.HttpServletRequest; -public abstract class BaseJpaResourceProviderConceptMapR5 extends JpaResourceProviderR5 { +public abstract class BaseJpaResourceProviderConceptMapR5 extends BaseJpaResourceProvider { @Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1), @OperationParam(name = "message", type = StringType.class, min = 0, max = 1), diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderEncounterR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderEncounterR5.java deleted file mode 100644 index 7c8ecc9123d..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderEncounterR5.java +++ /dev/null @@ -1,108 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r5; - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoEncounter; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.DateRangeParam; -import org.hl7.fhir.r5.model.Encounter; -import org.hl7.fhir.r5.model.IdType; -import org.hl7.fhir.r5.model.UnsignedIntType; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderEncounterR5 extends JpaResourceProviderR5 { - - /** - * Encounter/123/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET) - public IBundleProvider EncounterInstanceEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @IdParam - IdType theId, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Sort - SortSpec theSortSpec - ) { - - startRequest(theServletRequest); - try { - return ((IFhirResourceDaoEncounter)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec); - } finally { - endRequest(theServletRequest); - }} - - /** - * /Encounter/$everything - */ - @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET) - public IBundleProvider EncounterTypeEverything( - - javax.servlet.http.HttpServletRequest theServletRequest, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.") - @OperationParam(name = Constants.PARAM_COUNT) - UnsignedIntType theCount, - - @Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") - @OperationParam(name = Constants.PARAM_OFFSET) - UnsignedIntType theOffset, - - @Description(shortDefinition="Only return resources which were last updated as specified by the given range") - @OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1) - DateRangeParam theLastUpdated, - - @Sort - SortSpec theSortSpec - ) { - - startRequest(theServletRequest); - try { - return ((IFhirResourceDaoEncounter)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec); - } finally { - endRequest(theServletRequest); - } - - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderMessageHeaderR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderMessageHeaderR5.java deleted file mode 100644 index 18fef7cbd42..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderMessageHeaderR5.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r5; - -import org.hl7.fhir.r5.model.MessageHeader; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public abstract class BaseJpaResourceProviderMessageHeaderR5 extends JpaResourceProviderR5 { - // nothing right now -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java index 9b4aa94cf70..ffcb58a3c8a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java @@ -20,8 +20,9 @@ package ca.uhn.fhir.jpa.provider.r5; * #L% */ +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import org.hl7.fhir.r5.model.ValueSet; -public abstract class BaseJpaResourceProviderValueSetR5 extends JpaResourceProviderR5 { +public abstract class BaseJpaResourceProviderValueSetR5 extends BaseJpaResourceProvider { // nothing } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java deleted file mode 100644 index 9d6b9b8e802..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java +++ /dev/null @@ -1,37 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r5; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; -import org.hl7.fhir.instance.model.api.IAnyResource; - -public class JpaResourceProviderR5 extends BaseJpaResourceProvider { - - public JpaResourceProviderR5() { - // nothing - } - - public JpaResourceProviderR5(IFhirResourceDao theDao) { - super(theDao); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java deleted file mode 100644 index 1b3da4ca4cc..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java +++ /dev/null @@ -1,182 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r5; - -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Transaction; -import ca.uhn.fhir.rest.annotation.TransactionParam; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.provider.ProviderConstants; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import org.hl7.fhir.r5.model.Bundle; -import org.hl7.fhir.r5.model.IntegerType; -import org.hl7.fhir.r5.model.Meta; -import org.hl7.fhir.r5.model.Parameters; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; - -import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public class JpaSystemProviderR5 extends BaseJpaSystemProviderDstu2Plus { - - @Autowired() - @Qualifier("mySystemDaoR5") - private IFhirSystemDao mySystemDao; - - // This is generated by hand: - // ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/" - @Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true, returnParameters = { - @OperationParam(name = "AllergyIntolerance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Appointment", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "AppointmentResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "AuditEvent", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Basic", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Binary", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "BodySite", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Bundle", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "CarePlan", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "CarePlan2", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Claim", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ClaimResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ClinicalImpression", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Communication", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "CommunicationRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Composition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ConceptMap", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Condition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Conformance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Contract", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Contraindication", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Coverage", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DataElement", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Device", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceComponent", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceMetric", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceUseRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DeviceUseStatement", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DiagnosticOrder", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DiagnosticReport", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DocumentManifest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "DocumentReference", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EligibilityRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EligibilityResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Encounter", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EnrollmentRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EnrollmentResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "EpisodeOfCare", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ExplanationOfBenefit", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "FamilyMemberHistory", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Flag", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Goal", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Group", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "HealthcareService", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ImagingObjectSelection", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ImagingStudy", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Immunization", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ImmunizationRecommendation", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ListResource", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Location", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Media", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Medication", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationAdministration", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationDispense", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationPrescription", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MedicationStatement", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "MessageHeader", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "NamingSystem", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "NutritionOrder", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Observation", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "OperationDefinition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "OperationOutcome", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Order", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "OrderResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Organization", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Parameters", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Patient", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "PaymentNotice", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "PaymentReconciliation", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Person", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Practitioner", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Procedure", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ProcedureRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ProcessRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ProcessResponse", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Provenance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Questionnaire", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "QuestionnaireAnswers", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ReferralRequest", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "RelatedPerson", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "RiskAssessment", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Schedule", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "SearchParameter", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Slot", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Specimen", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "StructureDefinition", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Subscription", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Substance", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "Supply", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "ValueSet", type = IntegerType.class, min = 0, max = 1), - @OperationParam(name = "VisionPrescription", type = IntegerType.class, min = 0, max = 1) - }) - @Description(shortDefinition = "Provides the number of resources currently stored on the server, broken down by resource type") - public Parameters getResourceCounts() { - Parameters retVal = new Parameters(); - - Map counts = mySystemDao.getResourceCountsFromCache(); - counts = defaultIfNull(counts, Collections.emptyMap()); - counts = new TreeMap<>(counts); - for (Entry nextEntry : counts.entrySet()) { - retVal.addParameter().setName((nextEntry.getKey())).setValue(new IntegerType(nextEntry.getValue().intValue())); - } - - return retVal; - } - - @Operation(name = ProviderConstants.OPERATION_META, idempotent = true, returnParameters = { - @OperationParam(name = "return", type = Meta.class) - }) - public Parameters meta(RequestDetails theRequestDetails) { - Parameters parameters = new Parameters(); - parameters.addParameter().setName("return").setValue(getDao().metaGetOperation(theRequestDetails)); - return parameters; - } - - @Transaction - public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) { - startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); - try { - return getDao().transaction(theRequestDetails, theResources); - } finally { - endRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); - } - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 223a5b14945..0fe454704db 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -301,7 +301,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { populateCodeSystemVersionProperties(persCs, theCodeSystem, theResourceEntity); - persCs.getConcepts().addAll(BaseTermReadSvcImpl.toPersistedConcepts(theCodeSystem.getConcept(), persCs)); + persCs.getConcepts().addAll(TermReadSvcImpl.toPersistedConcepts(theCodeSystem.getConcept(), persCs)); ourLog.debug("Code system has {} concepts", persCs.getConcepts().size()); storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, theCodeSystem.getName(), theCodeSystem.getVersion(), persCs, theResourceEntity, theRequestDetails); @@ -654,13 +654,13 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { // Check if a non-versioned TermCodeSystemVersion entity already exists for this TermCodeSystem. codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidVersionIsNull(theCodeSystem.getPid()); if (codeSystemVersionEntity != null) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); } } else { // Check if a TermCodeSystemVersion entity already exists for this TermCodeSystem and version. codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystem.getPid(), theSystemVersionId); if (codeSystemVersionEntity != null) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); } } // Throw exception if the TermCodeSystemVersion is being duplicated. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java index f4efa4546b4..04beeed8a0d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java @@ -47,11 +47,8 @@ import org.apache.commons.lang3.StringUtils; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.CodeType; -import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.IdType; @@ -81,7 +78,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import static ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.isPlaceholder; +import static ca.uhn.fhir.jpa.term.TermReadSvcImpl.isPlaceholder; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -90,7 +87,7 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { private static final Logger ourLog = LoggerFactory.getLogger(TermConceptMappingSvcImpl.class); private static boolean ourLastResultsFromTranslationCache; // For testing. private static boolean ourLastResultsFromTranslationWithReverseCache; // For testing. - private final int myFetchSize = BaseTermReadSvcImpl.DEFAULT_FETCH_SIZE; + private final int myFetchSize = TermReadSvcImpl.DEFAULT_FETCH_SIZE; @Autowired protected ITermConceptMapDao myConceptMapDao; @Autowired @@ -266,7 +263,7 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { if (isBlank(conceptMapVersion)) { String msg = myContext.getLocalizer().getMessage( - BaseTermReadSvcImpl.class, + TermReadSvcImpl.class, "cannotCreateDuplicateConceptMapUrl", conceptMapUrl, existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue()); @@ -274,7 +271,7 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { } else { String msg = myContext.getLocalizer().getMessage( - BaseTermReadSvcImpl.class, + TermReadSvcImpl.class, "cannotCreateDuplicateConceptMapUrlAndVersion", conceptMapUrl, conceptMapVersion, existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java index c00c11e094d..c6ea77a2730 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java @@ -495,7 +495,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { CodeSystem imgthlaCs; try { - String imgthlaCsString = IOUtils.toString(BaseTermReadSvcImpl.class.getResourceAsStream("/ca/uhn/fhir/jpa/term/imgthla/imgthla.xml"), Charsets.UTF_8); + String imgthlaCsString = IOUtils.toString(TermReadSvcImpl.class.getResourceAsStream("/ca/uhn/fhir/jpa/term/imgthla/imgthla.xml"), Charsets.UTF_8); imgthlaCs = FhirContext.forR4().newXmlParser().parseResource(CodeSystem.class, imgthlaCsString); } catch (IOException e) { throw new InternalErrorException(Msg.code(869) + "Failed to load imgthla.xml", e); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java deleted file mode 100644 index 978f953d129..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java +++ /dev/null @@ -1,182 +0,0 @@ -package ca.uhn.fhir.jpa.term; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.context.support.ConceptValidationOptions; -import ca.uhn.fhir.context.support.ValueSetExpansionOptions; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; -import ca.uhn.fhir.model.dstu2.composite.CodingDt; -import ca.uhn.fhir.util.FhirVersionIndependentConcept; -import org.hl7.fhir.instance.model.api.IBaseCoding; -import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.ValueSet; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { - - private void addAllChildren(String theSystemString, ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept theCode, List theListToPopulate) { - if (isNotBlank(theCode.getCode())) { - theListToPopulate.add(new FhirVersionIndependentConcept(theSystemString, theCode.getCode())); - } - for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept nextChild : theCode.getConcept()) { - addAllChildren(theSystemString, nextChild, theListToPopulate); - } - } - - private boolean addTreeIfItContainsCode(String theSystemString, ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept theNext, String theCode, List theListToPopulate) { - boolean foundCodeInChild = false; - for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept nextChild : theNext.getConcept()) { - foundCodeInChild |= addTreeIfItContainsCode(theSystemString, nextChild, theCode, theListToPopulate); - } - - if (theCode.equals(theNext.getCode()) || foundCodeInChild) { - theListToPopulate.add(new FhirVersionIndependentConcept(theSystemString, theNext.getCode())); - return true; - } - - return false; - } - - @Override - protected ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) { - throw new UnsupportedOperationException(Msg.code(852)); - } - - @Override - protected ValueSet toCanonicalValueSet(IBaseResource theValueSet) { - throw new UnsupportedOperationException(Msg.code(853)); - } - - @Override - protected CodeSystem toCanonicalCodeSystem(IBaseResource theCodeSystem) { - throw new UnsupportedOperationException(Msg.code(854)); - } - - @Override - public IBaseResource expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand) { - throw new UnsupportedOperationException(Msg.code(855)); - } - - @Override - public void expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) { - throw new UnsupportedOperationException(Msg.code(856)); - } - - private void findCodesAbove(ca.uhn.fhir.model.dstu2.resource.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { - List conceptList = theSystem.getCodeSystem().getConcept(); - for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept next : conceptList) { - addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate); - } - } - - @Override - public List findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) { - ArrayList retVal = new ArrayList<>(); - ca.uhn.fhir.model.dstu2.resource.ValueSet system = (ca.uhn.fhir.model.dstu2.resource.ValueSet) provideValidationSupport().fetchCodeSystem(theSystem); - if (system != null) { - findCodesAbove(system, theSystem, theCode, retVal); - } - return retVal; - } - - private void findCodesBelow(ca.uhn.fhir.model.dstu2.resource.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { - List conceptList = theSystem.getCodeSystem().getConcept(); - findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList); - } - - private void findCodesBelow(String theSystemString, String theCode, List theListToPopulate, List conceptList) { - for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept next : conceptList) { - if (theCode.equals(next.getCode())) { - addAllChildren(theSystemString, next, theListToPopulate); - } else { - findCodesBelow(theSystemString, theCode, theListToPopulate, next.getConcept()); - } - } - } - - @Override - public List findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) { - ArrayList retVal = new ArrayList<>(); - ca.uhn.fhir.model.dstu2.resource.ValueSet system = (ca.uhn.fhir.model.dstu2.resource.ValueSet) provideValidationSupport().fetchCodeSystem(theSystem); - if (system != null) { - findCodesBelow(system, theSystem, theCode, retVal); - } - return retVal; - } - - @Nullable - @Override - protected Coding toCanonicalCoding(@Nullable IBaseDatatype theCoding) { - Coding retVal = null; - if (theCoding != null) { - CodingDt coding = (CodingDt) theCoding; - retVal = new Coding(coding.getSystem(), coding.getCode(), coding.getDisplay()); - } - return retVal; - } - - @Nullable - @Override - protected Coding toCanonicalCoding(@Nullable IBaseCoding theCoding) { - Coding retVal = null; - if (theCoding != null) { - CodingDt coding = (CodingDt) theCoding; - retVal = new Coding(coding.getSystem(), coding.getCode(), coding.getDisplay()); - } - return retVal; - } - - @Nullable - @Override - protected CodeableConcept toCanonicalCodeableConcept(@Nullable IBaseDatatype theCodeableConcept) { - CodeableConcept outcome = null; - if (theCodeableConcept != null) { - outcome = new CodeableConcept(); - CodeableConceptDt cc = (CodeableConceptDt) theCodeableConcept; - outcome.setText(cc.getText()); - for (CodingDt next : cc.getCoding()) { - outcome.addCoding(toCanonicalCoding((IBaseDatatype)next)); - } - } - return outcome; - } - - @Override - public CodeValidationResult validateCodeIsInPreExpandedValueSet(ConceptValidationOptions theOptions, IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { - throw new UnsupportedOperationException(Msg.code(857)); - } - - @Override - public boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet) { - throw new UnsupportedOperationException(Msg.code(858)); - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java deleted file mode 100644 index e1e1f36fa69..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java +++ /dev/null @@ -1,191 +0,0 @@ -package ca.uhn.fhir.jpa.term; - -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.support.ConceptValidationOptions; -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.context.support.ValidationSupportContext; -import ca.uhn.fhir.context.support.ValueSetExpansionOptions; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3; -import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.util.ValidateUtil; -import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40; -import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40; -import org.hl7.fhir.dstu3.model.CodeSystem; -import org.hl7.fhir.dstu3.model.CodeableConcept; -import org.hl7.fhir.dstu3.model.Coding; -import org.hl7.fhir.dstu3.model.ValueSet; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBaseCoding; -import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidationSupport, ITermReadSvcDstu3 { - - @Autowired - private PlatformTransactionManager myTransactionManager; - - /** - * Constructor - */ - public TermReadSvcDstu3() { - super(); - } - - - - @Override - public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) { - try { - org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4; - valueSetToExpandR4 = toCanonicalValueSet(theValueSetToExpand); - org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(theExpansionOptions, valueSetToExpandR4); - return new ValueSetExpansionOutcome(VersionConvertorFactory_30_40.convertResource(expandedR4, new BaseAdvisor_30_40(false))); - } catch (FHIRException e) { - throw new InternalErrorException(Msg.code(833) + e); - } - } - - @Override - public IBaseResource expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theInput) { - ValueSet valueSetToExpand = (ValueSet) theInput; - - try { - org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4; - valueSetToExpandR4 = toCanonicalValueSet(valueSetToExpand); - org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(theExpansionOptions, valueSetToExpandR4); - return VersionConvertorFactory_30_40.convertResource(expandedR4, new BaseAdvisor_30_40(false)); - } catch (FHIRException e) { - throw new InternalErrorException(Msg.code(834) + e); - } - } - - @Override - protected org.hl7.fhir.r4.model.ValueSet toCanonicalValueSet(IBaseResource theValueSet) throws FHIRException { - org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4; - valueSetToExpandR4 = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_30_40.convertResource((ValueSet) theValueSet, new BaseAdvisor_30_40(false)); - return valueSetToExpandR4; - } - - @Override - protected org.hl7.fhir.r4.model.CodeSystem toCanonicalCodeSystem(IBaseResource theCodeSystem) { - return (org.hl7.fhir.r4.model.CodeSystem) VersionConvertorFactory_30_40.convertResource((CodeSystem)theCodeSystem, new BaseAdvisor_30_40(false)); - } - - @Override - @Nullable - protected org.hl7.fhir.r4.model.Coding toCanonicalCoding(IBaseDatatype theCoding) { - return (org.hl7.fhir.r4.model.Coding) VersionConvertorFactory_30_40.convertType((Coding) theCoding, new BaseAdvisor_30_40(false)); - } - - @Override - @Nullable - protected org.hl7.fhir.r4.model.Coding toCanonicalCoding(IBaseCoding theCoding) { - return (org.hl7.fhir.r4.model.Coding) VersionConvertorFactory_30_40.convertType((org.hl7.fhir.dstu3.model.Coding) theCoding, new BaseAdvisor_30_40(false)); - } - - @Override - @Nullable - protected org.hl7.fhir.r4.model.CodeableConcept toCanonicalCodeableConcept(IBaseDatatype theCoding) { - return (org.hl7.fhir.r4.model.CodeableConcept) VersionConvertorFactory_30_40.convertType((CodeableConcept) theCoding, new BaseAdvisor_30_40(false)); - } - - - - @Override - public void expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) { - ValueSet valueSetToExpand = (ValueSet) theValueSetToExpand; - - try { - org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4; - valueSetToExpandR4 = toCanonicalValueSet(valueSetToExpand); - super.expandValueSet(theExpansionOptions, valueSetToExpandR4, theValueSetCodeAccumulator); - } catch (FHIRException e) { - throw new InternalErrorException(Msg.code(835) + e); - } - } - - @Override - protected org.hl7.fhir.r4.model.ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) { - ValueSet valueSet = myDaoRegistry.getResourceDao("ValueSet").toResource(ValueSet.class, theResourceTable, null, false); - - org.hl7.fhir.r4.model.ValueSet valueSetR4; - try { - valueSetR4 = toCanonicalValueSet(valueSet); - } catch (FHIRException e) { - throw new InternalErrorException(Msg.code(836) + e); - } - - return valueSetR4; - } - - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { - return super.lookupCode(theSystem, theCode, theDisplayLanguage); - } - - @Override - public FhirContext getFhirContext() { - return myContext; - } - - @Override - public IValidationSupport.CodeValidationResult validateCodeIsInPreExpandedValueSet(ConceptValidationOptions theOptions, IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { - ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null"); - ValueSet valueSet = (ValueSet) theValueSet; - org.hl7.fhir.r4.model.ValueSet valueSetR4 = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_30_40.convertResource(valueSet, new BaseAdvisor_30_40(false)); - - Coding coding = (Coding) theCoding; - org.hl7.fhir.r4.model.Coding codingR4 = null; - if (coding != null) { - codingR4 = new org.hl7.fhir.r4.model.Coding(coding.getSystem(), coding.getCode(), coding.getDisplay()); - } - - CodeableConcept codeableConcept = (CodeableConcept) theCodeableConcept; - org.hl7.fhir.r4.model.CodeableConcept codeableConceptR4 = null; - if (codeableConcept != null) { - codeableConceptR4 = new org.hl7.fhir.r4.model.CodeableConcept(); - for (Coding nestedCoding : codeableConcept.getCoding()) { - codeableConceptR4.addCoding(new org.hl7.fhir.r4.model.Coding(nestedCoding.getSystem(), nestedCoding.getCode(), nestedCoding.getDisplay())); - } - } - - return super.validateCodeIsInPreExpandedValueSet(theOptions, valueSetR4, theSystem, theCode, theDisplay, codingR4, codeableConceptR4); - } - - @Override - public boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet) { - ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null"); - ValueSet valueSet = (ValueSet) theValueSet; - org.hl7.fhir.r4.model.ValueSet valueSetR4 = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_30_40.convertResource(valueSet, new BaseAdvisor_30_40(false)); - return super.isValueSetPreExpandedForCodeValidation(valueSetR4); - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java similarity index 95% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java index 2edcb3db62f..0db16fb0d33 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java @@ -85,6 +85,7 @@ import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; +import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.annotations.VisibleForTesting; @@ -120,6 +121,7 @@ import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; import org.hl7.fhir.convertors.context.ConversionContext40_50; import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50; import org.hl7.fhir.convertors.conv40_50.resources40_50.ValueSet40_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; @@ -206,10 +208,10 @@ import static org.apache.commons.lang3.StringUtils.lowerCase; import static org.apache.commons.lang3.StringUtils.startsWithIgnoreCase; import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW; -public abstract class BaseTermReadSvcImpl implements ITermReadSvc { +public class TermReadSvcImpl implements ITermReadSvc { public static final int DEFAULT_FETCH_SIZE = 250; private static final int SINGLE_FETCH_SIZE = 1; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseTermReadSvcImpl.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermReadSvcImpl.class); private static final ValueSetExpansionOptions DEFAULT_EXPANSION_OPTIONS = new ValueSetExpansionOptions(); private static final TermCodeSystemVersion NO_CURRENT_VERSION = new TermCodeSystemVersion().setId(-1L); private static Runnable myInvokeOnNextCallForUnitTest; @@ -292,6 +294,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return cs != null; } + @Override + public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { + return fetchValueSet(theValueSetUrl) != null; + } + private boolean addCodeIfNotAlreadyAdded(@Nullable ValueSetExpansionOptions theExpansionOptions, IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, TermConcept theConcept, boolean theAdd, String theValueSetIncludeVersion) { String codeSystem = theConcept.getCodeSystemVersion().getCodeSystem().getCodeSystemUri(); String codeSystemVersion = theConcept.getCodeSystemVersion().getCodeSystemVersionId(); @@ -365,7 +372,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { boolean retVal = theSetToPopulate.add(theConcept); if (retVal) { if (theSetToPopulate.size() >= myDaoConfig.getMaximumExpansionSize()) { - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionTooLarge", myDaoConfig.getMaximumExpansionSize()); + String msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "expansionTooLarge", myDaoConfig.getMaximumExpansionSize()); throw new ExpansionTooCostlyException(Msg.code(885) + msg); } } @@ -504,7 +511,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { */ if (!optionalTermValueSet.isPresent()) { ourLog.debug("ValueSet is not present in terminology tables. Will perform in-memory expansion without parameters. {}", getValueSetInfo(theValueSetToExpand)); - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetExpandedUsingInMemoryExpansion", getValueSetInfo(theValueSetToExpand)); + String msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "valueSetExpandedUsingInMemoryExpansion", getValueSetInfo(theValueSetToExpand)); theAccumulator.addMessage(msg); doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter); return; @@ -515,7 +522,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { */ TermValueSet termValueSet = optionalTermValueSet.get(); if (termValueSet.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) { - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetNotYetExpanded", getValueSetInfo(theValueSetToExpand), termValueSet.getExpansionStatus().name(), termValueSet.getExpansionStatus().getDescription()); + String msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "valueSetNotYetExpanded", getValueSetInfo(theValueSetToExpand), termValueSet.getExpansionStatus().name(), termValueSet.getExpansionStatus().getDescription()); theAccumulator.addMessage(msg); doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter); return; @@ -525,7 +532,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { * ValueSet is pre-expanded in database so let's use that */ String expansionTimestamp = toHumanReadableExpansionTimestamp(termValueSet); - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetExpandedUsingPreExpansion", expansionTimestamp); + String msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "valueSetExpandedUsingPreExpansion", expansionTimestamp); theAccumulator.addMessage(msg); expandConcepts(theExpansionOptions, theAccumulator, termValueSet, theFilter, theAdd, isOracleDialect()); } @@ -754,7 +761,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { Integer skipCountRemaining = theValueSetCodeAccumulator.getSkipCountRemaining(); if (skipCountRemaining != null && skipCountRemaining > 0) { if (theValueSetToExpand.getCompose().getExclude().size() > 0) { - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetNotYetExpanded_OffsetNotAllowed", valueSetInfo); + String msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "valueSetNotYetExpanded_OffsetNotAllowed", valueSetInfo); throw new InvalidRequestException(Msg.code(887) + msg); } } @@ -1631,17 +1638,17 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Transactional public String invalidatePreCalculatedExpansion(IIdType theValueSetId, RequestDetails theRequestDetails) { IBaseResource valueSet = myDaoRegistry.getResourceDao("ValueSet").read(theValueSetId, theRequestDetails); - ValueSet canonicalValueSet = toCanonicalValueSet(valueSet); + ValueSet canonicalValueSet = myVersionCanonicalizer.valueSetToCanonical(valueSet); Optional optionalTermValueSet = fetchValueSetEntity(canonicalValueSet); if (!optionalTermValueSet.isPresent()) { - return myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetNotFoundInTerminologyDatabase", theValueSetId); + return myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "valueSetNotFoundInTerminologyDatabase", theValueSetId); } ourLog.info("Invalidating pre-calculated expansion on ValueSet {} / {}", theValueSetId, canonicalValueSet.getUrl()); TermValueSet termValueSet = optionalTermValueSet.get(); if (termValueSet.getExpansionStatus() == TermValueSetPreExpansionStatusEnum.NOT_EXPANDED) { - return myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetCantInvalidateNotYetPrecalculated", termValueSet.getUrl(), termValueSet.getExpansionStatus()); + return myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "valueSetCantInvalidateNotYetPrecalculated", termValueSet.getUrl(), termValueSet.getExpansionStatus()); } Long totalConcepts = termValueSet.getTotalConcepts(); @@ -1651,7 +1658,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { termValueSet.setExpansionStatus(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED); termValueSet.setExpansionTimestamp(null); myTermValueSetDao.save(termValueSet); - return myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetPreExpansionInvalidated", termValueSet.getUrl(), totalConcepts); + return myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "valueSetPreExpansionInvalidated", termValueSet.getUrl(), totalConcepts); } @Override @@ -1721,7 +1728,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TermValueSet valueSetEntity = myTermValueSetDao.findByResourcePid(valueSetResourcePid.getIdAsLong()).orElseThrow(() -> new IllegalStateException()); String timingDescription = toHumanReadableExpansionTimestamp(valueSetEntity); - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "validationPerformedAgainstPreExpansion", timingDescription); + String msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "validationPerformedAgainstPreExpansion", timingDescription); if (theValidationOptions.isValidateDisplay() && concepts.size() > 0) { String systemVersion = null; @@ -1839,6 +1846,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } + @Override @Transactional(propagation = Propagation.MANDATORY) public List findCodes(String theCodeSystem, List theCodeList) { TermCodeSystemVersion csv = getCurrentCodeSystemVersion(theCodeSystem); @@ -2059,10 +2067,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Transactional public CodeValidationResult validateCode(ConceptValidationOptions theOptions, IIdType theValueSetId, String theValueSetIdentifier, String theCodeSystemIdentifierToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseDatatype theCodingToValidate, IBaseDatatype theCodeableConceptToValidate) { - CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConceptToValidate); + CodeableConcept codeableConcept = myVersionCanonicalizer.codeableConceptToCanonical(theCodeableConceptToValidate); boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0; - Coding canonicalCodingToValidate = toCanonicalCoding(theCodingToValidate); + Coding canonicalCodingToValidate = myVersionCanonicalizer.codingToCanonical((IBaseCoding) theCodingToValidate); boolean haveCoding = canonicalCodingToValidate != null && canonicalCodingToValidate.isEmpty() == false; boolean haveCode = theCodeToValidate != null && theCodeToValidate.isEmpty() == false; @@ -2128,8 +2136,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return myDeferredStorageSvc != null && !myDeferredStorageSvc.isStorageQueueEmpty(); } - protected abstract ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable); - private Optional getNextTermValueSetNotExpanded() { Optional retVal = Optional.empty(); Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 1), TermValueSetPreExpansionStatusEnum.NOT_EXPANDED); @@ -2186,12 +2192,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { String msg; if (version != null) { msg = myContext.getLocalizer().getMessage( - BaseTermReadSvcImpl.class, + TermReadSvcImpl.class, "cannotCreateDuplicateValueSetUrlAndVersion", url, version, existingTermValueSet.getResource().getIdDt().toUnqualifiedVersionless().getValue()); } else { msg = myContext.getLocalizer().getMessage( - BaseTermReadSvcImpl.class, + TermReadSvcImpl.class, "cannotCreateDuplicateValueSetUrl", url, existingTermValueSet.getResource().getIdDt().toUnqualifiedVersionless().getValue()); } @@ -2246,8 +2252,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return new IFhirResourceDaoCodeSystem.SubsumesResult(subsumes); } - protected abstract ValueSet toCanonicalValueSet(IBaseResource theValueSet); - protected IValidationSupport.LookupCodeResult lookupCode(String theSystem, String theCode, String theDisplayLanguage) { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); return txTemplate.execute(t -> { @@ -2325,7 +2329,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { invokeRunnableForUnitTest(); - IPrimitiveType urlPrimitive = myContext.newTerser().getSingleValueOrNull(theValueSet, "url", IPrimitiveType.class); + IPrimitiveType urlPrimitive; + if (theValueSet instanceof org.hl7.fhir.dstu2.model.ValueSet) { + urlPrimitive = FhirContext.forDstu2Hl7OrgCached().newTerser().getSingleValueOrNull(theValueSet, "url", IPrimitiveType.class); + } else { + urlPrimitive = myContext.newTerser().getSingleValueOrNull(theValueSet, "url", IPrimitiveType.class); + } String url = urlPrimitive.getValueAsString(); if (isNotBlank(url)) { return validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, url); @@ -2415,7 +2424,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { IValidationSupport validationSupport = provideValidationSupport(); IBaseResource codeSystem = validationSupport.fetchCodeSystem(theSystem); if (codeSystem != null) { - codeSystem = toCanonicalCodeSystem(codeSystem); + codeSystem = myVersionCanonicalizer.codeSystemToCanonical(codeSystem); } return (CodeSystem) codeSystem; } @@ -2444,13 +2453,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { IValidationSupport validationSupport = provideValidationSupport(); IBaseResource valueSet = validationSupport.fetchValueSet(theSystem); if (valueSet != null) { - valueSet = toCanonicalValueSet(valueSet); + valueSet = myVersionCanonicalizer.valueSetToCanonical(valueSet); } return (ValueSet) valueSet; } - protected abstract CodeSystem toCanonicalCodeSystem(IBaseResource theCodeSystem); - @Override public IBaseResource fetchValueSet(String theValueSetUrl) { return provideJpaValidationSupport().fetchValueSet(theValueSetUrl); @@ -2526,14 +2533,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return false; } - @Nullable - protected abstract Coding toCanonicalCoding(@Nullable IBaseDatatype theCoding); - - @Nullable - protected abstract Coding toCanonicalCoding(@Nullable IBaseCoding theCoding); - - @Nullable - protected abstract CodeableConcept toCanonicalCodeableConcept(@Nullable IBaseDatatype theCodeableConcept); @Nonnull private FhirVersionIndependentConcept toConcept(IPrimitiveType theCodeType, IPrimitiveType theCodeSystemIdentifierType, IBaseCoding theCodingType) { @@ -2541,7 +2540,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { String system = theCodeSystemIdentifierType != null ? getUrlFromIdentifier(theCodeSystemIdentifierType.getValueAsString()) : null; String systemVersion = theCodeSystemIdentifierType != null ? getVersionFromIdentifier(theCodeSystemIdentifierType.getValueAsString()) : null; if (theCodingType != null) { - Coding canonicalizedCoding = toCanonicalCoding(theCodingType); + Coding canonicalizedCoding = myVersionCanonicalizer.codingToCanonical(theCodingType); assert canonicalizedCoding != null; // Shouldn't be null, since theCodingType isn't code = canonicalizedCoding.getCode(); system = canonicalizedCoding.getSystem(); @@ -2554,10 +2553,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Transactional public CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theCodeSystemUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { - CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept); + CodeableConcept codeableConcept = myVersionCanonicalizer.codeableConceptToCanonical(theCodeableConcept); boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0; - Coding coding = toCanonicalCoding(theCoding); + Coding coding = myVersionCanonicalizer.codingToCanonical((IBaseCoding) theCoding); boolean haveCoding = coding != null && coding.isEmpty() == false; boolean haveCode = theCode != null && theCode.isEmpty() == false; @@ -2924,4 +2923,59 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { myIncludeOrExcludeCodes = theIncludeOrExcludeCodes; } } + + @Override + public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) { + ValueSet canonicalInput = myVersionCanonicalizer.valueSetToCanonical(theValueSetToExpand); + org.hl7.fhir.r4.model.ValueSet expandedR4 = expandValueSet(theExpansionOptions, canonicalInput); + return new ValueSetExpansionOutcome(myVersionCanonicalizer.valueSetFromCanonical(expandedR4)); + } + + @Override + public IBaseResource expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theInput) { + org.hl7.fhir.r4.model.ValueSet valueSetToExpand = myVersionCanonicalizer.valueSetToCanonical(theInput); + org.hl7.fhir.r4.model.ValueSet valueSetR4 = expandValueSet(theExpansionOptions, valueSetToExpand); + return myVersionCanonicalizer.valueSetFromCanonical(valueSetR4); + } + + @Override + public void expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) { + org.hl7.fhir.r4.model.ValueSet valueSetToExpand = myVersionCanonicalizer.valueSetToCanonical(theValueSetToExpand); + expandValueSet(theExpansionOptions, valueSetToExpand, theValueSetCodeAccumulator); + } + + private org.hl7.fhir.r4.model.ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) { + Class type = getFhirContext().getResourceDefinition("ValueSet").getImplementingClass(); + IBaseResource valueSet = myDaoRegistry.getResourceDao("ValueSet").toResource(type, theResourceTable, null, false); + return myVersionCanonicalizer.valueSetToCanonical(valueSet); + } + + @Override + public CodeValidationResult validateCodeIsInPreExpandedValueSet(ConceptValidationOptions theOptions, IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { + ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null"); + org.hl7.fhir.r4.model.ValueSet valueSetR4 = myVersionCanonicalizer.valueSetToCanonical(theValueSet); + org.hl7.fhir.r4.model.Coding codingR4 = myVersionCanonicalizer.codingToCanonical((IBaseCoding) theCoding); + org.hl7.fhir.r4.model.CodeableConcept codeableConcept = myVersionCanonicalizer.codeableConceptToCanonical(theCodeableConcept); + + return validateCodeIsInPreExpandedValueSet(theOptions, valueSetR4, theSystem, theCode, theDisplay, codingR4, codeableConcept); + } + + @Autowired + private VersionCanonicalizer myVersionCanonicalizer; + + + @Override + public boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet) { + ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null"); + org.hl7.fhir.r4.model.ValueSet valueSetR4 = myVersionCanonicalizer.valueSetToCanonical(theValueSet); + return isValueSetPreExpandedForCodeValidation(valueSetR4); + } + + @Override + public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { + return lookupCode(theSystem, theCode, theDisplayLanguage); + } + + + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java deleted file mode 100644 index 4efb0613841..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java +++ /dev/null @@ -1,126 +0,0 @@ -package ca.uhn.fhir.jpa.term; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.support.ConceptValidationOptions; -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.context.support.ValidationSupportContext; -import ca.uhn.fhir.context.support.ValueSetExpansionOptions; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; -import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException; -import org.hl7.fhir.instance.model.api.IBaseCoding; -import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.ValueSet; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.PlatformTransactionManager; - -import javax.annotation.Nonnull; -import org.springframework.transaction.annotation.Transactional; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4 { - - @Autowired - private PlatformTransactionManager myTransactionManager; - - @Override - public IBaseResource expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theInput) { - ValueSet valueSetToExpand = (ValueSet) theInput; - return super.expandValueSet(theExpansionOptions, valueSetToExpand); - } - - @Override - public void expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) { - ValueSet valueSetToExpand = (ValueSet) theValueSetToExpand; - super.expandValueSet(theExpansionOptions, valueSetToExpand, theValueSetCodeAccumulator); - } - - @Override - public IValidationSupport.ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) { - ValueSet expanded = super.expandValueSet(theExpansionOptions, (ValueSet) theValueSetToExpand); - return new IValidationSupport.ValueSetExpansionOutcome(expanded); - } - - @Override - protected ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) { - return myDaoRegistry.getResourceDao("ValueSet").toResource(ValueSet.class, theResourceTable, null, false); - } - - @Override - public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { - return fetchValueSet(theValueSetUrl) != null; - } - - @Override - public FhirContext getFhirContext() { - return myContext; - } - - - @Override - protected ValueSet toCanonicalValueSet(IBaseResource theValueSet) { - return (ValueSet) theValueSet; - } - - @Override - protected CodeSystem toCanonicalCodeSystem(IBaseResource theCodeSystem) { - return (CodeSystem) theCodeSystem; - } - - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { - return super.lookupCode(theSystem, theCode, theDisplayLanguage); - } - - @Override - public IValidationSupport.CodeValidationResult validateCodeIsInPreExpandedValueSet(ConceptValidationOptions theOptions, IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { - ValueSet valueSet = (ValueSet) theValueSet; - Coding coding = toCanonicalCoding(theCoding); - CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept); - return super.validateCodeIsInPreExpandedValueSet(theOptions, valueSet, theSystem, theCode, theDisplay, coding, codeableConcept); - } - - @Override - protected Coding toCanonicalCoding(IBaseDatatype theCoding) { - return (Coding) theCoding; - } - - @Override - protected Coding toCanonicalCoding(IBaseCoding theCoding) { - return (Coding) theCoding; - } - - @Override - protected CodeableConcept toCanonicalCodeableConcept(IBaseDatatype theCodeableConcept) { - return (CodeableConcept) theCodeableConcept; - } - - @Override - public boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet) { - ValueSet valueSet = (ValueSet) theValueSet; - return super.isValueSetPreExpandedForCodeValidation(valueSet); - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java deleted file mode 100644 index f93eb79dee7..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java +++ /dev/null @@ -1,145 +0,0 @@ -package ca.uhn.fhir.jpa.term; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.support.ConceptValidationOptions; -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.context.support.ValidationSupportContext; -import ca.uhn.fhir.context.support.ValueSetExpansionOptions; -import ca.uhn.fhir.jpa.api.dao.DaoRegistry; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR5; -import ca.uhn.fhir.util.ValidateUtil; -import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; -import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; -import org.hl7.fhir.instance.model.api.IBaseCoding; -import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r5.model.CodeSystem; -import org.hl7.fhir.r5.model.CodeableConcept; -import org.hl7.fhir.r5.model.Coding; -import org.hl7.fhir.r5.model.ValueSet; -import org.springframework.beans.factory.annotation.Autowired; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSupport, ITermReadSvcR5 { - - @Autowired - private DaoRegistry myDaoRegistry; - - @Override - public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) { - ValueSet valueSetToExpand = (ValueSet) theValueSetToExpand; - org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(theExpansionOptions, - (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSetToExpand, new BaseAdvisor_40_50(false))); - return new ValueSetExpansionOutcome(VersionConvertorFactory_40_50.convertResource(expandedR4, new BaseAdvisor_40_50(false))); - } - - @Override - public IBaseResource expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theInput) { - org.hl7.fhir.r4.model.ValueSet valueSetToExpand = toCanonicalValueSet(theInput); - org.hl7.fhir.r4.model.ValueSet valueSetR4 = super.expandValueSet(theExpansionOptions, valueSetToExpand); - return VersionConvertorFactory_40_50.convertResource(valueSetR4, new BaseAdvisor_40_50(false)); - } - - @Override - public void expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) { - org.hl7.fhir.r4.model.ValueSet valueSetToExpand = toCanonicalValueSet(theValueSetToExpand); - super.expandValueSet(theExpansionOptions, valueSetToExpand, theValueSetCodeAccumulator); - } - - @Override - protected org.hl7.fhir.r4.model.ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) { - ValueSet valueSetR5 = myDaoRegistry.getResourceDao("ValueSet").toResource(ValueSet.class, theResourceTable, null, false); - return (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSetR5, new BaseAdvisor_40_50(false)); - } - - @Override - public FhirContext getFhirContext() { - return myContext; - } - - @Override - public CodeValidationResult validateCodeIsInPreExpandedValueSet(ConceptValidationOptions theOptions, IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { - ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null"); - ValueSet valueSet = (ValueSet) theValueSet; - org.hl7.fhir.r4.model.ValueSet valueSetR4 = toCanonicalValueSet(valueSet); - - org.hl7.fhir.r4.model.Coding codingR4 = toCanonicalCoding(theCoding); - - CodeableConcept codeableConcept = (CodeableConcept) theCodeableConcept; - org.hl7.fhir.r4.model.CodeableConcept codeableConceptR4 = null; - if (codeableConcept != null) { - codeableConceptR4 = new org.hl7.fhir.r4.model.CodeableConcept(); - for (Coding nestedCoding : codeableConcept.getCoding()) { - codeableConceptR4.addCoding(new org.hl7.fhir.r4.model.Coding(nestedCoding.getSystem(), nestedCoding.getCode(), nestedCoding.getDisplay())); - } - } - - return super.validateCodeIsInPreExpandedValueSet(theOptions, valueSetR4, theSystem, theCode, theDisplay, codingR4, codeableConceptR4); - } - - @Override - @Nullable - protected org.hl7.fhir.r4.model.Coding toCanonicalCoding(IBaseDatatype theCoding) { - return (org.hl7.fhir.r4.model.Coding) VersionConvertorFactory_40_50.convertType((Coding) theCoding, new BaseAdvisor_40_50(false)); - } - - @Override - @Nullable - protected org.hl7.fhir.r4.model.Coding toCanonicalCoding(IBaseCoding theCoding) { - return (org.hl7.fhir.r4.model.Coding) VersionConvertorFactory_40_50.convertType((Coding) theCoding, new BaseAdvisor_40_50(false)); - } - - @Override - @Nullable - protected org.hl7.fhir.r4.model.CodeableConcept toCanonicalCodeableConcept(IBaseDatatype theCoding) { - return (org.hl7.fhir.r4.model.CodeableConcept) VersionConvertorFactory_40_50.convertType((CodeableConcept) theCoding, new BaseAdvisor_40_50(false)); - } - - - @Override - protected org.hl7.fhir.r4.model.ValueSet toCanonicalValueSet(IBaseResource theValueSet) throws org.hl7.fhir.exceptions.FHIRException { - return (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource((ValueSet) theValueSet, new BaseAdvisor_40_50(false)); - } - - @Override - protected org.hl7.fhir.r4.model.CodeSystem toCanonicalCodeSystem(IBaseResource theCodeSystem) { - return (org.hl7.fhir.r4.model.CodeSystem) VersionConvertorFactory_40_50.convertResource((CodeSystem) theCodeSystem, new BaseAdvisor_40_50(false)); - } - - @Override - public boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet) { - ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null"); - ValueSet valueSet = (ValueSet) theValueSet; - org.hl7.fhir.r4.model.ValueSet valueSetR4 = toCanonicalValueSet(valueSet); - return super.isValueSetPreExpandedForCodeValidation(valueSetR4); - } - - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { - return super.lookupCode(theSystem, theCode, theDisplayLanguage); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR4B.java new file mode 100644 index 00000000000..faacbbbf0c9 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR4B.java @@ -0,0 +1,108 @@ +package ca.uhn.fhir.jpa.term; + +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.util.UrlUtil; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4b.model.CodeSystem; +import org.hl7.fhir.r4b.model.ConceptMap; +import org.hl7.fhir.r4b.model.ValueSet; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; + +import static org.apache.commons.lang3.StringUtils.isBlank; + +public class TermVersionAdapterSvcR4B extends BaseTermVersionAdapterSvcImpl implements ITermVersionAdapterSvc { + private IFhirResourceDao myConceptMapResourceDao; + private IFhirResourceDao myCodeSystemResourceDao; + private IFhirResourceDao myValueSetResourceDao; + + @Autowired + private ApplicationContext myAppCtx; + + /** + * Initialize the beans that are used by this service. + * + * Note: There is a circular dependency here where the CodeSystem DAO + * needs terminology services, and the term services need the CodeSystem DAO. + * So we look these up in a refresh event instead of just autowiring them + * in order to avoid weird circular reference errors. + */ + @SuppressWarnings({"unchecked", "unused"}) + @EventListener + public void start(ContextRefreshedEvent theEvent) { + myCodeSystemResourceDao = (IFhirResourceDao) myAppCtx.getBean("myCodeSystemDaoR4B"); + myValueSetResourceDao = (IFhirResourceDao) myAppCtx.getBean("myValueSetDaoR4B"); + myConceptMapResourceDao = (IFhirResourceDao) myAppCtx.getBean("myConceptMapDaoR4B"); + } + + @Override + public IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { + validateCodeSystemForStorage(theCodeSystemResource); + + org.hl7.fhir.r5.model.CodeSystem codeSystemR5 = (org.hl7.fhir.r5.model.CodeSystem) VersionConvertorFactory_40_50.convertResource(theCodeSystemResource, new BaseAdvisor_40_50(false)); + CodeSystem codeSystemR4 = (CodeSystem) VersionConvertorFactory_43_50.convertResource(codeSystemR5, new BaseAdvisor_43_50(false)); + if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) { + String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); + return myCodeSystemResourceDao.update(codeSystemR4, matchUrl, theRequestDetails).getId(); + } else { + return myCodeSystemResourceDao.update(codeSystemR4, theRequestDetails).getId(); + } + } + + @Override + public void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap) { + + org.hl7.fhir.r5.model.ConceptMap conceptMapR5 = (org.hl7.fhir.r5.model.ConceptMap) VersionConvertorFactory_40_50.convertResource(theConceptMap, new BaseAdvisor_40_50(false)); + ConceptMap conceptMapR4 = (ConceptMap) VersionConvertorFactory_43_50.convertResource(conceptMapR5, new BaseAdvisor_43_50(false)); + + if (isBlank(theConceptMap.getIdElement().getIdPart())) { + String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); + myConceptMapResourceDao.update(conceptMapR4, matchUrl); + } else { + myConceptMapResourceDao.update(conceptMapR4); + } + } + + @Override + public void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet) { + + org.hl7.fhir.r5.model.ValueSet valueSetR5 = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(theValueSet, new BaseAdvisor_40_50(false)); + ValueSet valueSetR4 = (ValueSet) VersionConvertorFactory_43_50.convertResource(valueSetR5, new BaseAdvisor_43_50(false)); + + if (isBlank(theValueSet.getIdElement().getIdPart())) { + String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl()); + myValueSetResourceDao.update(valueSetR4, matchUrl); + } else { + myValueSetResourceDao.update(valueSetR4); + } + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java index e811f801ea4..04f86f47ef2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java @@ -188,13 +188,13 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V private void incrementConceptsCount() { Integer capacityRemaining = getCapacityRemaining(); if (capacityRemaining == 0) { - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionTooLarge", myMaxCapacity); + String msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "expansionTooLarge", myMaxCapacity); msg = appendAccumulatorMessages(msg); throw new ExpansionTooCostlyException(Msg.code(831) + msg); } if (myHardExpansionMaximumSize > 0 && myAddedConcepts > myHardExpansionMaximumSize) { - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionTooLarge", myHardExpansionMaximumSize); + String msg = myContext.getLocalizer().getMessage(TermReadSvcImpl.class, "expansionTooLarge", myHardExpansionMaximumSize); msg = appendAccumulatorMessages(msg); throw new ExpansionTooCostlyException(Msg.code(832) + msg); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvcDstu3.java deleted file mode 100644 index 3618421482a..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvcDstu3.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.uhn.fhir.jpa.term.api; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.context.support.IValidationSupport; - -public interface ITermReadSvcDstu3 extends ITermReadSvc, IValidationSupport { - // nothing -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvcR4.java deleted file mode 100644 index 2c865dcc409..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvcR4.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.uhn.fhir.jpa.term.api; - -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.context.support.IValidationSupport; - -public interface ITermReadSvcR4 extends ITermReadSvc, IValidationSupport { - // nothing -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvcR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvcR5.java deleted file mode 100644 index 102dd2fb600..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvcR5.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.uhn.fhir.jpa.term.api; - -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2022 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.context.support.IValidationSupport; - -public interface ITermReadSvcR5 extends ITermReadSvc, IValidationSupport { - // nothing -} diff --git a/hapi-fhir-jpaserver-cql/pom.xml b/hapi-fhir-jpaserver-cql/pom.xml index bc42d76b23c..88395c4a2c1 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/config/CqlDstu3Config.java b/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/config/CqlDstu3Config.java index d64abf51779..13905cdf992 100644 --- a/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/config/CqlDstu3Config.java +++ b/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/config/CqlDstu3Config.java @@ -34,7 +34,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import org.cqframework.cql.cql2elm.CqlTranslatorOptions; import org.cqframework.cql.cql2elm.model.Model; import org.cqframework.cql.elm.execution.Library; @@ -53,7 +53,7 @@ import java.util.Map; public class CqlDstu3Config extends BaseCqlConfig { @Lazy @Bean - TerminologyProvider terminologyProvider(ITermReadSvcDstu3 theITermReadSvc, DaoRegistry theDaoRegistry, IValidationSupport theValidationSupport) { + TerminologyProvider terminologyProvider(ITermReadSvc theITermReadSvc, DaoRegistry theDaoRegistry, IValidationSupport theValidationSupport) { return new JpaTerminologyProvider(theITermReadSvc, theDaoRegistry, theValidationSupport); } diff --git a/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/config/CqlR4Config.java b/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/config/CqlR4Config.java index 74b3a0dcedd..94c385f38bf 100644 --- a/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/config/CqlR4Config.java +++ b/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/config/CqlR4Config.java @@ -35,7 +35,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import org.cqframework.cql.cql2elm.CqlTranslatorOptions; import org.cqframework.cql.cql2elm.model.Model; import org.cqframework.cql.elm.execution.Library; @@ -53,6 +53,7 @@ import java.util.Map; @Configuration public class CqlR4Config extends BaseCqlConfig { + @Override @Lazy @Bean CqlProviderFactory cqlProviderFactory() { @@ -61,8 +62,8 @@ public class CqlR4Config extends BaseCqlConfig { @Lazy @Bean - TerminologyProvider terminologyProvider(ITermReadSvcR4 theITermReadSvc, DaoRegistry theDaoRegistry, - IValidationSupport theValidationSupport) { + TerminologyProvider terminologyProvider(ITermReadSvc theITermReadSvc, DaoRegistry theDaoRegistry, + IValidationSupport theValidationSupport) { return new JpaTerminologyProvider(theITermReadSvc, theDaoRegistry, theValidationSupport); } diff --git a/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/dstu3/provider/JpaTerminologyProvider.java b/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/dstu3/provider/JpaTerminologyProvider.java index f13effdb427..c499f05ea4b 100644 --- a/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/dstu3/provider/JpaTerminologyProvider.java +++ b/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/dstu3/provider/JpaTerminologyProvider.java @@ -20,15 +20,15 @@ package ca.uhn.fhir.cql.dstu3.provider; * #L% */ -import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; @@ -52,14 +52,14 @@ import java.util.List; @Component public class JpaTerminologyProvider implements TerminologyProvider { - private final ITermReadSvcDstu3 myTerminologySvc; + private final ITermReadSvc myTerminologySvc; private final DaoRegistry myDaoRegistry; private final IValidationSupport myValidationSupport; private IFhirResourceDao myValueSetDao; @Autowired - public JpaTerminologyProvider(ITermReadSvcDstu3 theTerminologySvc, DaoRegistry theDaoRegistry, IValidationSupport theValidationSupport) { + public JpaTerminologyProvider(ITermReadSvc theTerminologySvc, DaoRegistry theDaoRegistry, IValidationSupport theValidationSupport) { myTerminologySvc = theTerminologySvc; myDaoRegistry = theDaoRegistry; myValidationSupport = theValidationSupport; diff --git a/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/r4/provider/JpaTerminologyProvider.java b/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/r4/provider/JpaTerminologyProvider.java index 45ac468a1f2..9506fa55337 100644 --- a/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/r4/provider/JpaTerminologyProvider.java +++ b/hapi-fhir-jpaserver-cql/src/main/java/ca/uhn/fhir/cql/r4/provider/JpaTerminologyProvider.java @@ -20,15 +20,15 @@ package ca.uhn.fhir.cql.r4.provider; * #L% */ -import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; @@ -50,14 +50,14 @@ import java.util.List; @Component public class JpaTerminologyProvider implements TerminologyProvider { - private final ITermReadSvcR4 myTerminologySvc; + private final ITermReadSvc myTerminologySvc; private final DaoRegistry myDaoRegistry; private final IValidationSupport myValidationSupport; private IFhirResourceDao myValueSetDao; @Autowired - public JpaTerminologyProvider(ITermReadSvcR4 theTerminologySvc, DaoRegistry theDaoRegistry, IValidationSupport theValidationSupport) { + public JpaTerminologyProvider(ITermReadSvc theTerminologySvc, DaoRegistry theDaoRegistry, IValidationSupport theValidationSupport) { myTerminologySvc = theTerminologySvc; myDaoRegistry = theDaoRegistry; myValidationSupport = theValidationSupport; diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index d1b84d68d90..38fe3c7b457 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java index a3fe2e03aa7..5c8482bf854 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java @@ -27,10 +27,9 @@ import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.test.BaseJpaTest; import ca.uhn.fhir.jpa.test.util.TestHSearchEventDispatcher; -import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.SearchTotalModeEnum; @@ -129,7 +128,6 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(SpringExtension.class) @@ -145,13 +143,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) @TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class - ,FhirResourceDaoR4SearchWithElasticSearchIT.TestDirtiesContextTestExecutionListener.class - }) + , FhirResourceDaoR4SearchWithElasticSearchIT.TestDirtiesContextTestExecutionListener.class +}) public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest implements ITestDataBuilder { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchWithElasticSearchIT.class); - public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchWithElasticSearchIT.class); private static final String SPACE = "%20"; @Autowired @@ -170,10 +167,19 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl @Qualifier("myValueSetDaoR4") protected IFhirResourceDaoValueSet myValueSetDao; @Autowired - protected ITermReadSvcR4 myTermSvc; + protected ITermReadSvc myTermSvc; @Autowired protected IResourceTableDao myResourceTableDao; @Autowired + @Qualifier("myRiskAssessmentDaoR4") + protected IFhirResourceDao myRiskAssessmentDao; + @Autowired + ITestDataBuilder.WithSupport myTestDataBuilder; + @Autowired + TestDaoSearch myTestDaoSearch; + @RegisterExtension + LogbackLevelOverrideExtension myLogbackLevelOverrideExtension = new LogbackLevelOverrideExtension(); + @Autowired @Qualifier("myCodeSystemDaoR4") private IFhirResourceDao myCodeSystemDao; @Autowired @@ -187,11 +193,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl @Autowired @Qualifier("myEncounterDaoR4") private IFhirResourceDao myEncounterDao; - - @Autowired - @Qualifier("myRiskAssessmentDaoR4") - protected IFhirResourceDao myRiskAssessmentDao; - @Autowired @Qualifier("mySystemDaoR4") private IFhirSystemDao mySystemDao; @@ -204,33 +205,26 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl @Autowired private DaoRegistry myDaoRegistry; @Autowired - ITestDataBuilder.WithSupport myTestDataBuilder; - @Autowired - TestDaoSearch myTestDaoSearch; - @Autowired @Qualifier("myQuestionnaireDaoR4") private IFhirResourceDao myQuestionnaireDao; - @Autowired private IFhirResourceDao myDiagnosticReportDao; - @Autowired @Qualifier("myQuestionnaireResponseDaoR4") private IFhirResourceDao myQuestionnaireResponseDao; - @RegisterExtension - LogbackLevelOverrideExtension myLogbackLevelOverrideExtension = new LogbackLevelOverrideExtension(); - @Autowired private TestHSearchEventDispatcher myHSearchEventDispatcher; - @Mock private IHSearchEventListener mySearchEventListener; + @Mock + private IHSearchEventListener mySearchEventListener; @BeforeEach public void beforePurgeDatabase() { purgeDatabase(myDaoConfig, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataScheduleHelper); } + @Override public IIdType doCreateResource(IBaseResource theResource) { return myTestDataBuilder.doCreateResource(theResource); @@ -419,185 +413,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } } - @Nested - public class StringTextSearch { - - @Test - void secondWordFound() { - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Cloudy, yellow") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=yellow"); - assertThat(resourceIds, hasItem(id1)); - } - - @Test - void stringMatchesPrefixAndWhole() { - // smit - matches "smit" and "smith" - - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=smit"); - assertThat(resourceIds, hasItems(id1, id2)); - } - - - @Test - void stringPlusStarMatchesPrefixAndWhole() { - // smit* - matches "smit" and "smith" - - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?_elements=valueString&value-string:text=smit*"); - assertThat(resourceIds, hasItems(id1, id2)); - } - - - @Test - void quotedStringMatchesIdenticalButNotAsPrefix() { - // "smit"- matches "smit", but not "smith" - - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=\"smit\""); - assertThat(resourceIds, contains(id2)); - } - - - @Test - void stringTokensAreAnded() { - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=car%20smit"); - assertThat(resourceIds, hasItems(id2)); - } - - @Nested - public class DocumentationSamplesTests { - - @Test - void line1() { - // | Fhir Query String | Executed Query | Matches | No Match - // | Smit | Smit* | John Smith | John Smi - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smi") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=Smit"); - assertThat(resourceIds, hasItems(id1)); - } - - @Test - void line2() { - // | Fhir Query String | Executed Query | Matches | No Match | Note - // | Jo Smit | Jo* Smit* | John Smith | John Frank | Multiple bare terms are `AND` - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Frank") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=Jo%20Smit"); - assertThat(resourceIds, hasItems(id1)); - } - - @Test - void line3() { - // | Fhir Query String | Executed Query | Matches | No Match | Note - // | frank | john | frank | john | Frank Smith | Franklin Smith | SQS characters disable prefix wildcard - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Frank Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Franklin Smith") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=frank|john"); - assertThat(resourceIds, hasItems(id1)); - } - - @Test - void line4() { - // | Fhir Query String | Executed Query | Matches | No Match | Note - // | 'frank' | 'frank' | Frank Smith | Franklin Smith | Quoted terms are exact match - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Frank Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Franklin Smith") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text='frank'"); - assertThat(resourceIds, hasItems(id1)); - } - } - } - - @Nested - public class TokenTextSearch { - - @Test - void secondWordFound() { - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withObservationCode("http://example.com", "code-AA", "Cloudy, yellow") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?code:text=yellow"); - assertThat(resourceIds, hasItem(id1)); - } - - @Test - void stringMatchesPrefixAndWhole() { - // smit - matches "smit" and "smith" - - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withObservationCode("http://example.com", "code-AA", "John Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withObservationCode("http://example.com", "code-BB", "Carl Smit") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?code:text=smit"); - assertThat(resourceIds, hasItems(id1, id2)); - } - - - @Test - void stringPlusStarMatchesPrefixAndWhole() { - // smit* - matches "smit" and "smith" - - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withObservationCode("http://example.com", "code-AA", "Adam Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withObservationCode("http://example.com", "code-BB", "John Smit") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?code:text=smit*"); - assertThat(resourceIds, hasItems(id1, id2)); - } - - - @Test - void quotedStringMatchesIdenticalButNotAsPrefix() { - // "smit"- matches "smit", but not "smith" - - String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withObservationCode("http://example.com", "code-AA", "John Smith") )).getIdPart(); - String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withObservationCode("http://example.com", "code-BB", "Karl Smit") )).getIdPart(); - - List resourceIds = myTestDaoSearch.searchForIds("/Observation?code:text=\"Smit\""); - assertThat(resourceIds, contains(id2)); - } - - } - - - @Test public void testResourceCodeTextSearch() { IIdType id1, id2, id3, id4; @@ -718,7 +533,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } } - @Test public void testResourceReferenceSearchForCanonicalReferences() { String questionnaireCanonicalUrl = "https://test.fhir.org/R4/Questionnaire/xl-5000-q"; @@ -865,7 +679,9 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } - /** Our url parser requires all chars to be single-byte, and in utf8, that means ascii. */ + /** + * Our url parser requires all chars to be single-byte, and in utf8, that means ascii. + */ private String urlencode(String theParam) { return URLEncoder.encode(theParam, CHARSET_UTF8); } @@ -883,47 +699,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl assertObservationSearchMatches(theMessage, map, theIds); } - @Nested - public class WithContainedIndexingIT { - @BeforeEach - public void enableContains() { - // we don't support chained or contained yet, but turn it on to test we don't blow up. - myDaoConfig.getModelConfig().setIndexOnContainedResources(true); - myDaoConfig.getModelConfig().setIndexOnContainedResourcesRecursively(true); - } - - @AfterEach - public void restoreContains() { - ModelConfig defaultModelConfig = new ModelConfig(); - myDaoConfig.getModelConfig().setIndexOnContainedResources(defaultModelConfig.isIndexOnContainedResources()); - myDaoConfig.getModelConfig().setIndexOnContainedResourcesRecursively(defaultModelConfig.isIndexOnContainedResourcesRecursively()); - } - /** - * We were throwing when indexing contained. - * https://github.com/hapifhir/hapi-fhir/issues/3371 - */ - @Test - public void ignoreContainedResources_noError() { - // given - String json = - "{" + - "\"resourceType\": \"Observation\"," + - "\"contained\": [{" + - "\"resourceType\": \"Patient\"," + - "\"id\": \"contained-patient\"," + - "\"name\": [{ \"family\": \"Smith\"}]" + - "}]," + - "\"subject\": { \"reference\": \"#contained-patient\" }" + - "}"; - Observation o = myFhirCtx.newJsonParser().parseResource(Observation.class, json); - - IIdType id = myObservationDao.create(o, mySrd).getId().toUnqualifiedVersionless(); - - // no error. - assertThat(id, notNullValue()); - } - } - /** * When configuring direct resource load for populated index a full reindex is required * Code should detect the case and instead of throwing "Resource not stored in search index" exception, should log @@ -943,7 +718,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl myCaptureQueriesListener.logSelectQueriesForCurrentThread(); assertThat(result, hasSize(1)); - assertEquals( ((Observation) result.get(0)).getIdElement().getIdPart(), id1.getIdPart()); + assertEquals(((Observation) result.get(0)).getIdElement().getIdPart(), id1.getIdPart()); assertEquals(2, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "JPA search for IDs and for resources"); // restore changed property @@ -951,7 +726,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl myDaoConfig.setStoreResourceInHSearchIndex(defaultConfig.isStoreResourceInHSearchIndex()); } - @Test public void testExpandWithIsAInExternalValueSet() { createExternalCsAndLocalVs(); @@ -1043,7 +817,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl assertThat(codes.toString(), codes, Matchers.contains("advice", "message", "note", "notification")); } - @Test public void testExpandVsWithMultiInclude_Some() throws IOException { CodeSystem cs = loadResource(myFhirCtx, CodeSystem.class, "/r4/expand-multi-cs.json"); @@ -1147,6 +920,308 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } + private IIdType createRiskAssessment() { + return (createRiskAssessmentWithPredictionProbability(null)); + } + + private IIdType createRiskAssessmentWithPredictionProbability(Number theProbability) { + RiskAssessment ra1 = new RiskAssessment(); + if (theProbability != null) { + RiskAssessment.RiskAssessmentPredictionComponent component = ra1.addPrediction(); + component.setProbability(new DecimalType(theProbability.doubleValue())); + } + return myRiskAssessmentDao.create(ra1).getId().toUnqualifiedVersionless(); + } + + @Disabled("keeping to debug search scrolling") + @Test + public void withoutCount() { + createObservations(600); + + SearchParameterMap map = new SearchParameterMap(); + map.add("code", new TokenParam().setSystem("http://example.com")); + List bp = myObservationDao.searchForIds(map, new ServletRequestDetails()); + assertNotNull(bp); + assertEquals(600, bp.size()); + + } + + private void createObservations(int theCount) { + for (int i = 0; i < theCount; i++) { + myTestDataBuilder.createObservation(asArray( + myTestDataBuilder.withObservationCode("http://example.com", "code-" + i))); + } + } + + private Consumer[] asArray(Consumer theIBaseResourceConsumer) { + @SuppressWarnings("unchecked") + Consumer[] array = (Consumer[]) new Consumer[]{theIBaseResourceConsumer}; + return array; + } + + private List getResultIds(IBundleProvider theResult) { + return theResult.getAllResources().stream().map(r -> r.getIdElement().getIdPart()).collect(Collectors.toList()); + } + + private void assertFindId(String theMessage, IIdType theResourceId, String theUrl) { + List resourceIds = myTestDaoSearch.searchForIds(theUrl); + assertThat(theMessage, resourceIds, hasItem(equalTo(theResourceId.getIdPart()))); + } + + private void assertFindIds(String theMessage, Collection theResourceIds, String theUrl) { + List resourceIds = myTestDaoSearch.searchForIds(theUrl); + assertEquals(theResourceIds, new HashSet<>(resourceIds), theMessage); + } + + private void assertNotFindId(String theMessage, IIdType theResourceId, String theUrl) { + List resourceIds = myTestDaoSearch.searchForIds(theUrl); + assertThat(theMessage, resourceIds, not(hasItem(equalTo(theResourceId.getIdPart())))); + } + + /** + * Search for resources in the first query, instead of searching for IDs first + */ + public List searchForFastResources(String theQueryUrl) { + SearchParameterMap map = myTestDaoSearch.toSearchParameters(theQueryUrl); + map.setLoadSynchronous(true); + + SortSpec sort = (SortSpec) new ca.uhn.fhir.rest.server.method.SortParameter(myFhirCtx) + .translateQueryParametersIntoServerArgument(fakeRequestDetailsFromUrl(theQueryUrl), null); + if (sort != null) { + map.setSort(sort); + } + + return myTestDaoSearch.searchForResources(theQueryUrl); + } + + @Nonnull + private SystemRequestDetails fakeRequestDetailsFromUrl(String theQueryUrl) { + SystemRequestDetails request = new SystemRequestDetails(); + UriComponents uriComponents = UriComponentsBuilder.fromUriString(theQueryUrl).build(); + uriComponents.getQueryParams() + .forEach((key, value) -> request.addParameter(key, value.toArray(new String[0]))); + return request; + } + + @Nested + public class StringTextSearch { + + @Test + void secondWordFound() { + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "Cloudy, yellow"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=yellow"); + assertThat(resourceIds, hasItem(id1)); + } + + @Test + void stringMatchesPrefixAndWhole() { + // smit - matches "smit" and "smith" + + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=smit"); + assertThat(resourceIds, hasItems(id1, id2)); + } + + + @Test + void stringPlusStarMatchesPrefixAndWhole() { + // smit* - matches "smit" and "smith" + + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?_elements=valueString&value-string:text=smit*"); + assertThat(resourceIds, hasItems(id1, id2)); + } + + + @Test + void quotedStringMatchesIdenticalButNotAsPrefix() { + // "smit"- matches "smit", but not "smith" + + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=\"smit\""); + assertThat(resourceIds, contains(id2)); + } + + + @Test + void stringTokensAreAnded() { + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=car%20smit"); + assertThat(resourceIds, hasItems(id2)); + } + + @Nested + public class DocumentationSamplesTests { + + @Test + void line1() { + // | Fhir Query String | Executed Query | Matches | No Match + // | Smit | Smit* | John Smith | John Smi + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smi"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=Smit"); + assertThat(resourceIds, hasItems(id1)); + } + + @Test + void line2() { + // | Fhir Query String | Executed Query | Matches | No Match | Note + // | Jo Smit | Jo* Smit* | John Smith | John Frank | Multiple bare terms are `AND` + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "John Frank"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=Jo%20Smit"); + assertThat(resourceIds, hasItems(id1)); + } + + @Test + void line3() { + // | Fhir Query String | Executed Query | Matches | No Match | Note + // | frank | john | frank | john | Frank Smith | Franklin Smith | SQS characters disable prefix wildcard + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "Frank Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "Franklin Smith"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=frank|john"); + assertThat(resourceIds, hasItems(id1)); + } + + @Test + void line4() { + // | Fhir Query String | Executed Query | Matches | No Match | Note + // | 'frank' | 'frank' | Frank Smith | Franklin Smith | Quoted terms are exact match + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "Frank Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withPrimitiveAttribute("valueString", "Franklin Smith"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text='frank'"); + assertThat(resourceIds, hasItems(id1)); + } + } + } + + @Nested + public class TokenTextSearch { + + @Test + void secondWordFound() { + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withObservationCode("http://example.com", "code-AA", "Cloudy, yellow"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?code:text=yellow"); + assertThat(resourceIds, hasItem(id1)); + } + + @Test + void stringMatchesPrefixAndWhole() { + // smit - matches "smit" and "smith" + + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withObservationCode("http://example.com", "code-AA", "John Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withObservationCode("http://example.com", "code-BB", "Carl Smit"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?code:text=smit"); + assertThat(resourceIds, hasItems(id1, id2)); + } + + + @Test + void stringPlusStarMatchesPrefixAndWhole() { + // smit* - matches "smit" and "smith" + + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withObservationCode("http://example.com", "code-AA", "Adam Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withObservationCode("http://example.com", "code-BB", "John Smit"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?code:text=smit*"); + assertThat(resourceIds, hasItems(id1, id2)); + } + + + @Test + void quotedStringMatchesIdenticalButNotAsPrefix() { + // "smit"- matches "smit", but not "smith" + + String id1 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withObservationCode("http://example.com", "code-AA", "John Smith"))).getIdPart(); + String id2 = myTestDataBuilder.createObservation(List.of( + myTestDataBuilder.withObservationCode("http://example.com", "code-BB", "Karl Smit"))).getIdPart(); + + List resourceIds = myTestDaoSearch.searchForIds("/Observation?code:text=\"Smit\""); + assertThat(resourceIds, contains(id2)); + } + + } + + @Nested + public class WithContainedIndexingIT { + @BeforeEach + public void enableContains() { + // we don't support chained or contained yet, but turn it on to test we don't blow up. + myDaoConfig.getModelConfig().setIndexOnContainedResources(true); + myDaoConfig.getModelConfig().setIndexOnContainedResourcesRecursively(true); + } + + @AfterEach + public void restoreContains() { + ModelConfig defaultModelConfig = new ModelConfig(); + myDaoConfig.getModelConfig().setIndexOnContainedResources(defaultModelConfig.isIndexOnContainedResources()); + myDaoConfig.getModelConfig().setIndexOnContainedResourcesRecursively(defaultModelConfig.isIndexOnContainedResourcesRecursively()); + } + + /** + * We were throwing when indexing contained. + * https://github.com/hapifhir/hapi-fhir/issues/3371 + */ + @Test + public void ignoreContainedResources_noError() { + // given + String json = + "{" + + "\"resourceType\": \"Observation\"," + + "\"contained\": [{" + + "\"resourceType\": \"Patient\"," + + "\"id\": \"contained-patient\"," + + "\"name\": [{ \"family\": \"Smith\"}]" + + "}]," + + "\"subject\": { \"reference\": \"#contained-patient\" }" + + "}"; + Observation o = myFhirCtx.newJsonParser().parseResource(Observation.class, json); + + IIdType id = myObservationDao.create(o, mySrd).getId().toUnqualifiedVersionless(); + + // no error. + assertThat(id, notNullValue()); + } + } + @Nested public class DateSearchIT extends BaseDateSearchDaoTests { @@ -1185,7 +1260,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl myCaptureQueriesListener.logSelectQueriesForCurrentThread(); assertThat(result, hasSize(1)); - assertEquals( ((Observation) result.get(0)).getId(), id.getIdPart() ); + assertEquals(((Observation) result.get(0)).getId(), id.getIdPart()); assertEquals(0, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "we build the bundle with no sql"); // only one hibernate search took place @@ -1242,7 +1317,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl /** * A paranoid test to make sure tags stay with the resource. - * + *

* Tags live outside the resource, and can be modified by * Since we lost the id, also check tags in case someone changes metadata processing during ingestion. */ @@ -1456,9 +1531,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } - - - @Nested public class LastUpdatedTests { @@ -1471,11 +1543,11 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl myOldObsId = myTestDataBuilder.createObservation(List.of( myTestDataBuilder.withObservationCode("http://example.com/", "theCodeOld"), - myTestDataBuilder.withLastUpdated(myOldLastUpdatedDateTime) )).getIdPart(); + myTestDataBuilder.withLastUpdated(myOldLastUpdatedDateTime))).getIdPart(); myNewObsId = myTestDataBuilder.createObservation(List.of( myTestDataBuilder.withObservationCode("http://example.com/", "theCodeNew"), - myTestDataBuilder.withLastUpdated(new Date()) )).getIdPart(); + myTestDataBuilder.withLastUpdated(new Date()))).getIdPart(); } @AfterEach @@ -1549,7 +1621,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } - @Nested public class TotalParameter { @@ -1577,7 +1648,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } - @Nested public class OffsetParameter { @@ -1659,7 +1729,25 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl myDaoConfig.setAdvancedHSearchIndexing(defaultConfig.isAdvancedHSearchIndexing()); myDaoConfig.setStoreResourceInHSearchIndex(defaultConfig.isStoreResourceInHSearchIndex()); myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel( - defaultConfig.getModelConfig().getNormalizedQuantitySearchLevel() ); + defaultConfig.getModelConfig().getNormalizedQuantitySearchLevel()); + } + + @Test + public void directResourceLoadWhenSorting() { + String idA = myTestDataBuilder.createObservation(List.of(myTestDataBuilder.withObservationCode("http://example.com/", "code-a"))).getIdPart(); + String idC = myTestDataBuilder.createObservation(List.of(myTestDataBuilder.withObservationCode("http://example.com/", "code-c"))).getIdPart(); + String idB = myTestDataBuilder.createObservation(List.of(myTestDataBuilder.withObservationCode("http://example.com/", "code-b"))).getIdPart(); + myCaptureQueriesListener.clear(); + myHSearchEventDispatcher.register(mySearchEventListener); + + List result = searchForFastResources("Observation?_sort=-code"); + myCaptureQueriesListener.logSelectQueriesForCurrentThread(); + + assertThat(result.stream().map(r -> r.getIdElement().getIdPart()).collect(Collectors.toList()), contains(idC, idB, idA)); + assertEquals(0, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "we build the bundle with no sql"); + + // only one hibernate search took place + Mockito.verify(mySearchEventListener, Mockito.times(1)).hsearchEvent(IHSearchEventListener.HSearchEventType.SEARCH); } @Nested @@ -2046,27 +2134,8 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } - @Test - public void directResourceLoadWhenSorting() { - String idA = myTestDataBuilder.createObservation(List.of(myTestDataBuilder.withObservationCode("http://example.com/", "code-a"))).getIdPart(); - String idC = myTestDataBuilder.createObservation(List.of(myTestDataBuilder.withObservationCode("http://example.com/", "code-c"))).getIdPart(); - String idB = myTestDataBuilder.createObservation(List.of(myTestDataBuilder.withObservationCode("http://example.com/", "code-b"))).getIdPart(); - myCaptureQueriesListener.clear(); - myHSearchEventDispatcher.register(mySearchEventListener); - - List result = searchForFastResources("Observation?_sort=-code"); - myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - - assertThat( result.stream().map(r -> r.getIdElement().getIdPart()).collect(Collectors.toList()), contains(idC, idB, idA) ); - assertEquals(0, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "we build the bundle with no sql"); - - // only one hibernate search took place - Mockito.verify(mySearchEventListener, Mockito.times(1)).hsearchEvent(IHSearchEventListener.HSearchEventType.SEARCH); - } - } - @Nested public class NumberParameter { @@ -2086,7 +2155,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl myDaoConfig.setAdvancedHSearchIndexing(defaultConfig.isAdvancedHSearchIndexing()); myDaoConfig.setStoreResourceInHSearchIndex(defaultConfig.isStoreResourceInHSearchIndex()); myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel( - defaultConfig.getModelConfig().getNormalizedQuantitySearchLevel() ); + defaultConfig.getModelConfig().getNormalizedQuantitySearchLevel()); } @Test @@ -2098,6 +2167,28 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl assertEquals(0, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "we build the bundle with no sql"); } + @Test + void andClauses() { + String raId1 = createRiskAssessmentWithPredictionProbability(0.15).getIdPart(); + String raId2 = createRiskAssessmentWithPredictionProbability(0.20).getIdPart(); + String raId3 = createRiskAssessmentWithPredictionProbability(0.25).getIdPart(); + String raId4 = createRiskAssessmentWithPredictionProbability(0.35).getIdPart(); + String raId5 = createRiskAssessmentWithPredictionProbability(0.45).getIdPart(); + String raId6 = createRiskAssessmentWithPredictionProbability(0.55).getIdPart(); + assertFindIds("when le", Set.of(raId2, raId3, raId4), "/RiskAssessment?probability=ge0.2&probability=lt0.45"); + } + + @Test + void orClauses() { + String raId1 = createRiskAssessmentWithPredictionProbability(0.15).getIdPart(); + String raId2 = createRiskAssessmentWithPredictionProbability(0.20).getIdPart(); + String raId3 = createRiskAssessmentWithPredictionProbability(0.25).getIdPart(); + String raId4 = createRiskAssessmentWithPredictionProbability(0.35).getIdPart(); + String raId5 = createRiskAssessmentWithPredictionProbability(0.45).getIdPart(); + String raId6 = createRiskAssessmentWithPredictionProbability(0.55).getIdPart(); + assertFindIds("when le", Set.of(raId1, raId2, raId3, raId6), "/RiskAssessment?probability=le0.25,gt0.50"); + } + /** * The following tests are to validate the specification implementation */ @@ -2181,80 +2272,17 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } } - - @Test - void andClauses() { - String raId1 = createRiskAssessmentWithPredictionProbability(0.15).getIdPart(); - String raId2 = createRiskAssessmentWithPredictionProbability(0.20).getIdPart(); - String raId3 = createRiskAssessmentWithPredictionProbability(0.25).getIdPart(); - String raId4 = createRiskAssessmentWithPredictionProbability(0.35).getIdPart(); - String raId5 = createRiskAssessmentWithPredictionProbability(0.45).getIdPart(); - String raId6 = createRiskAssessmentWithPredictionProbability(0.55).getIdPart(); - assertFindIds("when le", Set.of(raId2, raId3, raId4), "/RiskAssessment?probability=ge0.2&probability=lt0.45"); - } - - @Test - void orClauses() { - String raId1 = createRiskAssessmentWithPredictionProbability(0.15).getIdPart(); - String raId2 = createRiskAssessmentWithPredictionProbability(0.20).getIdPart(); - String raId3 = createRiskAssessmentWithPredictionProbability(0.25).getIdPart(); - String raId4 = createRiskAssessmentWithPredictionProbability(0.35).getIdPart(); - String raId5 = createRiskAssessmentWithPredictionProbability(0.45).getIdPart(); - String raId6 = createRiskAssessmentWithPredictionProbability(0.55).getIdPart(); - assertFindIds("when le", Set.of(raId1, raId2, raId3, raId6), "/RiskAssessment?probability=le0.25,gt0.50"); - } } - private IIdType createRiskAssessment() { - return (createRiskAssessmentWithPredictionProbability(null)); - } - - private IIdType createRiskAssessmentWithPredictionProbability(Number theProbability) { - RiskAssessment ra1 = new RiskAssessment(); - if (theProbability != null) { - RiskAssessment.RiskAssessmentPredictionComponent component = ra1.addPrediction(); - component.setProbability(new DecimalType(theProbability.doubleValue())); - } - return myRiskAssessmentDao.create(ra1).getId().toUnqualifiedVersionless(); - } - - - @Disabled("keeping to debug search scrolling") - @Test - public void withoutCount() { - createObservations(600); - - SearchParameterMap map = new SearchParameterMap(); - map.add("code", new TokenParam().setSystem("http://example.com")); - List bp = myObservationDao.searchForIds(map, new ServletRequestDetails()); - assertNotNull(bp); - assertEquals(600, bp.size()); - - } - - - private void createObservations(int theCount) { - for (int i = 0; i < theCount; i++) { - myTestDataBuilder.createObservation(asArray( - myTestDataBuilder.withObservationCode("http://example.com", "code-" + i))); - } - } - - - private Consumer[] asArray(Consumer theIBaseResourceConsumer) { - @SuppressWarnings("unchecked") - Consumer[] array = (Consumer[]) new Consumer[]{theIBaseResourceConsumer}; - return array; - } - - @Nested class CompositeSearch extends CompositeSearchParameterTestCases { CompositeSearch() { super(myTestDataBuilder.getTestDataBuilderSupport(), myTestDaoSearch); } - /** HSearch supports it! */ + /** + * HSearch supports it! + */ @Override protected boolean isCorrelatedSupported() { return true; @@ -2268,59 +2296,11 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl @Override protected void beforeOrAfterTestClass(TestContext testContext, DirtiesContext.ClassMode requiredClassMode) throws Exception { - if ( ! testContext.getTestClass().getName().contains("$")) { + if (!testContext.getTestClass().getName().contains("$")) { super.beforeOrAfterTestClass(testContext, requiredClassMode); } } } - private List getResultIds(IBundleProvider theResult) { - return theResult.getAllResources().stream().map(r -> r.getIdElement().getIdPart()).collect(Collectors.toList()); - } - - private void assertFindId(String theMessage, IIdType theResourceId, String theUrl) { - List resourceIds = myTestDaoSearch.searchForIds(theUrl); - assertThat(theMessage, resourceIds, hasItem(equalTo(theResourceId.getIdPart()))); - } - - private void assertFindIds(String theMessage, Collection theResourceIds, String theUrl) { - List resourceIds = myTestDaoSearch.searchForIds(theUrl); - assertEquals(theResourceIds, new HashSet<>(resourceIds), theMessage); - } - - private void assertNotFindId(String theMessage, IIdType theResourceId, String theUrl) { - List resourceIds = myTestDaoSearch.searchForIds(theUrl); - assertThat(theMessage, resourceIds, not(hasItem(equalTo(theResourceId.getIdPart())))); - } - - - - /** - * Search for resources in the first query, instead of searching for IDs first - */ - public List searchForFastResources(String theQueryUrl) { - SearchParameterMap map = myTestDaoSearch.toSearchParameters(theQueryUrl); - map.setLoadSynchronous(true); - - SortSpec sort = (SortSpec) new ca.uhn.fhir.rest.server.method.SortParameter(myFhirCtx) - .translateQueryParametersIntoServerArgument(fakeRequestDetailsFromUrl(theQueryUrl), null); - if (sort != null) { - map.setSort(sort); - } - - return myTestDaoSearch.searchForResources(theQueryUrl); - } - - - @Nonnull - private SystemRequestDetails fakeRequestDetailsFromUrl(String theQueryUrl) { - SystemRequestDetails request = new SystemRequestDetails(); - UriComponents uriComponents = UriComponentsBuilder.fromUriString(theQueryUrl).build(); - uriComponents.getQueryParams() - .forEach((key, value) -> request.addParameter(key, value.toArray(new String[0]))); - return request; - } - - } diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java index f8ff57c5cc5..e2f9a4c4dcb 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java @@ -20,7 +20,7 @@ import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; import ca.uhn.fhir.jpa.test.BaseJpaTest; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; @@ -87,7 +87,7 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest { @Qualifier("myValueSetDaoR4") protected IFhirResourceDaoValueSet myValueSetDao; @Autowired - protected ITermReadSvcR4 myTermSvc; + protected ITermReadSvc myTermSvc; @Autowired protected ITermDeferredStorageSvc myTerminologyDeferredStorageSvc; @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -245,13 +245,13 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest { // need to be more than elastic [index.max_result_window] index level setting (default = 10_000) addTermConcepts(codeSystemVersion, 11_000); - ValueSet valueSet = getValueSetWithAllCodeSystemConcepts( codeSystemVersion.getCodeSystemVersionId() ); + ValueSet valueSet = getValueSetWithAllCodeSystemConcepts(codeSystemVersion.getCodeSystemVersionId()); myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(codeSystem, codeSystemVersion, new SystemRequestDetails(), Collections.singletonList(valueSet), Collections.emptyList()); myTerminologyDeferredStorageSvc.saveAllDeferred(); - await().atMost(10, SECONDS).until( myTerminologyDeferredStorageSvc::isStorageQueueEmpty ); + await().atMost(10, SECONDS).until(myTerminologyDeferredStorageSvc::isStorageQueueEmpty); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -262,7 +262,6 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest { } - private ValueSet getValueSetWithAllCodeSystemConcepts(String theCodeSystemVersionId) { ValueSet vs = new ValueSet(); vs.setId(LOINC_ALL_VALUESET_ID); @@ -316,8 +315,8 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest { valueSet.setDescription("A value set that includes all LOINC codes"); valueSet.getCompose().addInclude().setSystem(CS_URL).setVersion(codeSystemVersion.getCodeSystemVersionId()); - assertDoesNotThrow( () -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(codeSystem, codeSystemVersion, - new SystemRequestDetails(), Collections.singletonList(valueSet), Collections.emptyList() )); + assertDoesNotThrow(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(codeSystem, codeSystemVersion, + new SystemRequestDetails(), Collections.singletonList(valueSet), Collections.emptyList())); } diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/resources/logback-test.xml b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..84d0a1fcf5f --- /dev/null +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/resources/logback-test.xml @@ -0,0 +1,12 @@ + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n + + + + + + + + diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index d7de744eebb..0f28a1e0c63 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java index cd15ee44bf6..3a04ef3f628 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java @@ -88,7 +88,7 @@ public class MdmEventIT extends BaseMdmR4Test { ResourceOperationMessage resourceOperationMessage = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(ResourceOperationMessage.class); assertNotNull(resourceOperationMessage); - assertEquals(pr.getId(), resourceOperationMessage.getId()); + assertEquals(pr.getIdElement().toUnqualifiedVersionless().getValue(), resourceOperationMessage.getId()); MdmLink link = getLinkByTargetId(pr); diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 97de581fddc..0eaa7ff9093 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index adff3599438..e481b81db16 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -62,6 +62,11 @@ hapi-fhir-structures-r4 ${project.version} + + ca.uhn.hapi.fhir + hapi-fhir-structures-r4b + ${project.version} + ca.uhn.hapi.fhir hapi-fhir-structures-r5 diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java index dab9416b674..836b2258303 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor; import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu2; import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3; import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4; +import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4B; import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR5; import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher; @@ -66,6 +67,8 @@ public class SearchParamConfig { return new SearchParamExtractorDstu3(); case R4: return new SearchParamExtractorR4(); + case R4B: + return new SearchParamExtractorR4B(); case R5: return new SearchParamExtractorR5(); case DSTU2_HL7ORG: diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java new file mode 100644 index 00000000000..02eb26cb9ae --- /dev/null +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java @@ -0,0 +1,195 @@ +package ca.uhn.fhir.jpa.searchparam.extractor; + +/* + * #%L + * HAPI FHIR Search Parameters + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.model.entity.ModelConfig; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.google.common.annotations.VisibleForTesting; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.PathEngineException; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.r4b.context.IWorkerContext; +import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext; +import org.hl7.fhir.r4b.model.Base; +import org.hl7.fhir.r4b.model.ExpressionNode; +import org.hl7.fhir.r4b.model.IdType; +import org.hl7.fhir.r4b.model.Resource; +import org.hl7.fhir.r4b.model.ResourceType; +import org.hl7.fhir.r4b.model.TypeDetails; +import org.hl7.fhir.r4b.model.ValueSet; +import org.hl7.fhir.r4b.utils.FHIRPathEngine; + +import javax.annotation.PostConstruct; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class SearchParamExtractorR4B extends BaseSearchParamExtractor implements ISearchParamExtractor { + + private Cache myParsedFhirPathCache; + private FHIRPathEngine myFhirPathEngine; + + /** + * Constructor + */ + public SearchParamExtractorR4B() { + super(); + } + + // This constructor is used by tests + @VisibleForTesting + public SearchParamExtractorR4B(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry) { + super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry); + initFhirPath(); + start(); + } + + @Override + public IValueExtractor getPathValueExtractor(IBase theResource, String theSinglePath) { + return () -> { + ExpressionNode parsed = myParsedFhirPathCache.get(theSinglePath, path -> myFhirPathEngine.parse(path)); + return myFhirPathEngine.evaluate((Base) theResource, parsed); + }; + } + + + @Override + @PostConstruct + public void start() { + super.start(); + if (myFhirPathEngine == null) { + initFhirPath(); + } + } + + public void initFhirPath() { + IWorkerContext worker = new HapiWorkerContext(getContext(), getContext().getValidationSupport()); + myFhirPathEngine = new FHIRPathEngine(worker); + myFhirPathEngine.setHostServices(new SearchParamExtractorR4BHostServices()); + + myParsedFhirPathCache = Caffeine + .newBuilder() + .expireAfterWrite(10, TimeUnit.MINUTES) + .build(); + } + + + private static class SearchParamExtractorR4BHostServices implements FHIRPathEngine.IEvaluationContext { + + private final Map myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>()); + + @Override + public List resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { + return null; + } + + @Override + public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + return null; + } + + @Override + public boolean log(String argument, List focus) { + return false; + } + + @Override + public FunctionDetails resolveFunction(String functionName) { + return null; + } + + @Override + public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException { + return null; + } + + @Override + public List executeFunction(Object appContext, List focus, String functionName, List> parameters) { + return null; + } + + + @Override + public Base resolveReference(Object theAppContext, String theUrl, Base refContext) throws FHIRException { + + /* + * When we're doing resolution within the SearchParamExtractor, if we want + * to do a resolve() it's just to check the type, so there is no point + * going through the heavyweight test. We can just return a stub and + * that's good enough since we're just doing something like + * Encounter.patient.where(resolve() is Patient) + */ + IdType url = new IdType(theUrl); + Base retVal = null; + if (isNotBlank(url.getResourceType())) { + + retVal = myResourceTypeToStub.get(url.getResourceType()); + if (retVal != null) { + return retVal; + } + + ResourceType resourceType = ResourceType.fromCode(url.getResourceType()); + if (resourceType != null) { + retVal = new Resource() { + private static final long serialVersionUID = -5303169871827706447L; + + @Override + public Resource copy() { + return this; + } + + @Override + public ResourceType getResourceType() { + return resourceType; + } + + @Override + public String fhirType() { + return url.getResourceType(); + } + + }; + myResourceTypeToStub.put(url.getResourceType(), retVal); + } + } + return retVal; + } + + @Override + public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + return false; + } + + @Override + public ValueSet resolveValueSet(Object appContext, String url) { + return null; + } + } + +} diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 19539c2307e..a4e3528490d 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 18d82d2d048..e657e8ffcf0 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java index e7b395c0b9e..36cb7cf1f7d 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java @@ -10,13 +10,12 @@ import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamStringDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamTokenDao; import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.model.entity.ModelConfig; -import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; +import ca.uhn.fhir.jpa.provider.JpaSystemProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistryController; @@ -203,7 +202,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest { protected IFhirSystemDao mySystemDao; @Autowired @Qualifier("mySystemProviderDstu2") - protected JpaSystemProviderDstu2 mySystemProvider; + protected JpaSystemProvider mySystemProvider; @Autowired protected PlatformTransactionManager myTxManager; @Autowired diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java index 63fadfb48dd..79be8f8e103 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java @@ -2,13 +2,10 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.dao.dstu2.BaseJpaDstu2Test; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; -import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.FhirVersionIndependentConcept; -import org.hl7.fhir.instance.model.api.IBaseCoding; -import org.hl7.fhir.r4.model.Coding; import org.junit.jupiter.api.Test; import org.mockito.Answers; import org.mockito.Mock; @@ -20,7 +17,6 @@ import java.util.Set; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; -import static org.junit.jupiter.api.Assertions.assertEquals; public class TerminologySvcImplDstu2Test extends BaseJpaDstu2Test { @@ -30,15 +26,6 @@ public class TerminologySvcImplDstu2Test extends BaseJpaDstu2Test { @Mock(answer = Answers.RETURNS_DEEP_STUBS) protected ServletRequestDetails mySrd; - @Test - public void testToCanonicalCoding() { - TermReadSvcDstu2 myReadSvc = new TermReadSvcDstu2(); - IBaseCoding myCoding = new CodingDt("dstuSystem", "dstuCode"); - Coding convertedCoding = myReadSvc.toCanonicalCoding(myCoding); - assertEquals("dstuCode", convertedCoding.getCode()); - assertEquals("dstuSystem", convertedCoding.getSystem()); - } - @Test public void testFindCodesBelowBuiltInCodeSystem() { List concepts; diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 51ce5374ab8..a494c1bef83 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3CodeSystemTest.java index 06625ba96f4..f43cacce217 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3CodeSystemTest.java @@ -103,17 +103,4 @@ public class FhirResourceDaoDstu3CodeSystemTest extends BaseJpaDstu3Test { } - @Test - public void testValidateCodeForCodeSystemOperationNotSupported() { - try { - myCodeSystemDao.validateCode(null, null, null, null, null, null, null, null); - fail(); - } catch (UnsupportedOperationException theE) { - assertNotNull(theE); - } - - } - - - } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/QuestionnaireResourceProviderDstu3.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/QuestionnaireResourceProviderDstu3.java index 3d3a35958aa..c6d1af5974a 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/QuestionnaireResourceProviderDstu3.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/QuestionnaireResourceProviderDstu3.java @@ -1,9 +1,10 @@ package ca.uhn.fhir.jpa.provider.dstu3; +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import org.hl7.fhir.dstu3.model.Questionnaire; import org.hl7.fhir.instance.model.api.IBaseResource; -public class QuestionnaireResourceProviderDstu3 extends JpaResourceProviderDstu3 { +public class QuestionnaireResourceProviderDstu3 extends BaseJpaResourceProvider { @Override public Class getResourceType() { diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java index c82e7becacc..6c110dd47af 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java @@ -19,6 +19,7 @@ import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -28,16 +29,17 @@ import java.io.IOException; import java.util.stream.Collectors; import static ca.uhn.fhir.batch2.jobs.termcodesystem.TermCodeSystemJobConfig.TERM_CODE_SYSTEM_DELETE_JOB_NAME; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDstu3Test { - @Autowired - private Batch2JobHelper myBatchJobHelper; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu3CodeSystemTest.class); public static FhirContext ourCtx = FhirContext.forDstu3Cached(); + @Autowired + private Batch2JobHelper myBatchJobHelper; @BeforeEach @Transactional @@ -53,8 +55,8 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst public void testLookupOnExternalCode() { ResourceProviderDstu3ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, myCaptureQueriesListener); - runInTransaction(()->{ - ourLog.info("Code system versions:\n * " + myTermCodeSystemVersionDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * "))); + runInTransaction(() -> { + ourLog.info("Code system versions:\n * " + myTermCodeSystemVersionDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * "))); }); Parameters respParam = ourClient @@ -352,17 +354,22 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst @Test public void testValidateCodeOperation() { - + Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType("https://url")); inParams.addParameter().setName("code").setValue(new CodeType("1")); - try { - ourClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); - fail(); - } catch (InvalidRequestException e) { - assertEquals("HTTP 400 Bad Request: Invalid request: The FHIR endpoint on this server does not know how to handle POST operation[CodeSystem/$validate-code] with parameters [[]]", e.getMessage()); - } + Parameters outcome = ourClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); + + String message = outcome + .getParameter() + .stream() + .filter(t -> t.getName().equals("message")) + .map(t -> ((IPrimitiveType) t.getValue()).getValue()) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + assertThat(message, containsString("Code is not found in CodeSystem: https://url")); } } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3StructureDefinitionTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3StructureDefinitionTest.java index cb596eb69a5..fc08065a33d 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3StructureDefinitionTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3StructureDefinitionTest.java @@ -90,7 +90,7 @@ public class ResourceProviderDstu3StructureDefinitionTest extends BaseResourcePr .returnResourceType(StructureDefinition.class) .execute(); } catch (ResourceNotFoundException e) { - assertEquals("HTTP 404 Not Found: " + Msg.code(1152) + "No StructureDefiniton found with url = 'http://hl7.org/fhir/StructureDefinition/FOO'", e.getMessage()); + assertEquals("HTTP 404 Not Found: " + Msg.code(1162) + "No StructureDefiniton found with url = 'http://hl7.org/fhir/StructureDefinition/FOO'", e.getMessage()); } } } diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 5139c59f59d..1a3d7dd28a3 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -31,6 +31,7 @@ 1 + true false alphabetical diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ChainingR4SearchTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ChainingR4SearchTest.java index e541aeb6ccb..02dc60e694f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ChainingR4SearchTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ChainingR4SearchTest.java @@ -289,10 +289,15 @@ public class ChainingR4SearchTest extends BaseJpaR4Test { Observation obs = new Observation(); obs.getContained().add(p); - obs.getCode().setText("Observation 1"); - obs.setValue(new StringType("Test")); + + obs.getCode().addCoding().setCode("29463-7").setSystem("http://loinc.org").setDisplay("Body Weight"); + obs.setStatus(Observation.ObservationStatus.FINAL); + obs.setSubject(new Reference(p.getId())); + obs.setValue(new Quantity(null, 67.1, "http://unitsofmeasure.org", "kg", "kg")); obs.getSubject().setReference("#pat"); + ourLog.info("Resource: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); // Create a dummy record so that an unconstrained query doesn't pass the test due to returning the only record diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamIT.java index ebca1497ff6..1f77455b728 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamIT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamIT.java @@ -1,8 +1,13 @@ package ca.uhn.fhir.jpa.dao.r4; +import ca.uhn.fhir.batch2.api.IJobCoordinator; +import ca.uhn.fhir.batch2.jobs.reindex.ReindexAppCtx; +import ca.uhn.fhir.batch2.jobs.reindex.ReindexJobParameters; +import ca.uhn.fhir.batch2.model.JobInstanceStartRequest; import ca.uhn.fhir.context.ComboSearchParamType; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl; @@ -38,6 +43,7 @@ import org.hl7.fhir.r4.model.ServiceRequest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; @@ -66,6 +72,9 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ComboUniqueParamIT.class); + @Autowired + private IJobCoordinator myJobCoordinator; + @AfterEach public void purgeUniqueIndexes() { myResourceIndexedCompositeStringUniqueDao.deleteAll(); @@ -661,15 +670,17 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test { } - @Tag("intermittent") -// @Test - public void testDuplicateUniqueValuesAreReIndexed() throws Exception { + /** + * Note: This test was tagged as @Intermittent - I have removed that tag as I've + * refactored the whole class to use Batch2 reindexing instead of the resource + * reindexing service. It seems like the old service was the cause of intermittents + * as far as i can see, but who knows... + */ + @Test + public void testDuplicateUniqueValuesAreReIndexed() { myDaoConfig.setSchedulingDisabled(true); myDaoConfig.setReindexThreadCount(1); - ResourceReindexingSvcImpl svc = SpringObjectCaster.getTargetObject(myResourceReindexingSvc, ResourceReindexingSvcImpl.class); - svc.initExecutor(); - List uniqueSearchParams = mySearchParamRegistry.getActiveComboSearchParams("Observation"); assertEquals(0, uniqueSearchParams.size()); @@ -703,10 +714,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test { assertEquals(1, uniqueSearchParams.size()); assertEquals(3, uniqueSearchParams.get(0).getComponents().size()); - myResourceReindexingSvc.markAllResourcesForReindexing(); - assertEquals(6, myResourceReindexingSvc.forceReindexingPass()); - assertEquals(1, myResourceReindexingSvc.forceReindexingPass()); - assertEquals(0, myResourceReindexingSvc.forceReindexingPass()); + executeReindex(); runInTransaction(() -> { List uniques = myResourceIndexedCompositeStringUniqueDao.findAll(); @@ -719,11 +727,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test { assertEquals(1, mySearchParamRegistry.getActiveComboSearchParams("Observation").size()); - myResourceReindexingSvc.markAllResourcesForReindexing("Observation"); - assertEquals(1, myResourceReindexingSvc.forceReindexingPass()); - myResourceReindexingSvc.forceReindexingPass(); - myResourceReindexingSvc.forceReindexingPass(); - assertEquals(0, myResourceReindexingSvc.forceReindexingPass()); + executeReindex(); runInTransaction(() -> { List uniques = myResourceIndexedCompositeStringUniqueDao.findAll(); @@ -853,13 +857,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test { createUniqueIndexCoverageBeneficiary(); - myResourceReindexingSvc.markAllResourcesForReindexing("Coverage"); - // The first pass as a low of EPOCH - assertEquals(1, myResourceReindexingSvc.forceReindexingPass()); - // The second pass has a low of Coverage.lastUpdated - assertEquals(1, myResourceReindexingSvc.forceReindexingPass()); - // The third pass has a low of (Coverage.lastUpdated + 1ms) - assertEquals(0, myResourceReindexingSvc.forceReindexingPass()); + executeReindex("Coverage?"); runInTransaction(() -> { List tables = myResourceTableDao.findAll(); @@ -882,6 +880,18 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test { } + private void executeReindex(String... theUrls) { + ReindexJobParameters parameters = new ReindexJobParameters(); + for (String url : theUrls) { + parameters.addUrl(url); + } + JobInstanceStartRequest startRequest = new JobInstanceStartRequest(); + startRequest.setJobDefinitionId(ReindexAppCtx.JOB_REINDEX); + startRequest.setParameters(parameters); + Batch2JobStartResponse res = myJobCoordinator.startInstance(startRequest); + myBatch2JobHelper.awaitJobCompletion(res); + } + @Test public void testIndexTransactionWithMatchUrl2() { @@ -1478,9 +1488,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test { pt2.setActive(false); myPatientDao.create(pt1).getId().toUnqualifiedVersionless(); - myResourceReindexingSvc.markAllResourcesForReindexing(); - myResourceReindexingSvc.forceReindexingPass(); - myResourceReindexingSvc.forceReindexingPass(); + executeReindex(); runInTransaction(() -> { List uniques = myResourceIndexedCompositeStringUniqueDao.findAll(); @@ -1493,9 +1501,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test { myResourceIndexedCompositeStringUniqueDao.deleteAll(); }); - myResourceReindexingSvc.markAllResourcesForReindexing(); - myResourceReindexingSvc.forceReindexingPass(); - myResourceReindexingSvc.forceReindexingPass(); + executeReindex(); runInTransaction(() -> { List uniques = myResourceIndexedCompositeStringUniqueDao.findAll(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java index bc91cfefa42..ba5e19c9bc2 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java @@ -14,7 +14,7 @@ import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test; import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl; +import ca.uhn.fhir.jpa.term.TermReadSvcImpl; import ca.uhn.fhir.jpa.util.SqlQuery; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.SortSpec; @@ -94,7 +94,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myDaoConfig.setResourceClientIdStrategy(new DaoConfig().getResourceClientIdStrategy()); myDaoConfig.setTagStorageMode(new DaoConfig().getTagStorageMode()); - BaseTermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false); + TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false); } @Override @@ -2557,7 +2557,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test @Test public void testValueSetExpand_NotPreExpanded_DontUseHibernateSearch() { - BaseTermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(true); + TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(true); createLocalCsAndVs(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithHSearchDisabledTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithHSearchDisabledTest.java index 943acedb6a9..7c5390c4c0f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithHSearchDisabledTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithHSearchDisabledTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.i18n.Msg; @@ -81,21 +82,6 @@ public class FhirResourceDaoR4SearchWithHSearchDisabledTest extends BaseJpaTest @Autowired protected ISearchParamRegistry mySearchParamRegistry; @Autowired - @Qualifier("myAllergyIntoleranceDaoR4") - private IFhirResourceDao myAllergyIntoleranceDao; - @Autowired - @Qualifier("myAppointmentDaoR4") - private IFhirResourceDao myAppointmentDao; - @Autowired - @Qualifier("myAuditEventDaoR4") - private IFhirResourceDao myAuditEventDao; - @Autowired - @Qualifier("myBundleDaoR4") - private IFhirResourceDao myBundleDao; - @Autowired - @Qualifier("myCarePlanDaoR4") - private IFhirResourceDao myCarePlanDao; - @Autowired @Qualifier("myCodeSystemDaoR4") private IFhirResourceDao myCodeSystemDao; @Autowired @@ -105,35 +91,11 @@ public class FhirResourceDaoR4SearchWithHSearchDisabledTest extends BaseJpaTest @Qualifier("myObservationDaoR4") private IFhirResourceDao myObservationDao; @Autowired - @Qualifier("myCompartmentDefinitionDaoR4") - private IFhirResourceDao myCompartmentDefinitionDao; - @Autowired - @Qualifier("myConceptMapDaoR4") - private IFhirResourceDao myConceptMapDao; - @Autowired - @Qualifier("myConditionDaoR4") - private IFhirResourceDao myConditionDao; - @Autowired - @Qualifier("myDeviceDaoR4") - private IFhirResourceDao myDeviceDao; - @Autowired - @Qualifier("myDiagnosticReportDaoR4") - private IFhirResourceDao myDiagnosticReportDao; - @Autowired - @Qualifier("myEncounterDaoR4") - private IFhirResourceDao myEncounterDao; - // @PersistenceContext() - @Autowired - private EntityManager myEntityManager; - @Autowired private FhirContext myFhirCtx; @Autowired @Qualifier("myOrganizationDaoR4") private IFhirResourceDao myOrganizationDao; @Autowired - @Qualifier("myJpaValidationSupportChain") - private IValidationSupport myValidationSupport; - @Autowired private IFhirSystemDao mySystemDao; @Autowired private IResourceReindexingSvc myResourceReindexingSvc; @@ -141,6 +103,7 @@ public class FhirResourceDaoR4SearchWithHSearchDisabledTest extends BaseJpaTest private IBulkDataExportJobSchedulingHelper myBulkDataScheduleHelper; @Autowired private ITermReadSvc myTermSvc; + private final ValueSetTestUtil myValueSetTestUtil = new ValueSetTestUtil(FhirVersionEnum.R4); @BeforeEach @Transactional() @@ -296,7 +259,7 @@ public class FhirResourceDaoR4SearchWithHSearchDisabledTest extends BaseJpaTest // Non Pre-Expanded ValueSet outcome = myValueSetDao.expand(vs, new ValueSetExpansionOptions()); assertEquals("ValueSet \"ValueSet.url[http://vs]\" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: NOT_EXPANDED | The ValueSet is waiting to be picked up and pre-expanded by a scheduled task.", outcome.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE)); - assertThat(ValueSetTestUtil.toCodes(outcome).toString(), ValueSetTestUtil.toCodes(outcome), contains( + assertThat(myValueSetTestUtil.toCodes(outcome).toString(), myValueSetTestUtil.toCodes(outcome), contains( "code5", "code4", "code3", "code2", "code1" )); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index f17e681c475..68cd5ad1fdd 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -11,7 +11,7 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl; +import ca.uhn.fhir.jpa.term.TermReadSvcImpl; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; @@ -121,7 +121,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { myDaoConfig.setMaximumExpansionSize(DaoConfig.DEFAULT_MAX_EXPANSION_SIZE); myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); - BaseTermReadSvcImpl.setInvokeOnNextCallForUnitTest(null); + TermReadSvcImpl.setInvokeOnNextCallForUnitTest(null); myValidationSettings.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.IGNORE); myFhirContext.setParserErrorHandler(new StrictErrorHandler()); @@ -1193,7 +1193,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { @Test @Disabled public void testValidate_TermSvcHasDatabaseRollback() { - BaseTermReadSvcImpl.setInvokeOnNextCallForUnitTest(() -> { + TermReadSvcImpl.setInvokeOnNextCallForUnitTest(() -> { try { myResourceTableDao.save(new ResourceTable()); myResourceTableDao.flush(); @@ -1232,7 +1232,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); myCodeSystemDao.create(cs); - BaseTermReadSvcImpl.setInvokeOnNextCallForUnitTest(() -> { + TermReadSvcImpl.setInvokeOnNextCallForUnitTest(() -> { throw new NullPointerException("MY ERROR"); }); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java index 2bc3ae509b8..737b0789001 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.dao.r4; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.entity.TermValueSet; @@ -31,6 +32,7 @@ public class FhirResourceDaoR4ValueSetMultiVersionTest extends BaseJpaR4Test { public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; @Autowired protected ITermValueSetConceptDao myTermValueSetConceptDao; + private final ValueSetTestUtil myValueSetTestUtil = new ValueSetTestUtil(FhirVersionEnum.R4); private DaoMethodOutcome createLocalCsAndVs(String theVersion, Set theCodeSystemCodes) { CodeSystem codeSystem = new CodeSystem(); @@ -247,7 +249,7 @@ public class FhirResourceDaoR4ValueSetMultiVersionTest extends BaseJpaR4Test { include.addConcept().setCode("A"); ValueSet expansion = myValueSetDao.expand(vs, null); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expansion), Matchers.contains("A")); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expansion), Matchers.contains("A")); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java index 1c30b124880..75c2bd47ff8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java @@ -8,7 +8,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; -import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl; +import ca.uhn.fhir.jpa.term.TermReadSvcImpl; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; @@ -49,7 +49,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test { @AfterEach public void after() { - BaseTermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false); + TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false); myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); myDaoConfig.setMaximumExpansionSize(new DaoConfig().getMaximumExpansionSize()); } @@ -201,7 +201,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test { @Test public void testValidateCodeInValueSet_HierarchicalAndEnumeratedValueset_HibernateSearchDisabled() { - BaseTermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(true); + TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(true); myValueSetDao.delete(myExtensionalVsId); 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 bf2a6cfd2f1..9cc527f9443 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 @@ -302,10 +302,12 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes post.setEntity(new ResourceEntity(this.getFhirContext(), theInputParameters)); ourLog.info("Request: {}", post); try (CloseableHttpResponse response = ourHttpClient.execute(post)) { + String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info("Response: {}", responseString); assertEquals(200, response.getStatusLine().getStatusCode()); return theEncoding.newParser(myFhirContext).parseResource(Parameters.class, - IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8)); + responseString); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/QuestionnaireResourceProviderR4.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/QuestionnaireResourceProviderR4.java index 72a38cbd65c..f2dfaaa4f69 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/QuestionnaireResourceProviderR4.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/QuestionnaireResourceProviderR4.java @@ -1,9 +1,10 @@ package ca.uhn.fhir.jpa.provider.r4; +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Questionnaire; -public class QuestionnaireResourceProviderR4 extends JpaResourceProviderR4 { +public class QuestionnaireResourceProviderR4 extends BaseJpaResourceProvider { @Override public Class getResourceType() { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java index 7ac385d3e63..3765e991906 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java @@ -96,6 +96,8 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247")); createNextCodeSystemReturnParameters(true, DISPLAY, null); + logAllConcepts(); + Parameters respParam = myClient .operation() .onType(CodeSystem.class) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4StructureDefinitionTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4StructureDefinitionTest.java index 208e851a4bb..d9220f83846 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4StructureDefinitionTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4StructureDefinitionTest.java @@ -108,7 +108,7 @@ public class ResourceProviderR4StructureDefinitionTest extends BaseResourceProvi .returnResourceType(StructureDefinition.class) .execute(); } catch (ResourceNotFoundException e) { - assertEquals("HTTP 404 Not Found: " + Msg.code(1159) + "No StructureDefiniton found with url = 'http://hl7.org/fhir/StructureDefinition/FOO'", e.getMessage()); + assertEquals("HTTP 404 Not Found: " + Msg.code(1162) + "No StructureDefiniton found with url = 'http://hl7.org/fhir/StructureDefinition/FOO'", e.getMessage()); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java index c9f1a901a37..3c9a0ee995b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java @@ -56,8 +56,8 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import static ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.DEFAULT_MASS_INDEXER_OBJECT_LOADING_THREADS; -import static ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.MAX_MASS_INDEXER_OBJECT_LOADING_THREADS; +import static ca.uhn.fhir.jpa.term.TermReadSvcImpl.DEFAULT_MASS_INDEXER_OBJECT_LOADING_THREADS; +import static ca.uhn.fhir.jpa.term.TermReadSvcImpl.MAX_MASS_INDEXER_OBJECT_LOADING_THREADS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -77,11 +77,14 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ITermReadSvcTest { - private final ITermReadSvc testedClass = new TermReadSvcR4(); + private final ITermReadSvc testedClass = new TermReadSvcImpl(); - @Mock private ITermValueSetDao myTermValueSetDao; - @Mock private DaoRegistry myDaoRegistry; - @Mock private IFhirResourceDao myFhirResourceDao; + @Mock + private ITermValueSetDao myTermValueSetDao; + @Mock + private DaoRegistry myDaoRegistry; + @Mock + private IFhirResourceDao myFhirResourceDao; @Nested @@ -200,9 +203,12 @@ class ITermReadSvcTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private EntityManager myEntityManager; - @Mock private ResourceTable resource1; - @Mock private ResourceTable resource2; - @Mock private IBaseResource myCodeSystemResource; + @Mock + private ResourceTable resource1; + @Mock + private ResourceTable resource2; + @Mock + private IBaseResource myCodeSystemResource; @BeforeEach @@ -260,9 +266,12 @@ class ITermReadSvcTest { public static final String CODE_4 = "code-4"; public static final String CODE_5 = "code-5"; - @Mock TermConcept termConceptCode1; - @Mock TermConcept termConceptCode3; - @Mock TermConcept termConceptCode4; + @Mock + TermConcept termConceptCode1; + @Mock + TermConcept termConceptCode3; + @Mock + TermConcept termConceptCode4; @Test public void morePropertiesThanValues() { @@ -306,17 +315,22 @@ class ITermReadSvcTest { @Nested public class TestReindexTerminology { - @Mock private SearchSession mySearchSession; + @Mock + private SearchSession mySearchSession; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private MassIndexer myMassIndexer; - @Mock private IFulltextSearchSvc myFulltextSearchSvc; - @Mock private ITermDeferredStorageSvc myDeferredStorageSvc; - @Mock private HibernatePropertiesProvider myHibernatePropertiesProvider; + @Mock + private IFulltextSearchSvc myFulltextSearchSvc; + @Mock + private ITermDeferredStorageSvc myDeferredStorageSvc; + @Mock + private HibernatePropertiesProvider myHibernatePropertiesProvider; @InjectMocks - @Spy private BaseTermReadSvcImpl myTermReadSvc = (BaseTermReadSvcImpl) spy(testedClass); + @Spy + private TermReadSvcImpl myTermReadSvc = (TermReadSvcImpl) spy(testedClass); @Test @@ -334,7 +348,7 @@ class ITermReadSvcTest { public class TestCalculateObjectLoadingThreadNumber { private final BasicDataSource myBasicDataSource = new BasicDataSource(); - private final ProxyDataSource myProxyDataSource = new ProxyDataSource(myBasicDataSource) ; + private final ProxyDataSource myProxyDataSource = new ProxyDataSource(myBasicDataSource); @BeforeEach void setUp() { @@ -373,7 +387,8 @@ class ITermReadSvcTest { @Nested public class TestCalculateObjectLoadingThreadNumberDefault { - @Mock private DataSource myDataSource = new BasicDataSource(); + @Mock + private DataSource myDataSource = new BasicDataSource(); @BeforeEach void setUp() { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index 96cbfc89c49..7d0b993294f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.term; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; @@ -67,6 +68,8 @@ import static org.junit.jupiter.api.Assertions.fail; public class ValueSetExpansionR4Test extends BaseTermR4Test { private static final Logger ourLog = LoggerFactory.getLogger(ValueSetExpansionR4Test.class); + private final ValueSetTestUtil myValueSetTestUtil = new ValueSetTestUtil(FhirVersionEnum.R4); + @Mock private IValueSetConceptAccumulator myValueSetCodeAccumulator; @@ -183,10 +186,10 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ValueSet expandedValueSet = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), input); ourLog.debug("Expanded ValueSet:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); - assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), containsInAnyOrder( + assertThat(myValueSetTestUtil.toCodes(expandedValueSet).toString(), myValueSetTestUtil.toCodes(expandedValueSet), containsInAnyOrder( "code9", "code90", "code91", "code92", "code93", "code94", "code95", "code96", "code97", "code98", "code99" )); - Assertions.assertEquals(11, expandedValueSet.getExpansion().getContains().size(), ValueSetTestUtil.toCodes(expandedValueSet).toString()); + Assertions.assertEquals(11, expandedValueSet.getExpansion().getContains().size(), myValueSetTestUtil.toCodes(expandedValueSet).toString()); assertEquals(11, expandedValueSet.getExpansion().getTotal()); // Make sure we used the pre-expanded version @@ -215,7 +218,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Expansion should contain all codes myCaptureQueriesListener.clear(); ValueSet expandedValueSet = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), input); - List codes = ValueSetTestUtil.toCodes(expandedValueSet); + List codes = myValueSetTestUtil.toCodes(expandedValueSet); assertThat(codes.toString(), codes, containsInAnyOrder("code100", "code1000", "code1001", "code1002", "code1003", "code1004")); // Make sure we used the pre-expanded version @@ -230,7 +233,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { myCaptureQueriesListener.clear(); ValueSetExpansionOptions options = ValueSetExpansionOptions.forOffsetAndCount(0, 1000).setFilter("display value 100"); ValueSet expandedValueSet = myValueSetDao.expand(vsId, options, mySrd); - List codes = ValueSetTestUtil.toCodes(expandedValueSet); + List codes = myValueSetTestUtil.toCodes(expandedValueSet); assertThat(codes.toString(), codes, containsInAnyOrder("code100", "code1000", "code1001", "code1002", "code1003", "code1004")); // Make sure we used the pre-expanded version @@ -273,8 +276,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { expandedConceptCodes.removeIf(concept -> !concept.startsWith("code9")); //Ensure that the subsequent expansion with offset returns the same slice we are anticipating. - assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(offset, offset + count)))); - Assertions.assertEquals(4, expandedValueSet.getExpansion().getContains().size(), ValueSetTestUtil.toCodes(expandedValueSet).toString()); + assertThat(myValueSetTestUtil.toCodes(expandedValueSet).toString(), myValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(offset, offset + count)))); + Assertions.assertEquals(4, expandedValueSet.getExpansion().getContains().size(), myValueSetTestUtil.toCodes(expandedValueSet).toString()); assertEquals(11, expandedValueSet.getExpansion().getTotal()); // Make sure we used the pre-expanded version @@ -301,7 +304,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ValueSet expandedValueSet = myTermSvc.expandValueSet(null, input); ourLog.debug("Expanded ValueSet:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); - assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), contains("code99")); + assertThat(myValueSetTestUtil.toCodes(expandedValueSet).toString(), myValueSetTestUtil.toCodes(expandedValueSet), contains("code99")); // Make sure we used the pre-expanded version List selectQueries = myCaptureQueriesListener.getSelectQueries(); @@ -335,10 +338,10 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ValueSet expandedValueSet = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), input); ourLog.debug("Expanded ValueSet:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); - assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), containsInAnyOrder( + assertThat(myValueSetTestUtil.toCodes(expandedValueSet).toString(), myValueSetTestUtil.toCodes(expandedValueSet), containsInAnyOrder( "code9", "code91", "code92", "code93", "code94", "code95", "code96", "code97", "code98", "code99" )); - Assertions.assertEquals(10, expandedValueSet.getExpansion().getContains().size(), ValueSetTestUtil.toCodes(expandedValueSet).toString()); + Assertions.assertEquals(10, expandedValueSet.getExpansion().getContains().size(), myValueSetTestUtil.toCodes(expandedValueSet).toString()); assertEquals(10, expandedValueSet.getExpansion().getTotal()); // Make sure we used the pre-expanded version @@ -434,11 +437,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ValueSet expandedValueSet = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), input); ourLog.debug("Expanded ValueSet:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); - assertThat(ValueSetTestUtil.toCodes(expandedValueSet).toString(), ValueSetTestUtil.toCodes(expandedValueSet), containsInAnyOrder( + assertThat(myValueSetTestUtil.toCodes(expandedValueSet).toString(), myValueSetTestUtil.toCodes(expandedValueSet), containsInAnyOrder( "code9", "code90", "code91", "code92", "code93", "code94", "code95", "code96", "code97", "code98", "code99" )); - Assertions.assertEquals(11, expandedValueSet.getExpansion().getContains().size(), ValueSetTestUtil.toCodes(expandedValueSet).toString()); - Assertions.assertEquals(11, expandedValueSet.getExpansion().getTotal(), ValueSetTestUtil.toCodes(expandedValueSet).toString()); + Assertions.assertEquals(11, expandedValueSet.getExpansion().getContains().size(), myValueSetTestUtil.toCodes(expandedValueSet).toString()); + Assertions.assertEquals(11, expandedValueSet.getExpansion().getTotal(), myValueSetTestUtil.toCodes(expandedValueSet).toString()); // Make sure we used the pre-expanded version List selectQueries = myCaptureQueriesListener.getSelectQueries(); @@ -451,8 +454,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Expand ValueSet expansion = myTermSvc.expandValueSet(new ValueSetExpansionOptions(), "http://hl7.org/fhir/ValueSet/administrative-gender"); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expansion), containsInAnyOrder("male", "female", "other", "unknown")); - Assertions.assertEquals("ValueSet with URL \"ValueSet.url[http://hl7.org/fhir/ValueSet/administrative-gender]\" was expanded using an in-memory expansion", ValueSetTestUtil.extractExpansionMessage(expansion)); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expansion), containsInAnyOrder("male", "female", "other", "unknown")); + Assertions.assertEquals("ValueSet with URL \"ValueSet.url[http://hl7.org/fhir/ValueSet/administrative-gender]\" was expanded using an in-memory expansion", myValueSetTestUtil.extractExpansionMessage(expansion)); // Validate Code - Good String codeSystemUrl = "http://hl7.org/fhir/administrative-gender"; @@ -583,8 +586,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertConceptContainsDesignation(otherConcept, "nl", "http://snomed.info/sct", "900000000000013009", "Synonym", "Systolische bloeddruk minimaal 1 uur"); //Ensure they are streamed back in the same order. - List firstExpansionCodes = ValueSetTestUtil.toCodes(reexpandedValueSet); - List secondExpansionCodes = ValueSetTestUtil.toCodes(expandedValueSet); + List firstExpansionCodes = myValueSetTestUtil.toCodes(reexpandedValueSet); + List secondExpansionCodes = myValueSetTestUtil.toCodes(expandedValueSet); assertThat(firstExpansionCodes, is(equalTo(secondExpansionCodes))); //Ensure that internally the designations are expanded back in the same order. @@ -665,11 +668,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals(23, expandedValueSet.getExpansion().getContains().size()); ValueSet.ValueSetExpansionContainsComponent concept = assertExpandedValueSetContainsConcept(expandedValueSet, "http://acme.org", "8450-9", "Systolic blood pressure--expiration", 1); - assertThat(concept.getDesignation().size() , is(equalTo(1))); + assertThat(concept.getDesignation().size(), is(equalTo(1))); assertConceptContainsDesignation(concept, "nl", "http://snomed.info/sct", "900000000000013009", "Synonym", "Systolische bloeddruk - expiratie"); //It is enough to test that the sublist returned is the correct one. - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(0, 23)))); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(0, 23)))); } @Test @@ -704,7 +707,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals(23, expandedValueSet.getExpansion().getContains().size()); //It is enough to test that the sublist returned is the correct one. - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(0, 23)))); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(0, 23)))); } @Test @@ -847,7 +850,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals(1000, expandedValueSet.getExpansion().getParameter().get(1).getValueIntegerType().getValue().intValue()); assertEquals(codeSystem.getConcept().size() - expandedValueSet.getExpansion().getOffset(), expandedValueSet.getExpansion().getContains().size()); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConcepts.subList(1, expandedConcepts.size())))); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConcepts.subList(1, expandedConcepts.size())))); } @Test @@ -879,7 +882,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals(1000, expandedValueSet.getExpansion().getParameter().get(1).getValueIntegerType().getValue().intValue()); assertEquals(codeSystem.getConcept().size() - expandedValueSet.getExpansion().getOffset(), expandedValueSet.getExpansion().getContains().size()); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConcepts.subList(1, expandedConcepts.size())))); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConcepts.subList(1, expandedConcepts.size())))); } @Test @@ -915,7 +918,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals(22, expandedValueSet.getExpansion().getContains().size()); //It is enough to test that the sublist returned is the correct one. - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(1, 23)))); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(1, 23)))); } @Test @@ -966,7 +969,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Make sure it's done runInTransaction(() -> assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://snomed.info/sct"))); - runInTransaction(() -> assertEquals(TermValueSetPreExpansionStatusEnum.FAILED_TO_EXPAND, myTermValueSetDao.findByUrl("http://vs-with-invalid-cs").orElseThrow(()->new IllegalStateException()).getExpansionStatus())); + runInTransaction(() -> assertEquals(TermValueSetPreExpansionStatusEnum.FAILED_TO_EXPAND, myTermValueSetDao.findByUrl("http://vs-with-invalid-cs").orElseThrow(() -> new IllegalStateException()).getExpansionStatus())); // Try expansion again try { @@ -1011,7 +1014,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals(22, expandedValueSet.getExpansion().getContains().size()); //It is enough to test that the sublist returned is the correct one. - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(1, 23)))); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(1, 23)))); } @@ -1046,7 +1049,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Non Pre-Expanded ValueSet outcome = myValueSetDao.expand(vs, new ValueSetExpansionOptions()); assertEquals("ValueSet \"ValueSet.url[http://vs]\" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: NOT_EXPANDED | The ValueSet is waiting to be picked up and pre-expanded by a scheduled task.", outcome.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE)); - assertThat(ValueSetTestUtil.toCodes(outcome).toString(), ValueSetTestUtil.toCodes(outcome), contains( + assertThat(myValueSetTestUtil.toCodes(outcome).toString(), myValueSetTestUtil.toCodes(outcome), contains( "code5", "code4", "code3", "code2", "code1" )); @@ -1057,7 +1060,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { outcome = myValueSetDao.expand(vs, new ValueSetExpansionOptions()); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); assertThat(outcome.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE), containsString("ValueSet was expanded using an expansion that was pre-calculated")); - assertThat(ValueSetTestUtil.toCodes(outcome).toString(), ValueSetTestUtil.toCodes(outcome), contains( + assertThat(myValueSetTestUtil.toCodes(outcome).toString(), myValueSetTestUtil.toCodes(outcome), contains( "code5", "code4", "code3", "code2", "code1" )); @@ -1571,8 +1574,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Expand VS expansion = myValueSetDao.expand(vsId, new ValueSetExpansionOptions(), mySrd); - MatcherAssert.assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("Current status: NOT_EXPANDED")); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expansion), contains("28571000087109")); + MatcherAssert.assertThat(myValueSetTestUtil.extractExpansionMessage(expansion), containsString("Current status: NOT_EXPANDED")); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expansion), contains("28571000087109")); // Validate code - good codeSystemUrl = "http://snomed.info/sct"; @@ -1661,8 +1664,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { } - - @Test public void testExpandValueSet_VsIsEnumeratedWithVersionedSystem_CsIsFragmentWithWrongVersion() { CodeSystem cs = new CodeSystem(); @@ -1696,9 +1697,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // In memory expansion ValueSet expansion = myValueSetDao.expand(vs, new ValueSetExpansionOptions()); ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); - MatcherAssert.assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("has not yet been pre-expanded")); - MatcherAssert.assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("Current status: NOT_EXPANDED")); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expansion), contains("28571000087109")); + MatcherAssert.assertThat(myValueSetTestUtil.extractExpansionMessage(expansion), containsString("has not yet been pre-expanded")); + MatcherAssert.assertThat(myValueSetTestUtil.extractExpansionMessage(expansion), containsString("Current status: NOT_EXPANDED")); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expansion), contains("28571000087109")); codeSystemUrl = "http://snomed.info/sct"; valueSetUrl = "http://ehealthontario.ca/fhir/ValueSet/vaccinecode"; @@ -1719,8 +1720,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Try expansion again expansion = myValueSetDao.expand(vs, new ValueSetExpansionOptions()); ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); - MatcherAssert.assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("ValueSet was expanded using an expansion that was pre-calculated")); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expansion), contains("28571000087109")); + MatcherAssert.assertThat(myValueSetTestUtil.extractExpansionMessage(expansion), containsString("ValueSet was expanded using an expansion that was pre-calculated")); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expansion), contains("28571000087109")); } @Test @@ -1756,7 +1757,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { try { myValueSetDao.expand(vs, new ValueSetExpansionOptions()); } catch (InternalErrorException e) { - assertEquals( Msg.code(888) + "org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport$ExpansionCouldNotBeCompletedInternallyException: " + Msg.code(702) + "Unable to expand ValueSet because CodeSystem could not be found: http://foo-cs|0.17", e.getMessage()); + assertEquals(Msg.code(888) + "org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport$ExpansionCouldNotBeCompletedInternallyException: " + Msg.code(702) + "Unable to expand ValueSet because CodeSystem could not be found: http://foo-cs|0.17", e.getMessage()); } codeSystemUrl = "http://snomed.info/sct"; @@ -1885,7 +1886,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("http://snomed.info/sct/20611000087101/version/20210331", expansionCode.getVersion()); - } @Test @@ -1961,8 +1961,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Expand ValueSet expansion = myValueSetDao.expand(new IdType("ValueSet/vs"), new ValueSetExpansionOptions(), mySrd); ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); - MatcherAssert.assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("ValueSet was expanded using an expansion that was pre-calculated")); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expansion), contains("A")); + MatcherAssert.assertThat(myValueSetTestUtil.extractExpansionMessage(expansion), containsString("ValueSet was expanded using an expansion that was pre-calculated")); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expansion), contains("A")); // Change the CodeSystem cs.getConcept().clear(); @@ -1971,7 +1971,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Previous precalculated expansion should still hold expansion = myValueSetDao.expand(new IdType("ValueSet/vs"), new ValueSetExpansionOptions(), mySrd); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expansion), contains("A")); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expansion), contains("A")); // Invalidate the precalculated expansion myTermSvc.invalidatePreCalculatedExpansion(new IdType("ValueSet/vs"), mySrd); @@ -1979,10 +1979,10 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Expand (should not use a precalculated expansion) expansion = myValueSetDao.expand(new IdType("ValueSet/vs"), new ValueSetExpansionOptions(), mySrd); ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); - MatcherAssert.assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("Performing in-memory expansion without parameters")); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expansion), contains("B")); + MatcherAssert.assertThat(myValueSetTestUtil.extractExpansionMessage(expansion), containsString("Performing in-memory expansion without parameters")); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expansion), contains("B")); - runInTransaction(()->{ + runInTransaction(() -> { List statuses = myTermValueSetDao .findAll() .stream() @@ -1997,7 +1997,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { myTerminologyDeferredStorageSvc.saveAllDeferred(); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - runInTransaction(()->{ + runInTransaction(() -> { List statuses = myTermValueSetDao .findAll() .stream() @@ -2011,8 +2011,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // Expand (should use the new precalculated expansion) expansion = myValueSetDao.expand(new IdType("ValueSet/vs"), new ValueSetExpansionOptions(), mySrd); ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); - MatcherAssert.assertThat(ValueSetTestUtil.extractExpansionMessage(expansion), containsString("ValueSet was expanded using an expansion that was pre-calculated")); - MatcherAssert.assertThat(ValueSetTestUtil.toCodes(expansion), contains("B")); + MatcherAssert.assertThat(myValueSetTestUtil.extractExpansionMessage(expansion), containsString("ValueSet was expanded using an expansion that was pre-calculated")); + MatcherAssert.assertThat(myValueSetTestUtil.toCodes(expansion), contains("B")); // Validate code that is good IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(vs.getUrlElement(), null, new StringType("B"), cs.getUrlElement(), null, null, null, mySrd); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java deleted file mode 100644 index 54fda5a66df..00000000000 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java +++ /dev/null @@ -1,25 +0,0 @@ -package ca.uhn.fhir.jpa.util; - -import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.ValueSet; - -import javax.annotation.Nonnull; -import java.util.List; -import java.util.stream.Collectors; - -import static ca.uhn.fhir.util.HapiExtensions.EXT_VALUESET_EXPANSION_MESSAGE; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ValueSetTestUtil { - public static String extractExpansionMessage(ValueSet outcome) { - List extensions = outcome.getMeta().getExtensionsByUrl(EXT_VALUESET_EXPANSION_MESSAGE); - assertEquals(1, extensions.size()); - String expansionMessage = extensions.get(0).getValueAsPrimitive().getValueAsString(); - return expansionMessage; - } - - @Nonnull - public static List toCodes(ValueSet theExpandedValueSet) { - return theExpandedValueSet.getExpansion().getContains().stream().map(t -> t.getCode()).collect(Collectors.toList()); - } -} diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml new file mode 100644 index 00000000000..1568f458388 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4b/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + ca.uhn.hapi.fhir + hapi-deployable-pom + 6.2.0-PRE17-SNAPSHOT + ../hapi-deployable-pom/pom.xml + + + jar + + HAPI FHIR JPA Server Test R4B + hapi-fhir-jpaserver-test-r4b + + + ca.uhn.hapi.fhir + hapi-fhir-jpaserver-test-utilities + ${project.version} + test + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + 1 + false + alphabetical + + **/*IT.java + + false + + + + + integration-test + verify + + + + + + org.junit.jupiter + junit-jupiter-engine + ${junit_version} + + + + + org.apache.maven.plugins + maven-surefire-plugin + + alphabetical + @{argLine} ${surefire_jvm_args} + 0.6C + *StressTest* + ${skipFailsafe} + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + + + + + + + + NOPARALLEL + + + + org.apache.maven.plugins + maven-surefire-plugin + + 1 + + **/stresstest/* + + + + + + + + CI + + + + org.apache.maven.plugins + maven-surefire-plugin + + 1 + alphabetical + + + + + + + + diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java new file mode 100644 index 00000000000..e5a6c1f0a59 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java @@ -0,0 +1,516 @@ +package ca.uhn.fhir.jpa.dao.r4b; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.interceptor.api.IInterceptorService; +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.dao.IFhirResourceDaoCodeSystem; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoPatient; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoStructureDefinition; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSubscription; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; +import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; +import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; +import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider; +import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper; +import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; +import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; +import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; +import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao; +import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamDateDao; +import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamQuantityDao; +import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamStringDao; +import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamTokenDao; +import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao; +import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao; +import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; +import ca.uhn.fhir.jpa.dao.data.IResourceTagDao; +import ca.uhn.fhir.jpa.dao.data.ISearchDao; +import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao; +import ca.uhn.fhir.jpa.dao.data.ITagDefinitionDao; +import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; +import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; +import ca.uhn.fhir.jpa.dao.data.ITermConceptDao; +import ca.uhn.fhir.jpa.dao.data.ITermConceptDesignationDao; +import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao; +import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementTargetDao; +import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; +import ca.uhn.fhir.jpa.interceptor.PerformanceTracingLoggingInterceptor; +import ca.uhn.fhir.jpa.model.entity.ModelConfig; +import ca.uhn.fhir.jpa.provider.JpaSystemProvider; +import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; +import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; +import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc; +import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl; +import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; +import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; +import ca.uhn.fhir.jpa.test.BaseJpaTest; +import ca.uhn.fhir.jpa.test.config.TestR4BConfig; +import ca.uhn.fhir.jpa.util.ResourceCountCache; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.parser.StrictErrorHandler; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.server.BasePagingProvider; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; +import ca.uhn.fhir.test.utilities.ITestDataBuilder; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.ValidationResult; +import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4b.model.AllergyIntolerance; +import org.hl7.fhir.r4b.model.Appointment; +import org.hl7.fhir.r4b.model.AuditEvent; +import org.hl7.fhir.r4b.model.Binary; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.CarePlan; +import org.hl7.fhir.r4b.model.ChargeItem; +import org.hl7.fhir.r4b.model.CodeSystem; +import org.hl7.fhir.r4b.model.CodeableConcept; +import org.hl7.fhir.r4b.model.Coding; +import org.hl7.fhir.r4b.model.Communication; +import org.hl7.fhir.r4b.model.CommunicationRequest; +import org.hl7.fhir.r4b.model.CompartmentDefinition; +import org.hl7.fhir.r4b.model.ConceptMap; +import org.hl7.fhir.r4b.model.Condition; +import org.hl7.fhir.r4b.model.Consent; +import org.hl7.fhir.r4b.model.Coverage; +import org.hl7.fhir.r4b.model.Device; +import org.hl7.fhir.r4b.model.DiagnosticReport; +import org.hl7.fhir.r4b.model.Encounter; +import org.hl7.fhir.r4b.model.Group; +import org.hl7.fhir.r4b.model.Immunization; +import org.hl7.fhir.r4b.model.ImmunizationRecommendation; +import org.hl7.fhir.r4b.model.Location; +import org.hl7.fhir.r4b.model.Medication; +import org.hl7.fhir.r4b.model.MedicationAdministration; +import org.hl7.fhir.r4b.model.MedicationRequest; +import org.hl7.fhir.r4b.model.Meta; +import org.hl7.fhir.r4b.model.MolecularSequence; +import org.hl7.fhir.r4b.model.NamingSystem; +import org.hl7.fhir.r4b.model.Observation; +import org.hl7.fhir.r4b.model.OperationDefinition; +import org.hl7.fhir.r4b.model.Organization; +import org.hl7.fhir.r4b.model.Patient; +import org.hl7.fhir.r4b.model.Practitioner; +import org.hl7.fhir.r4b.model.PractitionerRole; +import org.hl7.fhir.r4b.model.Procedure; +import org.hl7.fhir.r4b.model.Questionnaire; +import org.hl7.fhir.r4b.model.QuestionnaireResponse; +import org.hl7.fhir.r4b.model.RiskAssessment; +import org.hl7.fhir.r4b.model.SearchParameter; +import org.hl7.fhir.r4b.model.ServiceRequest; +import org.hl7.fhir.r4b.model.StructureDefinition; +import org.hl7.fhir.r4b.model.Subscription; +import org.hl7.fhir.r4b.model.Substance; +import org.hl7.fhir.r4b.model.Task; +import org.hl7.fhir.r4b.model.ValueSet; +import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.mock; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {TestR4BConfig.class}) +public abstract class BaseJpaR4BTest extends BaseJpaTest implements ITestDataBuilder { + private static IValidationSupport ourJpaValidationSupportChainR4B; + private static IFhirResourceDaoValueSet ourValueSetDao; + @Autowired + protected ITermCodeSystemStorageSvc myTermCodeSystemStorageSvc; + @Autowired + @Qualifier("myResourceCountsCache") + protected ResourceCountCache myResourceCountsCache; + @Autowired + protected IResourceLinkDao myResourceLinkDao; + @Autowired + protected ISearchParamPresentDao mySearchParamPresentDao; + @Autowired + protected IResourceIndexedSearchParamStringDao myResourceIndexedSearchParamStringDao; + @Autowired + protected IResourceIndexedSearchParamTokenDao myResourceIndexedSearchParamTokenDao; + @Autowired + protected IResourceIndexedSearchParamQuantityDao myResourceIndexedSearchParamQuantityDao; + @Autowired + protected IResourceIndexedSearchParamDateDao myResourceIndexedSearchParamDateDao; + @Autowired + protected IResourceIndexedComboStringUniqueDao myResourceIndexedCompositeStringUniqueDao; + @Autowired + @Qualifier("myAllergyIntoleranceDaoR4B") + protected IFhirResourceDao myAllergyIntoleranceDao; + @Autowired + protected BinaryAccessProvider myBinaryAccessProvider; + @Autowired + protected BinaryStorageInterceptor myBinaryStorageInterceptor; + @Autowired + protected ApplicationContext myAppCtx; + @Autowired + @Qualifier("myAppointmentDaoR4B") + protected IFhirResourceDao myAppointmentDao; + @Autowired + @Qualifier("myAuditEventDaoR4B") + protected IFhirResourceDao myAuditEventDao; + @Autowired + @Qualifier("myBundleDaoR4B") + protected IFhirResourceDao myBundleDao; + @Autowired + @Qualifier("myCommunicationDaoR4B") + protected IFhirResourceDao myCommunicationDao; + @Autowired + @Qualifier("myCommunicationRequestDaoR4B") + protected IFhirResourceDao myCommunicationRequestDao; + @Autowired + @Qualifier("myCarePlanDaoR4B") + protected IFhirResourceDao myCarePlanDao; + @Autowired + @Qualifier("myCodeSystemDaoR4B") + protected IFhirResourceDaoCodeSystem myCodeSystemDao; + @Autowired + protected ITermCodeSystemDao myTermCodeSystemDao; + @Autowired + protected ITermConceptParentChildLinkDao myTermConceptParentChildLinkDao; + @Autowired + protected ITermCodeSystemVersionDao myTermCodeSystemVersionDao; + @Autowired + @Qualifier("myCompartmentDefinitionDaoR4B") + protected IFhirResourceDao myCompartmentDefinitionDao; + @Autowired + @Qualifier("myConceptMapDaoR4B") + protected IFhirResourceDaoConceptMap myConceptMapDao; + @Autowired + protected ITermConceptDao myTermConceptDao; + @Autowired + protected ITermConceptDesignationDao myTermConceptDesignationDao; + @Autowired + @Qualifier("myConditionDaoR4B") + protected IFhirResourceDao myConditionDao; + @Autowired + protected ModelConfig myModelConfig; + @Autowired + @Qualifier("myDeviceDaoR4B") + protected IFhirResourceDao myDeviceDao; + @Autowired + @Qualifier("myDiagnosticReportDaoR4B") + protected IFhirResourceDao myDiagnosticReportDao; + @Autowired + @Qualifier("myEncounterDaoR4B") + protected IFhirResourceDao myEncounterDao; + // @PersistenceContext() + @Autowired + protected EntityManager myEntityManager; + @Autowired + protected FhirContext myFhirCtx; + @Autowired + @Qualifier("myGroupDaoR4B") + protected IFhirResourceDao myGroupDao; + @Autowired + @Qualifier("myMolecularSequenceDaoR4B") + protected IFhirResourceDao myMolecularSequenceDao; + @Autowired + @Qualifier("myImmunizationDaoR4B") + protected IFhirResourceDao myImmunizationDao; + @Autowired + @Qualifier("myImmunizationRecommendationDaoR4B") + protected IFhirResourceDao myImmunizationRecommendationDao; + @Autowired + @Qualifier("myRiskAssessmentDaoR4B") + protected IFhirResourceDao myRiskAssessmentDao; + @Autowired + protected IInterceptorService myInterceptorRegistry; + @Autowired + @Qualifier("myLocationDaoR4B") + protected IFhirResourceDao myLocationDao; + @Autowired + @Qualifier("myMedicationAdministrationDaoR4B") + protected IFhirResourceDao myMedicationAdministrationDao; + @Autowired + @Qualifier("myMedicationDaoR4B") + protected IFhirResourceDao myMedicationDao; + @Autowired + @Qualifier("myMedicationRequestDaoR4B") + protected IFhirResourceDao myMedicationRequestDao; + @Autowired + @Qualifier("myProcedureDaoR4B") + protected IFhirResourceDao myProcedureDao; + @Autowired + @Qualifier("myNamingSystemDaoR4B") + protected IFhirResourceDao myNamingSystemDao; + @Autowired + @Qualifier("myChargeItemDaoR4B") + protected IFhirResourceDao myChargeItemDao; + @Autowired + @Qualifier("myObservationDaoR4B") + protected IFhirResourceDao myObservationDao; + @Autowired + @Qualifier("myOperationDefinitionDaoR4B") + protected IFhirResourceDao myOperationDefinitionDao; + @Autowired + @Qualifier("myOrganizationDaoR4B") + protected IFhirResourceDao myOrganizationDao; + @Autowired + protected DatabaseBackedPagingProvider myPagingProvider; + @Autowired + @Qualifier("myBinaryDaoR4B") + protected IFhirResourceDao myBinaryDao; + @Autowired + @Qualifier("myPatientDaoR4B") + protected IFhirResourceDaoPatient myPatientDao; + @Autowired + protected IResourceTableDao myResourceTableDao; + @Autowired + protected IResourceHistoryTableDao myResourceHistoryTableDao; + @Autowired + protected IForcedIdDao myForcedIdDao; + @Autowired + @Qualifier("myCoverageDaoR4B") + protected IFhirResourceDao myCoverageDao; + @Autowired + @Qualifier("myPractitionerDaoR4B") + protected IFhirResourceDao myPractitionerDao; + @Autowired + @Qualifier("myPractitionerRoleDaoR4B") + protected IFhirResourceDao myPractitionerRoleDao; + @Autowired + @Qualifier("myServiceRequestDaoR4B") + protected IFhirResourceDao myServiceRequestDao; + @Autowired + @Qualifier("myQuestionnaireDaoR4B") + protected IFhirResourceDao myQuestionnaireDao; + @Autowired + @Qualifier("myQuestionnaireResponseDaoR4B") + protected IFhirResourceDao myQuestionnaireResponseDao; + @Autowired + @Qualifier("myResourceProvidersR4B") + protected ResourceProviderFactory myResourceProviders; + @Autowired + protected IResourceTagDao myResourceTagDao; + @Autowired + protected ISearchCoordinatorSvc mySearchCoordinatorSvc; + @Autowired(required = false) + protected IFulltextSearchSvc mySearchDao; + @Autowired(required = false) + protected ISearchDao mySearchEntityDao; + @Autowired + protected IResourceReindexJobDao myResourceReindexJobDao; + @Autowired + @Qualifier("mySearchParameterDaoR4B") + protected IFhirResourceDao mySearchParameterDao; + @Autowired + protected SearchParamRegistryImpl mySearchParamRegistry; + @Autowired + protected IStaleSearchDeletingSvc myStaleSearchDeletingSvc; + @Autowired + @Qualifier("myStructureDefinitionDaoR4B") + protected IFhirResourceDaoStructureDefinition myStructureDefinitionDao; + @Autowired + @Qualifier("myConsentDaoR4B") + protected IFhirResourceDao myConsentDao; + @Autowired + @Qualifier("mySubscriptionDaoR4B") + protected IFhirResourceDaoSubscription mySubscriptionDao; + @Autowired + @Qualifier("mySubstanceDaoR4B") + protected IFhirResourceDao mySubstanceDao; + @Autowired + @Qualifier("mySystemDaoR4B") + protected IFhirSystemDao mySystemDao; + @Autowired + protected IResourceReindexingSvc myResourceReindexingSvc; + @Autowired + protected JpaSystemProvider mySystemProvider; + @Autowired + protected ITagDefinitionDao myTagDefinitionDao; + @Autowired + @Qualifier("myTaskDaoR4B") + protected IFhirResourceDao myTaskDao; + @Autowired + protected PlatformTransactionManager myTransactionMgr; + @Autowired + protected PlatformTransactionManager myTxManager; + @Autowired + @Qualifier("myJpaValidationSupportChain") + protected IValidationSupport myValidationSupport; + @Autowired + @Qualifier("myValueSetDaoR4B") + protected IFhirResourceDaoValueSet myValueSetDao; + @Autowired + protected ITermValueSetDao myTermValueSetDao; + @Autowired + protected ITermValueSetConceptDao myTermValueSetConceptDao; + @Autowired + protected ITermValueSetConceptDesignationDao myTermValueSetConceptDesignationDao; + @Autowired + protected ITermConceptMapDao myTermConceptMapDao; + @Autowired + protected ITermConceptMapGroupElementTargetDao myTermConceptMapGroupElementTargetDao; + @Autowired + protected ICacheWarmingSvc myCacheWarmingSvc; + @Autowired + protected SubscriptionRegistry mySubscriptionRegistry; + protected IServerInterceptor myInterceptor; + @Autowired + protected ITermDeferredStorageSvc myTermDeferredStorageSvc; + @Autowired + private IValidationSupport myJpaValidationSupportChain; + private PerformanceTracingLoggingInterceptor myPerformanceTracingLoggingInterceptor; + private List mySystemInterceptors; + @Autowired + private DaoRegistry myDaoRegistry; + @Autowired + private IBulkDataExportJobSchedulingHelper myBulkDataSchedulerHelper; + + @Override + public IIdType doCreateResource(IBaseResource theResource) { + IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass()); + return dao.create(theResource, mySrd).getId().toUnqualifiedVersionless(); + } + + @Override + public IIdType doUpdateResource(IBaseResource theResource) { + IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass()); + return dao.update(theResource, mySrd).getId().toUnqualifiedVersionless(); + } + + @Override + public FhirContext getFhirContext() { + return myFhirCtx; + } + + @AfterEach() + public void afterCleanupDao() { + myDaoConfig.setExpireSearchResults(new DaoConfig().isExpireSearchResults()); + myDaoConfig.setEnforceReferentialIntegrityOnDelete(new DaoConfig().isEnforceReferentialIntegrityOnDelete()); + myDaoConfig.setExpireSearchResultsAfterMillis(new DaoConfig().getExpireSearchResultsAfterMillis()); + myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); + myDaoConfig.setSuppressUpdatesWithNoChange(new DaoConfig().isSuppressUpdatesWithNoChange()); + myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches()); + + myPagingProvider.setDefaultPageSize(BasePagingProvider.DEFAULT_DEFAULT_PAGE_SIZE); + myPagingProvider.setMaximumPageSize(BasePagingProvider.DEFAULT_MAX_PAGE_SIZE); + } + + @AfterEach + public void afterResetInterceptors() { + myInterceptorRegistry.unregisterAllInterceptors(); + } + + @AfterEach() + public void afterGrabCaches() { + ourValueSetDao = myValueSetDao; + ourJpaValidationSupportChainR4B = myJpaValidationSupportChain; + } + + @BeforeEach + public void beforeCreateInterceptor() { + mySystemInterceptors = myInterceptorRegistry.getAllRegisteredInterceptors(); + + myInterceptor = mock(IServerInterceptor.class); + + myPerformanceTracingLoggingInterceptor = new PerformanceTracingLoggingInterceptor(); + myInterceptorRegistry.registerInterceptor(myPerformanceTracingLoggingInterceptor); + } + + @BeforeEach + public void beforeFlushFT() { + purgeHibernateSearch(myEntityManager); + + myDaoConfig.setSchedulingDisabled(true); + myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED); + } + + @BeforeEach + @Transactional() + public void beforePurgeDatabase() { + purgeDatabase(myDaoConfig, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataSchedulerHelper); + } + + @BeforeEach + public void beforeResetConfig() { + myFhirCtx.setParserErrorHandler(new StrictErrorHandler()); + } + + @Override + protected PlatformTransactionManager getTxManager() { + return myTxManager; + } + + protected void validate(IBaseResource theResource) { + FhirValidator validatorModule = myFhirCtx.newValidator(); + FhirInstanceValidator instanceValidator = new FhirInstanceValidator(myValidationSupport); + instanceValidator.setBestPracticeWarningLevel(BestPracticeWarningLevel.Ignore); + validatorModule.registerValidatorModule(instanceValidator); + ValidationResult result = validatorModule.validateWithResult(theResource); + if (!result.isSuccessful()) { + fail(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome())); + } + } + + @SuppressWarnings("unchecked") + protected void upload(String theClasspath) throws IOException { + String resource = loadResource(theClasspath); + IParser parser = EncodingEnum.detectEncoding(resource).newParser(myFhirCtx); + IBaseResource resourceParsed = parser.parseResource(resource); + IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceParsed.getIdElement().getResourceType()); + dao.update(resourceParsed); + } + + protected ValueSet.ValueSetExpansionContainsComponent assertExpandedValueSetContainsConcept(ValueSet theValueSet, String theSystem, String theCode, String theDisplay, Integer theDesignationCount) { + List contains = theValueSet.getExpansion().getContains(); + + Stream stream = contains.stream(); + if (theSystem != null) { + stream = stream.filter(concept -> theSystem.equalsIgnoreCase(concept.getSystem())); + } + if (theCode != null) { + stream = stream.filter(concept -> theCode.equalsIgnoreCase(concept.getCode())); + } + if (theDisplay != null) { + stream = stream.filter(concept -> theDisplay.equalsIgnoreCase(concept.getDisplay())); + } + if (theDesignationCount != null) { + stream = stream.filter(concept -> concept.getDesignation().size() == theDesignationCount); + } + + Optional first = stream.findFirst(); + if (!first.isPresent()) { + String failureMessage = String.format("Expanded ValueSet %s did not contain concept [%s|%s|%s] with [%d] designations", theValueSet.getId(), theSystem, theCode, theDisplay, theDesignationCount); + fail(failureMessage); + return null; + } else { + return first.get(); + } + } + + @AfterEach + public void afterEachClearCaches() { + myValueSetDao.purgeCaches(); + myJpaValidationSupportChain.invalidateCaches(); + } + + +} diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/provider/r4b/BaseResourceProviderR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/provider/r4b/BaseResourceProviderR4BTest.java new file mode 100644 index 00000000000..cf2fd929c2d --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/provider/r4b/BaseResourceProviderR4BTest.java @@ -0,0 +1,275 @@ +package ca.uhn.fhir.jpa.provider.r4b; + +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; +import ca.uhn.fhir.jpa.dao.r4b.BaseJpaR4BTest; +import ca.uhn.fhir.jpa.graphql.GraphQLProvider; +import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; +import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl; +import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig; +import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader; +import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor; +import ca.uhn.fhir.jpa.util.ResourceCountCache; +import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; +import ca.uhn.fhir.parser.StrictErrorHandler; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor; +import ca.uhn.fhir.test.utilities.JettyUtil; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4b.model.Parameters; +import org.hl7.fhir.r4b.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.r4b.model.Patient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.context.ContextLoader; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.context.support.GenericWebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.servlet.DispatcherServlet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public abstract class BaseResourceProviderR4BTest extends BaseJpaR4BTest { + + protected static IValidationSupport myValidationSupport; + protected static CloseableHttpClient ourHttpClient; + protected static int ourPort; + protected static RestfulServer ourRestServer; + protected static String ourServerBase; + protected static SearchParamRegistryImpl ourSearchParamRegistry; + private static DatabaseBackedPagingProvider ourPagingProvider; + protected static ISearchCoordinatorSvc mySearchCoordinatorSvc; + private static GenericWebApplicationContext ourWebApplicationContext; + private static SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor; + protected static Server ourServer; + protected IGenericClient myClient; + ResourceCountCache ourResourceCountsCache; + private Object ourGraphQLProvider; + private boolean ourRestHookSubscriptionInterceptorRequested; + + @Autowired + protected SubscriptionLoader mySubscriptionLoader; + @Autowired + protected DaoRegistry myDaoRegistry; + private TerminologyUploaderProvider myTerminologyUploaderProvider; + + public BaseResourceProviderR4BTest() { + super(); + } + + @AfterEach + public void after() throws Exception { + myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); + if (ourRestServer != null) { + ourRestServer.getInterceptorService().unregisterAllInterceptors(); + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @BeforeEach + public void before() throws Exception { + myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + myFhirCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); + myFhirCtx.setParserErrorHandler(new StrictErrorHandler()); + + if (ourServer == null) { + ourRestServer = new RestfulServer(myFhirCtx); + ourRestServer.registerProviders(myResourceProviders.createProviders()); + ourRestServer.registerProvider(myBinaryAccessProvider); + ourRestServer.getInterceptorService().registerInterceptor(myBinaryStorageInterceptor); + ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourRestServer.setDefaultResponseEncoding(EncodingEnum.XML); + + myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class); + myDaoRegistry = myAppCtx.getBean(DaoRegistry.class); + ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider); + + ourRestServer.registerProvider(myAppCtx.getBean(ValueSetOperationProvider.class)); + + ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class)); + IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class); + + ourSearchParamRegistry = myAppCtx.getBean(SearchParamRegistryImpl.class); + + JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(ourRestServer, mySystemDao, myDaoConfig, ourSearchParamRegistry, validationSupport); + confProvider.setImplementationDescription("THIS IS THE DESC"); + ourRestServer.setServerConformanceProvider(confProvider); + + ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class); + ourResourceCountsCache = (ResourceCountCache) myAppCtx.getBean("myResourceCountsCache"); + + Server server = new Server(0); + + ServletContextHandler proxyHandler = new ServletContextHandler(); + proxyHandler.setContextPath("/"); + + ServletHolder servletHolder = new ServletHolder(); + servletHolder.setServlet(ourRestServer); + proxyHandler.addServlet(servletHolder, "/fhir/context/*"); + + ourWebApplicationContext = new GenericWebApplicationContext(); + ourWebApplicationContext.setParent(myAppCtx); + ourWebApplicationContext.refresh(); + proxyHandler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ourWebApplicationContext); + + DispatcherServlet dispatcherServlet = new DispatcherServlet(); + // dispatcherServlet.setApplicationContext(webApplicationContext); + dispatcherServlet.setContextClass(AnnotationConfigWebApplicationContext.class); + ServletHolder subsServletHolder = new ServletHolder(); + subsServletHolder.setServlet(dispatcherServlet); + subsServletHolder.setInitParameter( + ContextLoader.CONFIG_LOCATION_PARAM, + WebsocketDispatcherConfig.class.getName()); + proxyHandler.addServlet(subsServletHolder, "/*"); + + // Register a CORS filter + CorsConfiguration config = new CorsConfiguration(); + CorsInterceptor corsInterceptor = new CorsInterceptor(config); + config.addAllowedHeader("x-fhir-starter"); + config.addAllowedHeader("Origin"); + config.addAllowedHeader("Accept"); + config.addAllowedHeader("X-Requested-With"); + config.addAllowedHeader("Content-Type"); + config.addAllowedHeader("Access-Control-Request-Method"); + config.addAllowedHeader("Access-Control-Request-Headers"); + config.addAllowedOrigin("*"); + config.addExposedHeader("Location"); + config.addExposedHeader("Content-Location"); + config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + ourRestServer.registerInterceptor(corsInterceptor); + + server.setHandler(proxyHandler); + JettyUtil.startServer(server); + ourPort = JettyUtil.getPortForStartedServer(server); + ourServerBase = "http://localhost:" + ourPort + "/fhir/context"; + + WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext()); + myValidationSupport = wac.getBean(IValidationSupport.class); + mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class); + ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class); + + myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + builder.setMaxConnPerRoute(99); + ourHttpClient = builder.build(); + + ourServer = server; + } + + ourRestServer.setPagingProvider(ourPagingProvider); + + myClient = myFhirCtx.newRestfulGenericClient(ourServerBase); + if (shouldLogClient()) { + myClient.registerInterceptor(new LoggingInterceptor()); + } + } + + protected boolean shouldLogClient() { + return true; + } + + protected List toNameList(Bundle resp) { + List names = new ArrayList<>(); + for (BundleEntryComponent next : resp.getEntry()) { + Patient nextPt = (Patient) next.getResource(); + String nextStr = nextPt.getName().size() > 0 ? nextPt.getName().get(0).getGivenAsSingleString() + " " + nextPt.getName().get(0).getFamily() : ""; + if (isNotBlank(nextStr)) { + names.add(nextStr); + } + } + return names; + } + + @AfterAll + public static void afterClassClearContextBaseResourceProviderR5Test() throws Exception { + JettyUtil.closeServer(ourServer); + ourHttpClient.close(); + ourServer = null; + ourHttpClient = null; + myValidationSupport.invalidateCaches(); + myValidationSupport = null; + ourWebApplicationContext.close(); + ourWebApplicationContext = null; + } + + public static int getNumberOfParametersByName(Parameters theParameters, String theName) { + int retVal = 0; + + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + retVal++; + } + } + + return retVal; + } + + public static ParametersParameterComponent getParameterByName(Parameters theParameters, String theName) { + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + return param; + } + } + + return new ParametersParameterComponent(); + } + + public static List getParametersByName(Parameters theParameters, String theName) { + List params = new ArrayList<>(); + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + params.add(param); + } + } + + return params; + } + + public static ParametersParameterComponent getPartByName(ParametersParameterComponent theParameter, String theName) { + for (ParametersParameterComponent part : theParameter.getPart()) { + if (part.getName().equals(theName)) { + return part; + } + } + + return new ParametersParameterComponent(); + } + + public static boolean hasParameterByName(Parameters theParameters, String theName) { + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + return true; + } + } + + return false; + } + +} diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/provider/r4b/ResourceProviderR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/provider/r4b/ResourceProviderR4BTest.java new file mode 100644 index 00000000000..6d835cd99de --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/provider/r4b/ResourceProviderR4BTest.java @@ -0,0 +1,520 @@ +package ca.uhn.fhir.jpa.provider.r4b; + +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.entity.Search; +import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; +import ca.uhn.fhir.parser.StrictErrorHandler; +import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; +import ca.uhn.fhir.rest.openapi.OpenApiInterceptor; +import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import ca.uhn.fhir.util.UrlUtil; +import com.google.common.base.Charsets; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.hamcrest.Matchers; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4b.model.CodeableConcept; +import org.hl7.fhir.r4b.model.Condition; +import org.hl7.fhir.r4b.model.DateTimeType; +import org.hl7.fhir.r4b.model.MedicationRequest; +import org.hl7.fhir.r4b.model.Observation; +import org.hl7.fhir.r4b.model.Observation.ObservationComponentComponent; +import org.hl7.fhir.r4b.model.OperationOutcome; +import org.hl7.fhir.r4b.model.Organization; +import org.hl7.fhir.r4b.model.Parameters; +import org.hl7.fhir.r4b.model.Patient; +import org.hl7.fhir.r4b.model.Quantity; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItem; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +@SuppressWarnings("Duplicates") +public class ResourceProviderR4BTest extends BaseResourceProviderR4BTest { + + private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR4BTest.class); + private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor(); + + @Override + @AfterEach + public void after() throws Exception { + super.after(); + + myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete()); + myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); + myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); + myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo()); + myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds()); + myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches()); + + myClient.unregisterInterceptor(myCapturingInterceptor); + + ourRestServer.getInterceptorService().unregisterInterceptorsIf(t->t instanceof OpenApiInterceptor); + } + + @BeforeEach + @Override + public void before() throws Exception { + super.before(); + myFhirCtx.setParserErrorHandler(new StrictErrorHandler()); + + myDaoConfig.setAllowMultipleDelete(true); + myClient.registerInterceptor(myCapturingInterceptor); + myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds()); + } + + @Test + public void testSearchWithContainsLowerCase() { + myDaoConfig.setAllowContainsSearches(true); + + Patient pt1 = new Patient(); + pt1.addName().setFamily("Elizabeth"); + String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue(); + + Patient pt2 = new Patient(); + pt2.addName().setFamily("fghijk"); + String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue(); + + Patient pt3 = new Patient(); + pt3.addName().setFamily("zzzzz"); + myPatientDao.create(pt3).getId().toUnqualifiedVersionless().getValue(); + + + Bundle output = myClient + .search() + .forResource("Patient") + .where(org.hl7.fhir.r4.model.Patient.NAME.contains().value("ZAB")) + .returnBundle(Bundle.class) + .execute(); + List ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList()); + assertThat(ids, containsInAnyOrder(pt1id)); + + output = myClient + .search() + .forResource("Patient") + .where(org.hl7.fhir.r4.model.Patient.NAME.contains().value("zab")) + .returnBundle(Bundle.class) + .execute(); + ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList()); + assertThat(ids, containsInAnyOrder(pt1id)); + + } + + @Test + public void testErroredSearchIsNotReused() { + Patient pt1 = new Patient(); + pt1.addName().setFamily("Hello"); + myPatientDao.create(pt1); + + // Perform the search + Bundle response0 = myClient.search() + .forResource("Patient") + .where(org.hl7.fhir.r4.model.Patient.NAME.matches().value("Hello")) + .returnBundle(Bundle.class) + .execute(); + assertEquals(1, response0.getEntry().size()); + + // Perform the search again (should return the same) + Bundle response1 = myClient.search() + .forResource("Patient") + .where(org.hl7.fhir.r4.model.Patient.NAME.matches().value("Hello")) + .returnBundle(Bundle.class) + .execute(); + assertEquals(1, response1.getEntry().size()); + assertEquals(response0.getId(), response1.getId()); + + // Pretend the search was errored out + markSearchErrored(); + + // Perform the search again (shouldn't return the errored out search) + Bundle response3 = myClient.search() + .forResource("Patient") + .where(org.hl7.fhir.r4.model.Patient.NAME.matches().value("Hello")) + .returnBundle(Bundle.class) + .execute(); + assertEquals(1, response3.getEntry().size()); + assertNotEquals(response0.getId(), response3.getId()); + + } + + private void markSearchErrored() { + while (true) { + try { + runInTransaction(() -> { + assertEquals(1L, mySearchEntityDao.count()); + Search search = mySearchEntityDao.findAll().iterator().next(); + search.setStatus(SearchStatusEnum.FAILED); + search.setFailureMessage("Some Failure Message"); + search.setFailureCode(501); + mySearchEntityDao.save(search); + }); + break; + } catch (ResourceVersionConflictException e) { + ourLog.warn("Conflict while updating search: " + e); + continue; + } + } + } + + @Test + public void testErroredSearchReturnsAppropriateResponse() { + Patient pt1 = new Patient(); + pt1.addName().setFamily("Hello"); + myPatientDao.create(pt1); + + Patient pt2 = new Patient(); + pt2.addName().setFamily("Hello"); + myPatientDao.create(pt2); + + // Perform a search for the first page + Bundle response0 = myClient.search() + .forResource("Patient") + .where(org.hl7.fhir.r4.model.Patient.NAME.matches().value("Hello")) + .returnBundle(Bundle.class) + .count(1) + .execute(); + assertEquals(1, response0.getEntry().size()); + + // Make sure it works for now + myClient.loadPage().next(response0).execute(); + + // Pretend the search was errored out + markSearchErrored(); + + // Request the second page + try { + myClient.loadPage().next(response0).execute(); + } catch (NotImplementedOperationException e) { + assertEquals(501, e.getStatusCode()); + assertThat(e.getMessage(), containsString("Some Failure Message")); + } + + } + + @Test + public void testDateNowSyntax() { + Observation observation = new Observation(); + observation.setEffective(new DateTimeType("1965-08-09")); + IIdType oid = myObservationDao.create(observation).getId().toUnqualified(); + String nowParam = UrlUtil.escapeUrlParam("%now"); + Bundle output = myClient + .search() + .byUrl("Observation?date=lt" + nowParam) + .returnBundle(Bundle.class) + .execute(); + List ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualified()).collect(Collectors.toList()); + assertThat(ids, containsInAnyOrder(oid)); + } + + + @Test + public void testCount0() { + Observation observation = new Observation(); + observation.setEffective(new DateTimeType("1965-08-09")); + myObservationDao.create(observation).getId().toUnqualified(); + + observation = new Observation(); + observation.setEffective(new DateTimeType("1965-08-10")); + myObservationDao.create(observation).getId().toUnqualified(); + + myCaptureQueriesListener.clear(); + Bundle output = myClient + .search() + .byUrl("Observation?_count=0") + .returnBundle(Bundle.class) + .execute(); + ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output)); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(2, output.getTotal()); + assertEquals(0, output.getEntry().size()); + } + + @Test + public void testSearchWithCompositeSort() throws IOException { + + IIdType pid0; + IIdType oid1; + IIdType oid2; + IIdType oid3; + IIdType oid4; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().setFamily("Tester").addGiven("Joe"); + pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("FOO"); + obs.getSubject().setReferenceElement(pid0); + + ObservationComponentComponent comp = obs.addComponent(); + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("2345-7").setSystem("http://loinc.org"); + comp.setCode(cc); + comp.setValue(new Quantity().setValue(200)); + + oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + + ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + } + + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("FOO"); + obs.getSubject().setReferenceElement(pid0); + + ObservationComponentComponent comp = obs.addComponent(); + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("2345-7").setSystem("http://loinc.org"); + comp.setCode(cc); + comp.setValue(new Quantity().setValue(300)); + + oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + + ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + } + + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("FOO"); + obs.getSubject().setReferenceElement(pid0); + + ObservationComponentComponent comp = obs.addComponent(); + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("2345-7").setSystem("http://loinc.org"); + comp.setCode(cc); + comp.setValue(new Quantity().setValue(150)); + + oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + + ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + } + + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("FOO"); + obs.getSubject().setReferenceElement(pid0); + + ObservationComponentComponent comp = obs.addComponent(); + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("2345-7").setSystem("http://loinc.org"); + comp.setCode(cc); + comp.setValue(new Quantity().setValue(250)); + oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + + ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + } + + String uri = ourServerBase + "/Observation?_sort=combo-code-value-quantity"; + Bundle found; + + HttpGet get = new HttpGet(uri); + try (CloseableHttpResponse resp = ourHttpClient.execute(get)) { + String output = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8); + found = myFhirCtx.newXmlParser().parseResource(Bundle.class, output); + } + + ourLog.info("Bundle: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(found)); + + List list = toUnqualifiedVersionlessIds(found); + assertEquals(4, found.getEntry().size()); + assertEquals(oid3, list.get(0)); + assertEquals(oid1, list.get(1)); + assertEquals(oid4, list.get(2)); + assertEquals(oid2, list.get(3)); + } + + @Test + public void testEverythingPatientInstanceWithTypeParameter() { + String methodName = "testEverythingPatientInstanceWithTypeParameter"; + + //Patient 1 stuff. + IIdType o1Id = createOrganization(methodName, "1"); + IIdType p1Id = createPatientWithIndexAtOrganization(methodName, "1", o1Id); + IIdType c1Id = createConditionForPatient(methodName, "1", p1Id); + IIdType obs1Id = createObservationForPatient(p1Id, "1"); + IIdType m1Id = createMedicationRequestForPatient(p1Id, "1"); + + //Test for only one patient + Parameters parameters = new Parameters(); + parameters.addParameter("_type", "Condition, Observation"); + + myCaptureQueriesListener.clear(); + + Parameters output = myClient.operation().onInstance(p1Id).named("everything").withParameters(parameters).execute(); + ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output)); + Bundle b = (Bundle) output.getParameter().get(0).getResource(); + + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(Bundle.BundleType.SEARCHSET, b.getType()); + List ids = toUnqualifiedVersionlessIds(b); + + assertThat(ids, containsInAnyOrder(p1Id, c1Id, obs1Id)); + assertThat(ids, Matchers.not(hasItem(o1Id))); + assertThat(ids, Matchers.not(hasItem(m1Id))); + } + + @Test + public void testEverythingPatientTypeWithTypeParameter() { + String methodName = "testEverythingPatientTypeWithTypeParameter"; + + //Patient 1 stuff. + IIdType o1Id = createOrganization(methodName, "1"); + IIdType p1Id = createPatientWithIndexAtOrganization(methodName, "1", o1Id); + IIdType c1Id = createConditionForPatient(methodName, "1", p1Id); + IIdType obs1Id = createObservationForPatient(p1Id, "1"); + IIdType m1Id = createMedicationRequestForPatient(p1Id, "1"); + + //Test for only one patient + Parameters parameters = new Parameters(); + parameters.addParameter("_type", "Condition, Observation"); + + myCaptureQueriesListener.clear(); + + Parameters output = myClient.operation().onType(Patient.class).named("everything").withParameters(parameters).execute(); + ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output)); + Bundle b = (Bundle) output.getParameter().get(0).getResource(); + + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(Bundle.BundleType.SEARCHSET, b.getType()); + List ids = toUnqualifiedVersionlessIds(b); + + assertThat(ids, containsInAnyOrder(p1Id, c1Id, obs1Id)); + assertThat(ids, Matchers.not(hasItem(o1Id))); + assertThat(ids, Matchers.not(hasItem(m1Id))); + } + + @Test + public void testOpenApiFetchSwaggerUi() throws IOException { + ourRestServer.getInterceptorService().registerInterceptor(new OpenApiInterceptor()); + + String uri = ourServerBase + "/swagger-ui/"; + + HttpGet get = new HttpGet(uri); + try (CloseableHttpResponse resp = ourHttpClient.execute(get)) { + String output = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Fetch output: {}", output); + assertEquals(200, resp.getStatusLine().getStatusCode()); + } + } + + + @Test + public void testEverythingPatientTypeWithTypeAndIdParameter() { + String methodName = "testEverythingPatientTypeWithTypeAndIdParameter"; + + //Patient 1 stuff. + IIdType o1Id = createOrganization(methodName, "1"); + IIdType p1Id = createPatientWithIndexAtOrganization(methodName, "1", o1Id); + IIdType c1Id = createConditionForPatient(methodName, "1", p1Id); + IIdType obs1Id = createObservationForPatient(p1Id, "1"); + IIdType m1Id = createMedicationRequestForPatient(p1Id, "1"); + + //Patient 2 stuff. + IIdType o2Id = createOrganization(methodName, "2"); + IIdType p2Id = createPatientWithIndexAtOrganization(methodName, "2", o2Id); + IIdType c2Id = createConditionForPatient(methodName, "2", p2Id); + IIdType obs2Id = createObservationForPatient(p2Id, "2"); + IIdType m2Id = createMedicationRequestForPatient(p2Id, "2"); + + //Test for only patient 1 + Parameters parameters = new Parameters(); + parameters.addParameter("_type", "Condition, Observation"); + parameters.addParameter("_id", p1Id.getIdPart()); + + myCaptureQueriesListener.clear(); + + Parameters output = myClient.operation().onType(Patient.class).named("everything").withParameters(parameters).execute(); + ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output)); + Bundle b = (Bundle) output.getParameter().get(0).getResource(); + + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(Bundle.BundleType.SEARCHSET, b.getType()); + List ids = toUnqualifiedVersionlessIds(b); + + assertThat(ids, containsInAnyOrder(p1Id, c1Id, obs1Id)); + assertThat(ids, Matchers.not(hasItem(o1Id))); + assertThat(ids, Matchers.not(hasItem(m1Id))); + assertThat(ids, Matchers.not(hasItem(p2Id))); + assertThat(ids, Matchers.not(hasItem(o2Id))); + } + + private IIdType createOrganization(String methodName, String s) { + Organization o1 = new Organization(); + o1.setName(methodName + s); + return myClient.create().resource(o1).execute().getId().toUnqualifiedVersionless(); + } + + public IIdType createPatientWithIndexAtOrganization(String theMethodName, String theIndex, IIdType theOrganizationId) { + Patient p1 = new Patient(); + p1.addName().setFamily(theMethodName + theIndex); + p1.getManagingOrganization().setReferenceElement(theOrganizationId); + IIdType p1Id = myClient.create().resource(p1).execute().getId().toUnqualifiedVersionless(); + return p1Id; + } + + public IIdType createConditionForPatient(String theMethodName, String theIndex, IIdType thePatientId) { + Condition c = new Condition(); + c.addIdentifier().setValue(theMethodName + theIndex); + if (thePatientId != null) { + c.getSubject().setReferenceElement(thePatientId); + } + IIdType cId = myClient.create().resource(c).execute().getId().toUnqualifiedVersionless(); + return cId; + } + + private IIdType createMedicationRequestForPatient(IIdType thePatientId, String theIndex) { + MedicationRequest m = new MedicationRequest(); + m.addIdentifier().setValue(theIndex); + if (thePatientId != null) { + m.getSubject().setReferenceElement(thePatientId); + } + IIdType mId = myClient.create().resource(m).execute().getId().toUnqualifiedVersionless(); + return mId; + } + + private IIdType createObservationForPatient(IIdType thePatientId, String theIndex) { + Observation o = new Observation(); + o.addIdentifier().setValue(theIndex); + if (thePatientId != null) { + o.getSubject().setReferenceElement(thePatientId); + } + IIdType oId = myClient.create().resource(o).execute().getId().toUnqualifiedVersionless(); + return oId; + } + + protected List toUnqualifiedVersionlessIds(Bundle theFound) { + List retVal = new ArrayList<>(); + for (BundleEntryComponent next : theFound.getEntry()) { + if (next.getResource()!= null) { + retVal.add(next.getResource().getIdElement().toUnqualifiedVersionless()); + } + } + return retVal; + } +} diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index ef3e9b30d38..adcde20f19a 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java index 330aa773ab8..a42daba2756 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java @@ -46,19 +46,19 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.interceptor.PerformanceTracingLoggingInterceptor; import ca.uhn.fhir.jpa.model.entity.ModelConfig; -import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5; +import ca.uhn.fhir.jpa.provider.JpaSystemProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc; import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; -import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl; +import ca.uhn.fhir.jpa.term.TermReadSvcImpl; import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl; import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR5; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.test.BaseJpaTest; import ca.uhn.fhir.jpa.test.config.TestR5Config; import ca.uhn.fhir.jpa.util.ResourceCountCache; @@ -360,14 +360,14 @@ public abstract class BaseJpaR5Test extends BaseJpaTest implements ITestDataBuil protected IResourceReindexingSvc myResourceReindexingSvc; @Autowired @Qualifier("mySystemProviderR5") - protected JpaSystemProviderR5 mySystemProvider; + protected JpaSystemProvider mySystemProvider; @Autowired protected ITagDefinitionDao myTagDefinitionDao; @Autowired @Qualifier("myTaskDaoR5") protected IFhirResourceDao myTaskDao; @Autowired - protected ITermReadSvcR5 myTermSvc; + protected ITermReadSvc myTermSvc; @Autowired protected PlatformTransactionManager myTransactionMgr; @Autowired @@ -441,7 +441,7 @@ public abstract class BaseJpaR5Test extends BaseJpaTest implements ITestDataBuil @AfterEach public void afterClearTerminologyCaches() { - BaseTermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc); + TermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc); baseHapiTerminologySvc.clearCaches(); TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationCache(); TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationWithReverseCache(); diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java index dd8fdf92c52..9fb6b1d0a0a 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java @@ -1,13 +1,18 @@ package ca.uhn.fhir.jpa.dao.r5; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; +import ca.uhn.fhir.jpa.util.ValueSetTestUtil; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r5.model.CodeSystem; @@ -28,9 +33,11 @@ import java.io.IOException; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.stringContainsInOrder; 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.junit.jupiter.api.Assertions.fail; @@ -38,6 +45,8 @@ public class FhirResourceDaoR5ValueSetTest extends BaseJpaR5Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR5ValueSetTest.class); + private final ValueSetTestUtil myValueSetTestUtil = new ValueSetTestUtil(FhirVersionEnum.R5); + @Autowired protected ITermDeferredStorageSvc myTerminologyDeferredStorageSvc; @@ -262,6 +271,45 @@ public class FhirResourceDaoR5ValueSetTest extends BaseJpaR5Test { } } + @Test + public void testPrecalculatedValueSet() { + // Add a bunch of codes + CustomTerminologySet codesToAdd = new CustomTerminologySet(); + for (int i = 0; i < 10; i++) { + codesToAdd.addRootConcept("CODE" + i, "Display " + i); + } + myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://cs", codesToAdd); + + ValueSet vs = new ValueSet(); + vs.setUrl("http://vs"); + vs.getCompose().addInclude().setSystem("http://cs"); + myValueSetDao.create(vs); + + // Precalculate + + myTerminologyDeferredStorageSvc.saveAllDeferred(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + logAllValueSets(); + myCachingValidationSupport.invalidateCaches(); + + // Validate code + + ValidationSupportContext ctx = new ValidationSupportContext(myValidationSupport); + ConceptValidationOptions options= new ConceptValidationOptions(); + IValidationSupport.CodeValidationResult outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "CODE4", null, "http://vs"); + assertNotNull(outcome); + assertTrue(outcome.isOk()); + assertThat(outcome.getMessage(), startsWith("Code validation occurred using a ValueSet expansion that was pre-calculated at ")); + + // Expand valueset + + ValueSet outcomeVs = myValueSetDao.expand(vs, null); + String expansionMessage = myValueSetTestUtil.extractExpansionMessage(outcomeVs); + assertThat(expansionMessage, startsWith("ValueSet was expanded using an expansion that was pre-calculated")); + } + + @Autowired + protected CachingValidationSupport myCachingValidationSupport; @Test public void testValidateCodeAgainstBuiltInValueSetAndCodeSystemWithValidCode() { diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 00e98f32362..b8388631ee1 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/term/AbstractValueSetHSearchExpansionR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/term/AbstractValueSetHSearchExpansionR4Test.java index e6a52e91bcf..fcd8e882449 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/term/AbstractValueSetHSearchExpansionR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/term/AbstractValueSetHSearchExpansionR4Test.java @@ -38,7 +38,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; import ca.uhn.fhir.jpa.test.BaseJpaTest; import ca.uhn.fhir.parser.StrictErrorHandler; @@ -143,7 +143,7 @@ public abstract class AbstractValueSetHSearchExpansionR4Test extends BaseJpaTest protected IFhirResourceDaoValueSet myValueSetDao; @Autowired - protected ITermReadSvcR4 myTermSvc; + protected ITermReadSvc myTermSvc; @Autowired protected ITermDeferredStorageSvc myTerminologyDeferredStorageSvc; @@ -213,7 +213,7 @@ public abstract class AbstractValueSetHSearchExpansionR4Test extends BaseJpaTest @AfterEach public void afterClearTerminologyCaches() { - BaseTermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc); + TermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc); baseHapiTerminologySvc.clearCaches(); TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationCache(); TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationWithReverseCache(); @@ -1785,7 +1785,7 @@ public abstract class AbstractValueSetHSearchExpansionR4Test extends BaseJpaTest SearchPredicateFactory predicate = searchSession.scope(TermConcept.class).predicate(); Optional lastStepOpt = ReflectionTestUtils.invokeMethod( - new TermReadSvcR4(), "buildExpansionPredicate", theSearchedCodes, predicate); + new TermReadSvcImpl(), "buildExpansionPredicate", theSearchedCodes, predicate); assertNotNull(lastStepOpt); assertTrue(lastStepOpt.isPresent()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java index 05982bfe945..2ec67d73960 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java @@ -48,12 +48,12 @@ import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementTargetDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; import ca.uhn.fhir.jpa.model.entity.ModelConfig; -import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; +import ca.uhn.fhir.jpa.provider.JpaSystemProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; -import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl; +import ca.uhn.fhir.jpa.term.TermReadSvcImpl; import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl; import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; @@ -309,7 +309,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { protected IFhirSystemDao mySystemDao; @Autowired @Qualifier("mySystemProviderDstu3") - protected JpaSystemProviderDstu3 mySystemProvider; + protected JpaSystemProvider mySystemProvider; @Autowired protected ITagDefinitionDao myTagDefinitionDao; @Autowired @@ -367,7 +367,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { @AfterEach public void afterClearTerminologyCaches() { - BaseTermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc); + TermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc); baseHapiTerminologySvc.clearCaches(); TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationCache(); TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationWithReverseCache(); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java index 2d37b7bbf8d..b7c8c10110e 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java @@ -81,21 +81,20 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc; -import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; +import ca.uhn.fhir.jpa.provider.JpaSystemProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc; import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl; -import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl; import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl; import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl; +import ca.uhn.fhir.jpa.term.TermReadSvcImpl; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; -import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; import ca.uhn.fhir.jpa.test.config.TestR4Config; import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.ResourceCountCache; @@ -475,14 +474,14 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil protected IResourceReindexingSvc myResourceReindexingSvc; @Autowired @Qualifier("mySystemProviderR4") - protected JpaSystemProviderR4 mySystemProvider; + protected JpaSystemProvider mySystemProvider; @Autowired protected ITagDefinitionDao myTagDefinitionDao; @Autowired @Qualifier("myTaskDaoR4") protected IFhirResourceDao myTaskDao; @Autowired - protected ITermReadSvcR4 myTermSvc; + protected ITermReadSvc myTermSvc; @Autowired protected ITermDeferredStorageSvc myTerminologyDeferredStorageSvc; @Autowired @@ -548,7 +547,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @AfterEach public void afterClearTerminologyCaches() { - BaseTermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc); + TermReadSvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc); baseHapiTerminologySvc.clearCaches(); TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationCache(); TermConceptMappingSvcImpl.clearOurLastResultsFromTranslationWithReverseCache(); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java new file mode 100644 index 00000000000..e866d9a9e4e --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java @@ -0,0 +1,279 @@ +package ca.uhn.fhir.jpa.test.config; + +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; +import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; +import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; +import ca.uhn.fhir.jpa.config.HapiJpaConfig; +import ca.uhn.fhir.jpa.config.r4.JpaR4Config; +import ca.uhn.fhir.jpa.config.r4b.JpaR4BConfig; +import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect; +import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener; +import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener; +import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; +import ca.uhn.fhir.validation.ResultSeverityEnum; +import net.ttddyy.dsproxy.listener.SingleQueryCountHolder; +import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; +import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; +import org.apache.commons.dbcp2.BasicDataSource; +import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Lazy; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; + +import javax.sql.DataSource; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.sql.Connection; +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.fail; + +@Configuration +@Import({ + JpaR4BConfig.class, + HapiJpaConfig.class, + TestJPAConfig.class, + TestHSearchAddInConfig.DefaultLuceneHeap.class, + JpaBatch2Config.class, + Batch2JobsConfig.class +}) +public class TestR4BConfig { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR4BConfig.class); + public static Integer ourMaxThreads; + + static { + /* + * We use a randomized number of maximum threads in order to try + * and catch any potential deadlocks caused by database connection + * starvation + */ + if (ourMaxThreads == null) { + ourMaxThreads = (int) (Math.random() * 6.0) + 3; + + if ("true".equals(System.getProperty("single_db_connection"))) { + ourMaxThreads = 1; + } + if ("true".equals(System.getProperty("unlimited_db_connection"))) { + ourMaxThreads = 100; + } + } + } + + private final Deque myLastStackTrace = new LinkedList<>(); + @Autowired + TestHSearchAddInConfig.IHSearchConfigurer hibernateSearchConfigurer; + private boolean myHaveDumpedThreads; + + @Bean + public CircularQueueCaptureQueriesListener captureQueriesListener() { + return new CircularQueueCaptureQueriesListener(); + } + + @Bean + public DataSource dataSource() { + BasicDataSource retVal = new BasicDataSource() { + + @Override + public Connection getConnection() { + ConnectionWrapper retVal; + try { + retVal = new ConnectionWrapper(super.getConnection()); + } catch (Exception e) { + ourLog.error("Exceeded maximum wait for connection (" + ourMaxThreads + " max)", e); + logGetConnectionStackTrace(); + fail("Exceeded maximum wait for connection (" + ourMaxThreads + " max): " + e); + retVal = null; + } + + try { + throw new Exception(); + } catch (Exception e) { + synchronized (myLastStackTrace) { + myLastStackTrace.add(e); + while (myLastStackTrace.size() > ourMaxThreads) { + myLastStackTrace.removeFirst(); + } + } + } + + return retVal; + } + + private void logGetConnectionStackTrace() { + StringBuilder b = new StringBuilder(); + int i = 0; + synchronized (myLastStackTrace) { + for (Iterator iter = myLastStackTrace.descendingIterator(); iter.hasNext(); ) { + Exception nextStack = iter.next(); + b.append("\n\nPrevious request stack trace "); + b.append(i++); + b.append(":"); + for (StackTraceElement next : nextStack.getStackTrace()) { + b.append("\n "); + b.append(next.getClassName()); + b.append("."); + b.append(next.getMethodName()); + b.append("("); + b.append(next.getFileName()); + b.append(":"); + b.append(next.getLineNumber()); + b.append(")"); + } + } + } + ourLog.info(b.toString()); + + if (!myHaveDumpedThreads) { + ourLog.info("Thread dump:" + crunchifyGenerateThreadDump()); + myHaveDumpedThreads = true; + } + } + + }; + + setConnectionProperties(retVal); + + SLF4JLogLevel level = SLF4JLogLevel.INFO; + DataSource dataSource = ProxyDataSourceBuilder + .create(retVal) +// .logQueryBySlf4j(level) + .logSlowQueryBySlf4j(10, TimeUnit.SECONDS, level) + .beforeQuery(new BlockLargeNumbersOfParamsListener()) + .beforeQuery(getMandatoryTransactionListener()) + .afterQuery(captureQueriesListener()) + .afterQuery(new CurrentThreadCaptureQueriesListener()) + .countQuery(singleQueryCountHolder()) + .afterMethod(captureQueriesListener()) + .build(); + + return dataSource; + } + + + public void setConnectionProperties(BasicDataSource theDataSource) { + theDataSource.setDriver(new org.h2.Driver()); + theDataSource.setUrl("jdbc:h2:mem:testdb_r4b"); + theDataSource.setMaxWaitMillis(30000); + theDataSource.setUsername(""); + theDataSource.setPassword(""); + theDataSource.setMaxTotal(ourMaxThreads); + } + + + @Bean + public SingleQueryCountHolder singleQueryCountHolder() { + return new SingleQueryCountHolder(); + } + + @Bean + public ProxyDataSourceBuilder.SingleQueryExecution getMandatoryTransactionListener() { + return new MandatoryTransactionListener(); + } + + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + retVal.setPersistenceUnitName("PU_HapiFhirJpaR4B"); + retVal.setDataSource(dataSource()); + retVal.setJpaProperties(jpaProperties()); + return retVal; + } + + private Properties jpaProperties() { + Properties extraProperties = new Properties(); + extraProperties.put("hibernate.format_sql", "false"); + extraProperties.put("hibernate.show_sql", "false"); + extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.dialect", getHibernateDialect()); + + hibernateSearchConfigurer.apply(extraProperties); + + ourLog.info("jpaProperties: {}", extraProperties); + + return extraProperties; + } + + public String getHibernateDialect() { + return HapiFhirH2Dialect.class.getName(); + } + + /** + * Bean which validates incoming requests + */ + @Bean + @Lazy + public RequestValidatingInterceptor requestValidatingInterceptor(FhirInstanceValidator theFhirInstanceValidator) { + RequestValidatingInterceptor requestValidator = new RequestValidatingInterceptor(); + requestValidator.setFailOnSeverity(ResultSeverityEnum.ERROR); + requestValidator.setAddResponseHeaderOnSeverity(null); + requestValidator.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION); + requestValidator.addValidatorModule(theFhirInstanceValidator); + + return requestValidator; + } + + @Bean + public IBinaryStorageSvc binaryStorage() { + return new MemoryBinaryStorageSvcImpl(); + } + + public static String crunchifyGenerateThreadDump() { + final StringBuilder dump = new StringBuilder(); + final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + final ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100); + for (ThreadInfo threadInfo : threadInfos) { + dump.append('"'); + dump.append(threadInfo.getThreadName()); + dump.append("\" "); + final Thread.State state = threadInfo.getThreadState(); + dump.append("\n java.lang.Thread.State: "); + dump.append(state); + final StackTraceElement[] stackTraceElements = threadInfo.getStackTrace(); + for (final StackTraceElement stackTraceElement : stackTraceElements) { + dump.append("\n at "); + dump.append(stackTraceElement); + } + dump.append("\n\n"); + } + return dump.toString(); + } + + public static int getMaxThreads() { + return ourMaxThreads; + } + +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java new file mode 100644 index 00000000000..a508b6fdcef --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java @@ -0,0 +1,57 @@ +package ca.uhn.fhir.jpa.util; + +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2022 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.ValueSet; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.stream.Collectors; + +import static ca.uhn.fhir.util.HapiExtensions.EXT_VALUESET_EXPANSION_MESSAGE; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ValueSetTestUtil { + + private final VersionCanonicalizer myCanonicalizer; + + public ValueSetTestUtil(FhirVersionEnum theFhirVersion) { + myCanonicalizer = new VersionCanonicalizer(theFhirVersion); + } + + public String extractExpansionMessage(IBaseResource theValueSet) { + ValueSet outcome = myCanonicalizer.valueSetToCanonical(theValueSet); + List extensions = outcome.getMeta().getExtensionsByUrl(EXT_VALUESET_EXPANSION_MESSAGE); + assertEquals(1, extensions.size()); + String expansionMessage = extensions.get(0).getValueAsPrimitive().getValueAsString(); + return expansionMessage; + } + + @Nonnull + public List toCodes(IBaseResource theExpandedValueSet) { + ValueSet outcome = myCanonicalizer.valueSetToCanonical(theExpandedValueSet); + return outcome.getExpansion().getContains().stream().map(t -> t.getCode()).collect(Collectors.toList()); + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImplTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/TermReadSvcImplTest.java similarity index 93% rename from hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImplTest.java rename to hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/TermReadSvcImplTest.java index 57f44b23eda..46697be77a2 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/TermReadSvcImplTest.java @@ -5,9 +5,9 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -class BaseTermReadSvcImplTest { +class TermReadSvcImplTest { - private final TermReadSvcR5 mySvc = new TermReadSvcR5(); + private final TermReadSvcImpl mySvc = new TermReadSvcImpl(); @Test void applyFilterMatchWords() { diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 68cad600846..46f49bd5779 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java index 22f47541994..37188462944 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java @@ -15,13 +15,10 @@ import ca.uhn.fhir.jpa.provider.DiffProvider; import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; -import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; +import ca.uhn.fhir.jpa.provider.JpaSystemProvider; import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; -import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; -import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; -import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.rest.openapi.OpenApiInterceptor; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; @@ -41,6 +38,7 @@ import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhirtest.config.SqlCaptureInterceptor; import ca.uhn.fhirtest.config.TestDstu2Config; import ca.uhn.fhirtest.config.TestDstu3Config; +import ca.uhn.fhirtest.config.TestR4BConfig; import ca.uhn.fhirtest.config.TestR4Config; import ca.uhn.fhirtest.config.TestR5Config; import ca.uhn.hapi.converters.server.VersionedApiConverterInterceptor; @@ -60,6 +58,7 @@ public class TestRestfulServer extends RestfulServer { public static final String FHIR_BASEURL_R5 = "fhir.baseurl.r5"; public static final String FHIR_BASEURL_R4 = "fhir.baseurl.r4"; + public static final String FHIR_BASEURL_R4B = "fhir.baseurl.r4b"; public static final String FHIR_BASEURL_DSTU2 = "fhir.baseurl.dstu2"; public static final String FHIR_BASEURL_DSTU3 = "fhir.baseurl.dstu3"; public static final String FHIR_BASEURL_TDL2 = "fhir.baseurl.tdl2"; @@ -112,9 +111,8 @@ public class TestRestfulServer extends RestfulServer { myAppCtx.register(TestDstu2Config.class, WebsocketDispatcherConfig.class); baseUrlProperty = FHIR_BASEURL_DSTU2; myAppCtx.refresh(); - setFhirContext(FhirContext.forDstu2()); + setFhirContext(FhirContext.forDstu2Cached()); beans = myAppCtx.getBean("myResourceProvidersDstu2", ResourceProviderFactory.class); - providers.add(myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class)); systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class); etagSupport = ETagSupportEnum.ENABLED; JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, myAppCtx.getBean(DaoConfig.class)); @@ -128,9 +126,8 @@ public class TestRestfulServer extends RestfulServer { myAppCtx.register(TestDstu3Config.class, WebsocketDispatcherConfig.class); baseUrlProperty = FHIR_BASEURL_DSTU3; myAppCtx.refresh(); - setFhirContext(FhirContext.forDstu3()); + setFhirContext(FhirContext.forDstu3Cached()); beans = myAppCtx.getBean("myResourceProvidersDstu3", ResourceProviderFactory.class); - providers.add(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class)); systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class); etagSupport = ETagSupportEnum.ENABLED; JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class), myAppCtx.getBean(ISearchParamRegistry.class)); @@ -146,9 +143,8 @@ public class TestRestfulServer extends RestfulServer { myAppCtx.register(TestR4Config.class, WebsocketDispatcherConfig.class); baseUrlProperty = FHIR_BASEURL_R4; myAppCtx.refresh(); - setFhirContext(FhirContext.forR4()); + setFhirContext(FhirContext.forR4Cached()); beans = myAppCtx.getBean("myResourceProvidersR4", ResourceProviderFactory.class); - providers.add(myAppCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class)); systemDao = myAppCtx.getBean("mySystemDaoR4", IFhirSystemDao.class); etagSupport = ETagSupportEnum.ENABLED; IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class); @@ -158,6 +154,24 @@ public class TestRestfulServer extends RestfulServer { providers.add(myAppCtx.getBean(GraphQLProvider.class)); break; } + case "R4B": { + myAppCtx = new AnnotationConfigWebApplicationContext(); + myAppCtx.setServletConfig(getServletConfig()); + myAppCtx.setParent(parentAppCtx); + myAppCtx.register(TestR4BConfig.class); + baseUrlProperty = FHIR_BASEURL_R4B; + myAppCtx.refresh(); + setFhirContext(FhirContext.forR4BCached()); + beans = myAppCtx.getBean("myResourceProvidersR4B", ResourceProviderFactory.class); + systemDao = myAppCtx.getBean("mySystemDaoR4B", IFhirSystemDao.class); + etagSupport = ETagSupportEnum.ENABLED; + IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class); + JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(this, systemDao, myAppCtx.getBean(DaoConfig.class), myAppCtx.getBean(ISearchParamRegistry.class), validationSupport); + setServerConformanceProvider(confProvider); + providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); + providers.add(myAppCtx.getBean(GraphQLProvider.class)); + break; + } case "R5": { myAppCtx = new AnnotationConfigWebApplicationContext(); myAppCtx.setServletConfig(getServletConfig()); @@ -167,7 +181,6 @@ public class TestRestfulServer extends RestfulServer { myAppCtx.refresh(); setFhirContext(FhirContext.forR5()); beans = myAppCtx.getBean("myResourceProvidersR5", ResourceProviderFactory.class); - providers.add(myAppCtx.getBean("mySystemProviderR5", JpaSystemProviderR5.class)); systemDao = myAppCtx.getBean("mySystemDaoR5", IFhirSystemDao.class); etagSupport = ETagSupportEnum.ENABLED; IValidationSupport validationSupport = myAppCtx.getBean(IValidationSupport.class); @@ -181,6 +194,8 @@ public class TestRestfulServer extends RestfulServer { throw new ServletException(Msg.code(1975) + "Unknown FHIR version specified in init-param[FhirVersion]: " + fhirVersionParam); } + providers.add(myAppCtx.getBean(JpaSystemProvider.class)); + /* * On the DSTU2 endpoint, we want to enable ETag support */ diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java index 30d15078d3d..9e7f03f4d7f 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java @@ -46,22 +46,27 @@ public class FhirTesterConfig { .withId("home_r4") .withFhirVersion(FhirVersionEnum.R4) .withBaseUrl("http://hapi.fhir.org/baseR4") - .withName("UHN/HAPI Server (R4 FHIR)") + .withName("HAPI Test Server (R4 FHIR)") + .addServer() + .withId("home_r4b") + .withFhirVersion(FhirVersionEnum.R4B) + .withBaseUrl("http://hapi.fhir.org/baseR4B") + .withName("HAPI Test Server (R4B FHIR)") .addServer() .withId("home_21") .withFhirVersion(FhirVersionEnum.DSTU3) .withBaseUrl("http://hapi.fhir.org/baseDstu3") - .withName("UHN/HAPI Server (STU3 FHIR)") + .withName("HAPI Test Server (STU3 FHIR)") .addServer() .withId("hapi_dev") .withFhirVersion(FhirVersionEnum.DSTU2) .withBaseUrl("http://hapi.fhir.org/baseDstu2") - .withName("UHN/HAPI Server (DSTU2 FHIR)") + .withName("HAPI Test Server (DSTU2 FHIR)") .addServer() .withId("home_r5") .withFhirVersion(FhirVersionEnum.R5) .withBaseUrl("http://hapi.fhir.org/baseR5") - .withName("UHN/HAPI Server (R5 FHIR)") + .withName("HAPI Test Server (R5 FHIR)") // .addServer() // .withId("tdl_d2") // .withFhirVersion(FhirVersionEnum.DSTU2) diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java new file mode 100644 index 00000000000..23a75c9b9d2 --- /dev/null +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java @@ -0,0 +1,203 @@ +package ca.uhn.fhirtest.config; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.config.HapiJpaConfig; +import ca.uhn.fhir.jpa.config.r4b.JpaR4BConfig; +import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect; +import ca.uhn.fhir.jpa.model.entity.ModelConfig; +import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers; +import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener; +import ca.uhn.fhir.jpa.validation.ValidationSettings; +import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; +import ca.uhn.fhir.validation.IInstanceValidatorModule; +import ca.uhn.fhir.validation.ResultSeverityEnum; +import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; +import org.apache.commons.dbcp2.BasicDataSource; +import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; +import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings; +import org.hibernate.search.engine.cfg.BackendSettings; +import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Primary; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +@Configuration +@Import({CommonConfig.class, JpaR4BConfig.class, HapiJpaConfig.class}) +@EnableTransactionManagement() +public class TestR4BConfig { + public static final String FHIR_DB_USERNAME = "fhir.db.username"; + public static final String FHIR_DB_PASSWORD = "fhir.db.password"; + public static final String FHIR_LUCENE_LOCATION_R4B = "fhir.lucene.location.r4b"; + public static final Integer COUNT_SEARCH_RESULTS_UP_TO = 50000; + private static final Logger ourLog = LoggerFactory.getLogger(TestR4BConfig.class); + private String myDbUsername = System.getProperty(TestR4BConfig.FHIR_DB_USERNAME); + private String myDbPassword = System.getProperty(TestR4BConfig.FHIR_DB_PASSWORD); + private String myFhirLuceneLocation = System.getProperty(FHIR_LUCENE_LOCATION_R4B); + + @Bean + public DaoConfig daoConfig() { + DaoConfig retVal = new DaoConfig(); + retVal.setAllowContainsSearches(true); + retVal.setAllowMultipleDelete(true); + retVal.setAllowInlineMatchUrlReferences(true); + retVal.setAllowExternalReferences(true); + retVal.getTreatBaseUrlsAsLocal().add("http://hapi.fhir.org/baseR4B"); + retVal.getTreatBaseUrlsAsLocal().add("https://hapi.fhir.org/baseR4B"); + retVal.getTreatBaseUrlsAsLocal().add("http://fhirtest.uhn.ca/baseR4B"); + retVal.getTreatBaseUrlsAsLocal().add("https://fhirtest.uhn.ca/baseR4B"); + retVal.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED); + retVal.setCountSearchResultsUpTo(TestR4BConfig.COUNT_SEARCH_RESULTS_UP_TO); + retVal.setFetchSizeDefaultMaximum(10000); + retVal.setExpungeEnabled(true); + retVal.setFilterParameterEnabled(true); + retVal.setDefaultSearchParamsCanBeOverridden(false); + retVal.getModelConfig().setIndexOnContainedResources(true); + return retVal; + } + + @Bean + public ModelConfig modelConfig() { + ModelConfig retVal = daoConfig().getModelConfig(); + retVal.setIndexIdentifierOfType(true); + return retVal; + } + + @Bean + public ValidationSettings validationSettings() { + ValidationSettings retVal = new ValidationSettings(); + retVal.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.CHECK_VALID); + return retVal; + } + + @Bean(name = "myPersistenceDataSourceR4B") + public DataSource dataSource() { + ourLog.info("Starting R4B database with DB username: {}", myDbUsername); + ourLog.info("Have system property username: {}", System.getProperty(FHIR_DB_USERNAME)); + + BasicDataSource retVal = new BasicDataSource(); + if (CommonConfig.isLocalTestMode()) { + retVal.setUrl("jdbc:h2:mem:fhirtest_r4b"); + } else { + retVal.setDriver(new org.postgresql.Driver()); + retVal.setUrl("jdbc:postgresql://localhost/fhirtest_r4b"); + } + retVal.setUsername(myDbUsername); + retVal.setPassword(myDbPassword); + retVal.setDefaultQueryTimeout(20); + retVal.setTestOnBorrow(true); + + DataSource dataSource = ProxyDataSourceBuilder + .create(retVal) +// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL") + .logSlowQueryBySlf4j(10000, TimeUnit.MILLISECONDS) + .afterQuery(new CurrentThreadCaptureQueriesListener()) + .countQuery() + .build(); + + return dataSource; + } + + // TODO KHS there is code duplication between this and the other Test*Config classes in this directory + @Bean + public DatabaseBackedPagingProvider databaseBackedPagingProvider() { + DatabaseBackedPagingProvider retVal = new DatabaseBackedPagingProvider(); + retVal.setDefaultPageSize(20); + retVal.setMaximumPageSize(500); + return retVal; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + retVal.setPersistenceUnitName("PU_HapiFhirJpaR4B"); + retVal.setDataSource(dataSource()); + retVal.setJpaProperties(jpaProperties()); + return retVal; + } + + private Properties jpaProperties() { + Properties extraProperties = new Properties(); + if (CommonConfig.isLocalTestMode()) { + extraProperties.put("hibernate.dialect", HapiFhirH2Dialect.class.getName()); + } else { + extraProperties.put("hibernate.dialect", HapiFhirPostgres94Dialect.class.getName()); + } + extraProperties.put("hibernate.format_sql", "false"); + extraProperties.put("hibernate.show_sql", "false"); + extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.jdbc.batch_size", "20"); + extraProperties.put("hibernate.cache.use_query_cache", "false"); + extraProperties.put("hibernate.cache.use_second_level_cache", "false"); + extraProperties.put("hibernate.cache.use_structured_entries", "false"); + extraProperties.put("hibernate.cache.use_minimal_puts", "false"); + + extraProperties.put(BackendSettings.backendKey(BackendSettings.TYPE), "lucene"); + extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER), + HapiHSearchAnalysisConfigurers.HapiLuceneAnalysisConfigurer.class.getName()); + extraProperties.put(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE), "local-filesystem"); + extraProperties.put(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_ROOT), myFhirLuceneLocation); + extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), "LUCENE_CURRENT"); + + return extraProperties; + } + + /** + * Bean which validates incoming requests + * + * @param theFhirInstanceValidator + */ + @Bean + @Lazy + public RequestValidatingInterceptor requestValidatingInterceptor(IInstanceValidatorModule theFhirInstanceValidator) { + RequestValidatingInterceptor requestValidator = new RequestValidatingInterceptor(); + requestValidator.setFailOnSeverity(null); + requestValidator.setAddResponseHeaderOnSeverity(null); + requestValidator.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION); + requestValidator.addValidatorModule(theFhirInstanceValidator); + requestValidator.setIgnoreValidatorExceptions(true); + + return requestValidator; + } + + @Bean + public PublicSecurityInterceptor securityInterceptor() { + return new PublicSecurityInterceptor(); + } + + @Bean + @Primary + public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager retVal = new JpaTransactionManager(); + retVal.setEntityManagerFactory(entityManagerFactory); + return retVal; + } + + /** + * This lets the "@Value" fields reference properties from the properties file + */ + @Bean + public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { + return new PropertySourcesPlaceholderConfigurer(); + } + + +} diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml deleted file mode 100644 index 77d93e67f21..00000000000 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - home_dev , DSTU2 , UHN/HAPI Server (DSTU2 FHIR) , http://fhirtest.uhn.ca/baseDstu2 - home_21 , DSTU3 , UHN/HAPI Server (DSTU3 FHIR) , http://fhirtest.uhn.ca/baseDstu3 - home , DSTU1 , UHN/HAPI Server (DSTU1 FHIR) , http://fhirtest.uhn.ca/baseDstu1 - hidev , DSTU2 , Health Intersections (DSTU2 FHIR) , http://fhir-dev.healthintersections.com.au/open - hi , DSTU1 , Health Intersections (DSTU1 FHIR) , http://fhir.healthintersections.com.au/open - furored2 , DSTU2 , Spark - Furore (DSTU2 FHIR) , http://spark-dstu2.furore.com/fhir - furore , DSTU1 , Spark - Furore (DSTU1 FHIR) , http://spark.furore.com/fhir - sof , DSTU2 , SQL on FHIR - HealthConnex (DSTU2 FHIR) , http://sqlonfhir.azurewebsites.net/fhir - - - - - - - - - - - - - - - - - - - - diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml index dd383fa7648..baeeb5d9bb3 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml @@ -46,6 +46,16 @@ 1 + + fhirServletR4B + ca.uhn.fhirtest.TestRestfulServer + + FhirVersion + R4B + + 1 + + fhirServletR4 ca.uhn.fhirtest.TestRestfulServer @@ -80,6 +90,10 @@ fhirServletR5 /baseR5/* + + fhirServletR4B + /baseR4B/* + fhirServletR4 /baseR4/* diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java b/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java index 54c9bcfbe0a..b46303894cf 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java @@ -31,6 +31,8 @@ public class UhnFhirTestApp { System.setProperty("fhir.lucene.location.dstu3", "./target/testlucene_dstu3"); System.setProperty("fhir.db.location.r4", "./target/fhirtest_r4"); System.setProperty("fhir.lucene.location.r4", "./target/testlucene_r4"); + System.setProperty("fhir.db.location.r4b", "./target/fhirtest_r4b"); + System.setProperty("fhir.lucene.location.r4b", "./target/testlucene_r4b"); System.setProperty("fhir.db.location.r5", "./target/fhirtest_r5"); System.setProperty("fhir.lucene.location.r5", "./target/testlucene_r5"); System.setProperty("fhir.db.location.tdl2", "./target/testdb_tdl2"); @@ -41,6 +43,7 @@ public class UhnFhirTestApp { System.setProperty("fhir.baseurl.dstu1", base.replace("Dstu2", "Dstu1")); System.setProperty("fhir.baseurl.dstu3", base.replace("Dstu2", "Dstu3")); System.setProperty("fhir.baseurl.r4", base.replace("Dstu2", "R4")); + System.setProperty("fhir.baseurl.r4b", base.replace("Dstu2", "R4B")); System.setProperty("fhir.baseurl.r5", base.replace("Dstu2", "R5")); System.setProperty("fhir.baseurl.tdl2", base.replace("baseDstu2", "testDataLibraryDstu2")); System.setProperty("fhir.baseurl.tdl3", base.replace("baseDstu2", "testDataLibraryStu3")); diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 666ca206f78..7de91f3c884 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 8d9f38c6323..9ca4225b6ff 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java b/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java index 4fbd1445bc0..d7fa1a826b8 100644 --- a/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java +++ b/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java @@ -61,6 +61,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -927,6 +928,9 @@ public class OpenApiInterceptor { canonical = VersionConvertorFactory_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theNonCanonical); } else if (theNonCanonical instanceof org.hl7.fhir.r5.model.Resource) { canonical = VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r5.model.Resource) theNonCanonical); + } else if (theNonCanonical instanceof org.hl7.fhir.r4b.model.Resource) { + org.hl7.fhir.r5.model.Resource r5 = VersionConvertorFactory_43_50.convertResource((org.hl7.fhir.r4b.model.Resource) theNonCanonical); + canonical = VersionConvertorFactory_40_50.convertResource(r5); } else { canonical = theNonCanonical; } diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 783a65beb7c..ee94dcf1a8d 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java index 5ce23ecb158..01ec6627eb5 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java @@ -697,6 +697,12 @@ public class RestfulServerUtils { } private static FhirContext getContextForVersion(FhirContext theContext, FhirVersionEnum theForVersion) { + + // TODO: remove once we've bumped the core lib version + if (theContext.getVersion().getVersion() == FhirVersionEnum.R4B && theForVersion == FhirVersionEnum.R5) { + return theContext; + } + FhirContext context = theContext; if (context.getVersion().getVersion() != theForVersion) { context = myFhirContextMap.get(theForVersion); 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 14118740722..49e5cab8f69 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 2aeb0a5cf3d..6bea6ee6a64 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 2f22d7e71e4..0fdfd46ae15 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 699923f8d7e..9f14fe48776 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 306f2deff0b..52423b795fd 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 b1c51c441b9..2af4156231c 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 7e1a0f0b7fa..de1ba74621c 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index b1d01355e3e..dfe21dd2ccc 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 b13baf36601..201e083d9eb 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 dc1a8a0ea4b..a7132ccb479 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index ee0de1d45fc..3926d84b6b2 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 681ef3b74dc..6eb9268af23 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index c35f804d833..685a5872364 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProvider.java index 3ee76f0dd04..d73047fa7c2 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProvider.java @@ -96,6 +96,13 @@ public class GraphQLProvider { myEngineFactory = () -> new org.hl7.fhir.r4.utils.GraphQLEngine(workerContext); break; } + case R4B: { + IValidationSupport validationSupport = theValidationSupport; + validationSupport = ObjectUtils.defaultIfNull(validationSupport, new DefaultProfileValidationSupport(theFhirContext)); + org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext workerContext = new org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext(theFhirContext, validationSupport); + myEngineFactory = () -> new org.hl7.fhir.r4b.utils.GraphQLEngine(workerContext); + break; + } case R5: { IValidationSupport validationSupport = theValidationSupport; validationSupport = ObjectUtils.defaultIfNull(validationSupport, new DefaultProfileValidationSupport(theFhirContext)); diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 378fdfac270..053ba3bde1a 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 7e32f71a3d0..a10719ea7d1 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 515e433f9ab..e54e84a7a08 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java index 49c9940f7a9..ab4e3d9c0bb 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java @@ -100,7 +100,7 @@ public class CustomTypeServerDstu3 { assertEquals(400, status.getStatusLine().getStatusCode()); OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent); - assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); + assertEquals("HAPI-0365: Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); } @Test @@ -122,7 +122,7 @@ public class CustomTypeServerDstu3 { assertEquals(400, status.getStatusLine().getStatusCode()); OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent); - assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); + assertEquals("HAPI-0365: Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); } diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 3da310c77eb..9cb4486a254 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index c722959472a..583119cc297 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java index cd42c3fa81f..125754b1f4a 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java @@ -129,6 +129,60 @@ public class OperationGenericServer2R4Test { } + @Test + public void testDeclarativeTypedListParameters() throws Exception { + + @SuppressWarnings("unused") + class PatientProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Operation(name = "$OP_INSTANCE") + public Parameters opInstance( + @ResourceParam() IBaseResource theResourceParam, + @IdParam IdType theId, + @OperationParam(name = "PARAM1", typeName = "code", max = OperationParam.MAX_UNLIMITED) List> theParam1 + ) { + + ourLastId = theId; + ourLastParam1 = theParam1; + ourLastResourceParam = (Parameters) theResourceParam; + + Parameters retVal = new Parameters(); + retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); + return retVal; + } + + } + + PatientProvider provider = new PatientProvider(); + startServer(provider); + + Parameters p = new Parameters(); + p.addParameter().setName("PARAM1").setValue(new CodeType("PARAM1val")); + p.addParameter().setName("PARAM1").setValue(new CodeType("PARAM1val2")); + String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); + + HttpPost httpPost = new HttpPost("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); + try (CloseableHttpResponse status = ourClient.execute(httpPost)) { + assertEquals(200, status.getStatusLine().getStatusCode()); + String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info(response); + status.getEntity().getContent().close(); + + List> param1 = (List>) ourLastParam1; + assertEquals(2, param1.size()); + assertEquals(CodeType.class, param1.get(0).getClass()); + assertEquals("PARAM1val", param1.get(0).getValue()); + assertEquals("PARAM1val2", param1.get(1).getValue()); + } + + } + @SuppressWarnings("unchecked") @Test public void testDeclarativeStringTypedParameters() throws Exception { diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml new file mode 100644 index 00000000000..ea5dd4aab5a --- /dev/null +++ b/hapi-fhir-structures-r4b/pom.xml @@ -0,0 +1,391 @@ + + 4.0.0 + + + ca.uhn.hapi.fhir + hapi-deployable-pom + 6.2.0-PRE17-SNAPSHOT + ../hapi-deployable-pom/pom.xml + + + hapi-fhir-structures-r4b + bundle + + HAPI FHIR Structures - FHIR r4b + + + + com.squareup.okhttp3 + okhttp + + + ca.uhn.hapi.fhir + hapi-fhir-base + ${project.version} + + + + ca.uhn.hapi.fhir + org.hl7.fhir.utilities + ${fhir_core_version} + + + ca.uhn.hapi.fhir + org.hl7.fhir.r4b + ${fhir_core_version} + + + org.fhir + ucum + true + + + junit + junit + + + + + + + com.fasterxml.woodstox + woodstox-core + test + + + es.nitaur.markdown + txtmark + true + + + org.antlr + ST4 + true + + + xpp3 + xpp3 + true + + + com.google.guava + guava + true + + + + + com.github.ben-manes.caffeine + caffeine + true + + + + + ca.uhn.hapi.fhir + hapi-fhir-client + ${project.version} + true + + + ca.uhn.hapi.fhir + hapi-fhir-server + ${project.version} + true + + + javax.servlet + javax.servlet-api + true + + + + + + org.projectlombok + lombok + 1.18.22 + true + + + org.apache.poi + ooxml-schemas + true + + + org.apache.poi + poi + true + + + org.apache.poi + poi-ooxml + true + + + org.apache.poi + poi-ooxml-schemas + true + + + + com.google.code.gson + gson + true + + + org.jetbrains + annotations + true + + + + + org.xmlunit + xmlunit-core + test + + + org.eclipse.jetty + jetty-servlets + test + + + org.eclipse.jetty + jetty-servlet + test + + + org.eclipse.jetty + jetty-server + test + + + org.eclipse.jetty + jetty-util + test + + + org.eclipse.jetty + jetty-webapp + test + + + org.eclipse.jetty + jetty-http + test + + + ch.qos.logback + logback-classic + true + test + + + org.thymeleaf + thymeleaf + test + + + ca.uhn.hapi.fhir + hapi-fhir-test-utilities + ${project.version} + test + + + + + com.helger + ph-schematron + test + + + com.helger + ph-commons + test + + + javax.activation + javax.activation-api + test + + + javax.xml.bind + jaxb-api + test + + + org.glassfish.jaxb + jaxb-runtime + test + + + + + net.sf.json-lib + json-lib + jdk15 + test + + + commons-logging + commons-logging + + + commons-lang + commons-lang + + + + + net.sf.json-lib + json-lib + jdk15-sources + test + + + commons-logging + commons-logging + + + + + directory-naming + naming-java + test + + + commons-logging + commons-logging + + + + + + org.springframework + spring-context + test + + + org.springframework + spring-web + test + + + com.google.code.findbugs + jsr305 + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + public + true + + ca.uhn.hapi.fhir:org.hl7.fhir.r4b + + + + com.squareup.okhttp3 + okhttp + ${okhttp_version} + + + + + + + + + + + org.jacoco + jacoco-maven-plugin + + true + + + + default-prepare-agent + + prepare-agent + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + @{argLine} ${surefire_jvm_args} + + + + org.codehaus.mojo + license-maven-plugin + + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + + true + + + + org.apache.felix + maven-bundle-plugin + true + + + <_nouses>true + <_removeheaders>Built-By, Include-Resource, Private-Package, Require-Capability + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + ca.uhn.hapi.fhir:org.hl7.fhir.r4b + + + + com.google.guava + guava + ${guava_version} + + + com.squareup.okhttp3 + okhttp + ${okhttp_version} + + + + + + + + diff --git a/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/ctx/FhirR4B.java b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/ctx/FhirR4B.java new file mode 100644 index 00000000000..9b5fc3d01da --- /dev/null +++ b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/ctx/FhirR4B.java @@ -0,0 +1,134 @@ +package org.hl7.fhir.r4b.hapi.ctx; + +/* + * #%L + * HAPI FHIR Structures - DSTU2 (FHIR v1.0.0) + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.fhirpath.IFhirPath; +import ca.uhn.fhir.model.api.IFhirVersion; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; +import ca.uhn.fhir.util.ReflectionUtil; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4b.hapi.fhirpath.FhirPathR4B; +import org.hl7.fhir.r4b.hapi.rest.server.R4BBundleFactory; +import org.hl7.fhir.r4b.model.Coding; +import org.hl7.fhir.r4b.model.IdType; +import org.hl7.fhir.r4b.model.Reference; +import org.hl7.fhir.r4b.model.Resource; +import org.hl7.fhir.r4b.model.StructureDefinition; + +import java.io.InputStream; +import java.util.Date; +import java.util.List; + +public class FhirR4B implements IFhirVersion { + + private String myId; + + @Override + public IFhirPath createFhirPathExecutor(FhirContext theFhirContext) { + return new FhirPathR4B(theFhirContext); + } + + @Override + public IBaseResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase) { + StructureDefinition retVal = new StructureDefinition(); + + RuntimeResourceDefinition def = theRuntimeResourceDefinition; + + myId = def.getId(); + if (StringUtils.isBlank(myId)) { + myId = theRuntimeResourceDefinition.getName().toLowerCase(); + } + + retVal.setId(new IdDt(myId)); + return retVal; + } + + @SuppressWarnings("rawtypes") + @Override + public Class getContainedType() { + return List.class; + } + + @Override + public InputStream getFhirVersionPropertiesFile() { + String path = "org/hl7/fhir/r4b/model/fhirversion.properties"; + InputStream str = FhirR4B.class.getResourceAsStream("/" + path); + if (str == null) { + str = FhirR4B.class.getResourceAsStream(path); + } + if (str == null) { + throw new ConfigurationException(Msg.code(200) + "Can not find model property file on classpath: " + path); + } + return str; + } + + @Override + public IPrimitiveType getLastUpdated(IBaseResource theResource) { + return ((Resource) theResource).getMeta().getLastUpdatedElement(); + } + + @Override + public String getPathToSchemaDefinitions() { + return "/org/hl7/fhir/r4b/model/schema"; + } + + @Override + public Class getResourceReferenceType() { + return Reference.class; + } + + @Override + public Object getServerVersion() { + return ReflectionUtil.newInstanceOfFhirServerType("org.hl7.fhir.r4b.hapi.ctx.FhirServerR4B"); + } + + @Override + public FhirVersionEnum getVersion() { + return FhirVersionEnum.R4B; + } + + @Override + public IVersionSpecificBundleFactory newBundleFactory(FhirContext theContext) { + return new R4BBundleFactory(theContext); + } + + @Override + public IBaseCoding newCodingDt() { + return new Coding(); + } + + @Override + public IIdType newIdType() { + return new IdType(); + } + +} diff --git a/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/ctx/FhirServerR4B.java b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/ctx/FhirServerR4B.java new file mode 100644 index 00000000000..ab55b485eb9 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/ctx/FhirServerR4B.java @@ -0,0 +1,13 @@ +package org.hl7.fhir.r4b.hapi.ctx; + +import ca.uhn.fhir.rest.api.server.IFhirVersionServer; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider; + +public class FhirServerR4B implements IFhirVersionServer { + @Override + public ServerCapabilityStatementProvider createServerConformanceProvider(RestfulServer theServer) { + return new ServerCapabilityStatementProvider(theServer); + } + +} diff --git a/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/ctx/HapiWorkerContext.java b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/ctx/HapiWorkerContext.java new file mode 100644 index 00000000000..3a09e9734ed --- /dev/null +++ b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/ctx/HapiWorkerContext.java @@ -0,0 +1,518 @@ +package org.hl7.fhir.r4b.hapi.ctx; + +import ca.uhn.fhir.context.support.ValueSetExpansionOptions; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.ConceptValidationOptions; +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.util.CoverageIgnore; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.time.DateUtils; +import org.fhir.ucum.UcumService; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.TerminologyServiceException; +import org.hl7.fhir.r4b.context.IWorkerContext; +import org.hl7.fhir.r4b.formats.IParser; +import org.hl7.fhir.r4b.formats.ParserType; +import org.hl7.fhir.r4b.model.CanonicalResource; +import org.hl7.fhir.r4b.model.CodeSystem; +import org.hl7.fhir.r4b.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.r4b.model.CodeableConcept; +import org.hl7.fhir.r4b.model.Coding; +import org.hl7.fhir.r4b.model.ConceptMap; +import org.hl7.fhir.r4b.model.ElementDefinition.ElementDefinitionBindingComponent; +import org.hl7.fhir.r4b.model.Parameters; +import org.hl7.fhir.r4b.model.Resource; +import org.hl7.fhir.r4b.model.ResourceType; +import org.hl7.fhir.r4b.model.StructureDefinition; +import org.hl7.fhir.r4b.model.StructureMap; +import org.hl7.fhir.r4b.model.ValueSet; +import org.hl7.fhir.r4b.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.r4b.terminologies.ValueSetExpander; +import org.hl7.fhir.r4b.utils.validation.IResourceValidator; +import org.hl7.fhir.r4b.utils.validation.ValidationContextCarrier; +import org.hl7.fhir.utilities.TimeTracker; +import org.hl7.fhir.utilities.TranslationServices; +import org.hl7.fhir.utilities.i18n.I18nBase; +import org.hl7.fhir.utilities.npm.BasePackageCacheManager; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; +import org.hl7.fhir.utilities.validation.ValidationOptions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public final class HapiWorkerContext extends I18nBase implements IWorkerContext { + private final FhirContext myCtx; + private final Cache myFetchedResourceCache; + private final IValidationSupport myValidationSupport; + private Parameters myExpansionProfile; + private String myOverrideVersionNs; + + public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) { + Validate.notNull(theCtx, "theCtx must not be null"); + Validate.notNull(theValidationSupport, "theValidationSupport must not be null"); + myCtx = theCtx; + myValidationSupport = theValidationSupport; + + long timeoutMillis = 10 * DateUtils.MILLIS_PER_SECOND; + if (System.getProperties().containsKey(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)) { + timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)); + } + + myFetchedResourceCache = Caffeine.newBuilder().expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS).build(); + + // Set a default locale + setValidationMessageLanguage(getLocale()); + } + + @Override + public List allStructures() { + return myValidationSupport.fetchAllStructureDefinitions(); + } + + @Override + public List getStructures() { + return allStructures(); + } + + @Override + public CodeSystem fetchCodeSystem(String theSystem) { + if (myValidationSupport == null) { + return null; + } else { + return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem); + } + } + + @Override + public CodeSystem fetchCodeSystem(String theSystem, String version) { + if (myValidationSupport == null) { + return null; + } else { + return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem); + } + } + + @Override + public List findMapsForSource(String theUrl) { + throw new UnsupportedOperationException(Msg.code(201)); + } + + @Override + public String getAbbreviation(String theName) { + throw new UnsupportedOperationException(Msg.code(202)); + } + + @Override + public IParser getParser(ParserType theType) { + throw new UnsupportedOperationException(Msg.code(203)); + } + + @Override + public IParser getParser(String theType) { + throw new UnsupportedOperationException(Msg.code(204)); + } + + @Override + public List getResourceNames() { + List result = new ArrayList<>(); + for (ResourceType next : ResourceType.values()) { + result.add(next.name()); + } + Collections.sort(result); + return result; + } + + @Override + public IParser newJsonParser() { + throw new UnsupportedOperationException(Msg.code(205)); + } + + @Override + public IResourceValidator newValidator() { + throw new UnsupportedOperationException(Msg.code(206)); + } + + @Override + public IParser newXmlParser() { + throw new UnsupportedOperationException(Msg.code(207)); + } + + @Override + public String oid2Uri(String theCode) { + throw new UnsupportedOperationException(Msg.code(208)); + } + + @Override + public boolean supportsSystem(String theSystem) { + if (myValidationSupport == null) { + return false; + } else { + return myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), theSystem); + } + } + + + @Override + public ValidationResult validateCode(ValidationOptions theOptions, CodeableConcept theCode, ValueSet theVs) { + for (Coding next : theCode.getCoding()) { + ValidationResult retVal = validateCode(theOptions, next, theVs); + if (retVal.isOk()) { + return retVal; + } + } + + return new ValidationResult(IssueSeverity.ERROR, null); + } + + @Override + public ValidationResult validateCode(ValidationOptions theOptions, Coding theCode, ValueSet theVs) { + String system = theCode.getSystem(); + String code = theCode.getCode(); + String display = theCode.getDisplay(); + return validateCode(theOptions, system, null, code, display, theVs); + } + + @Override + public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs, ValidationContextCarrier ctxt) { + return validateCode(options, code, vs); + } + + @Override + public void validateCodeBatch(ValidationOptions options, List codes, ValueSet vs) { + throw new UnsupportedOperationException(Msg.code(209)); + } + + @Override + public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet theValueSet, boolean cacheOk, boolean heiarchical, boolean incompleteOk) { + return null; + } + + @Override + public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion, + String theCode, String theDisplay) { + IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), + convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, null); + if (result == null) { + return null; + } + IssueSeverity severity = null; + if (result.getSeverity() != null) { + severity = IssueSeverity.fromCode(result.getSeverityCode()); + } + ConceptDefinitionComponent definition = new ConceptDefinitionComponent().setCode(result.getCode()); + return new ValidationResult(severity, result.getMessage(), theSystem, definition); + } + + @Override + public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion, + String theCode, String theDisplay, ValueSet theVs) { + IValidationSupport.CodeValidationResult outcome; + if (isNotBlank(theVs.getUrl())) { + outcome = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), + convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs.getUrl()); + } else { + outcome = myValidationSupport.validateCodeInValueSet(new ValidationSupportContext(myValidationSupport), + convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs); + } + + if (outcome != null && outcome.isOk()) { + ConceptDefinitionComponent definition = new ConceptDefinitionComponent(); + definition.setCode(theCode); + definition.setDisplay(outcome.getDisplay()); + return new ValidationResult(theSystem, definition); + } + + return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + + Constants.codeSystemWithDefaultDescription(theSystem) + "]"); + } + + @Override + public ValidationResult validateCode(ValidationOptions theOptions, String code, ValueSet vs) { + return validateCode(theOptions, null, null, code, null, vs); + } + + @Override + @CoverageIgnore + public List allConformanceResources() { + throw new UnsupportedOperationException(Msg.code(210)); + } + + @Override + public void generateSnapshot(StructureDefinition p) throws FHIRException { + myValidationSupport.generateSnapshot(new ValidationSupportContext(myValidationSupport), p, "", "", ""); + } + + @Override + public void generateSnapshot(StructureDefinition mr, boolean ifLogical) { + + } + + @Override + public Parameters getExpansionParameters() { + return myExpansionProfile; + } + + @Override + public void setExpansionProfile(Parameters theExpParameters) { + myExpansionProfile = theExpParameters; + } + + @Override + @CoverageIgnore + public boolean hasCache() { + throw new UnsupportedOperationException(Msg.code(211)); + } + + @Override + public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk, boolean theHierarchical) { + throw new UnsupportedOperationException(Msg.code(212)); + } + + @Override + public ValueSetExpander.ValueSetExpansionOutcome expandVS(ConceptSetComponent theInc, boolean hierarchical) throws TerminologyServiceException { + ValueSet input = new ValueSet(); + input.getCompose().addInclude(theInc); + ValueSetExpansionOptions options = new ValueSetExpansionOptions(); + options.setIncludeHierarchy(hierarchical); + IValidationSupport.ValueSetExpansionOutcome output = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), options, input); + return new ValueSetExpander.ValueSetExpansionOutcome((ValueSet) output.getValueSet(), output.getError(), null); + } + + @Override + public Locale getLocale() { + return Locale.getDefault(); + } + + @Override + public void setLocale(Locale locale) { + // ignore + } + + @Override + public ILoggingService getLogger() { + throw new UnsupportedOperationException(Msg.code(213)); + } + + @Override + public void setLogger(ILoggingService theLogger) { + throw new UnsupportedOperationException(Msg.code(214)); + } + + @Override + public String getVersion() { + return myCtx.getVersion().getVersion().getFhirVersionString(); + } + + @Override + public String getSpecUrl() { + throw new UnsupportedOperationException(Msg.code(215)); + } + + @Override + public UcumService getUcumService() { + throw new UnsupportedOperationException(Msg.code(216)); + } + + @Override + public void setUcumService(UcumService ucumService) { + throw new UnsupportedOperationException(Msg.code(217)); + } + + @Override + public boolean isNoTerminologyServer() { + return false; + } + + @Override + public Set getCodeSystemsUsed() { + throw new UnsupportedOperationException(Msg.code(218)); + } + + @Override + public TranslationServices translator() { + throw new UnsupportedOperationException(Msg.code(219)); + } + + @Override + public List listTransforms() { + throw new UnsupportedOperationException(Msg.code(220)); + } + + @Override + public StructureMap getTransform(String url) { + throw new UnsupportedOperationException(Msg.code(221)); + } + + @Override + public String getOverrideVersionNs() { + return myOverrideVersionNs; + } + + @Override + public void setOverrideVersionNs(String value) { + myOverrideVersionNs = value; + } + + @Override + public StructureDefinition fetchTypeDefinition(String typeName) { + return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); + } + + @Override + public StructureDefinition fetchRawProfile(String url) { + throw new UnsupportedOperationException(Msg.code(222)); + } + + @Override + public List getTypeNames() { + throw new UnsupportedOperationException(Msg.code(223)); + } + + @Override + public T fetchResource(Class theClass, String theUri) { + if (myValidationSupport == null || theUri == null) { + return null; + } else { + @SuppressWarnings("unchecked") + T retVal = (T) myFetchedResourceCache.get(theUri, t -> myValidationSupport.fetchResource(theClass, theUri)); + return retVal; + } + } + + @Override + public T fetchResourceWithException(Class theClass, String theUri) throws FHIRException { + T retVal = fetchResource(theClass, theUri); + if (retVal == null) { + throw new FHIRException(Msg.code(224) + "Could not find resource: " + theUri); + } + return retVal; + } + + @Override + public T fetchResource(Class theClass, String theUri, String theVersion) { + return fetchResource(theClass, theUri + "|" + theVersion); + } + + @Override + public T fetchResource(Class class_, String uri, CanonicalResource canonicalForSource) { + throw new UnsupportedOperationException(Msg.code(225)); + } + + @Override + public org.hl7.fhir.r4b.model.Resource fetchResourceById(String theType, String theUri) { + throw new UnsupportedOperationException(Msg.code(226)); + } + + @Override + public boolean hasResource(Class theClass_, String theUri) { + throw new UnsupportedOperationException(Msg.code(227)); + } + + @Override + public void cacheResource(org.hl7.fhir.r4b.model.Resource theRes) throws FHIRException { + throw new UnsupportedOperationException(Msg.code(228)); + } + + @Override + public void cacheResourceFromPackage(Resource res, PackageVersion packageDetails) throws FHIRException { + throw new UnsupportedOperationException(Msg.code(229)); + } + + @Override + public void cachePackage(PackageDetails packageDetails, List list) { + + } + + @Override + public Set getResourceNamesAsSet() { + return myCtx.getResourceTypes(); + } + + @Override + public ValueSetExpander.ValueSetExpansionOutcome expandVS(ElementDefinitionBindingComponent theBinding, boolean theCacheOk, boolean theHierarchical) throws FHIRException { + throw new UnsupportedOperationException(Msg.code(230)); + } + + + @Override + public String getLinkForUrl(String corePath, String url) { + throw new UnsupportedOperationException(Msg.code(231)); + } + + @Override + public Map getBinaries() { + throw new UnsupportedOperationException(Msg.code(232)); + } + + @Override + public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws FHIRException { + throw new UnsupportedOperationException(Msg.code(233)); + } + + @Override + public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, String[] types) throws FHIRException { + throw new UnsupportedOperationException(Msg.code(234)); + } + + @Override + public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) throws FHIRException { + throw new UnsupportedOperationException(Msg.code(235)); + } + + @Override + public boolean hasPackage(String id, String ver) { + throw new UnsupportedOperationException(Msg.code(236)); + } + + @Override + public boolean hasPackage(PackageVersion packageVersion) { + return false; + } + + @Override + public PackageDetails getPackage(PackageVersion packageVersion) { + return null; + } + + @Override + public int getClientRetryCount() { + throw new UnsupportedOperationException(Msg.code(237)); + } + + @Override + public IWorkerContext setClientRetryCount(int value) { + throw new UnsupportedOperationException(Msg.code(238)); + } + + @Override + public TimeTracker clock() { + return null; + } + + @Override + public PackageVersion getPackageForUrl(String s) { + return null; + } + + public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) { + ConceptValidationOptions retVal = new ConceptValidationOptions(); + if (theOptions.isGuessSystem()) { + retVal = retVal.setInferSystem(true); + } + return retVal; + } + +} diff --git a/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/fhirpath/FhirPathR4B.java b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/fhirpath/FhirPathR4B.java new file mode 100644 index 00000000000..455c4d51ef1 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/fhirpath/FhirPathR4B.java @@ -0,0 +1,56 @@ +package org.hl7.fhir.r4b.hapi.fhirpath; + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.fhirpath.FhirPathExecutionException; +import ca.uhn.fhir.fhirpath.IFhirPath; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext; +import org.hl7.fhir.r4b.model.Base; +import org.hl7.fhir.r4b.utils.FHIRPathEngine; + +import java.util.List; +import java.util.Optional; + +public class FhirPathR4B implements IFhirPath { + + private FHIRPathEngine myEngine; + + public FhirPathR4B(FhirContext theCtx) { + IValidationSupport validationSupport = theCtx.getValidationSupport(); + myEngine = new FHIRPathEngine(new HapiWorkerContext(theCtx, validationSupport)); + } + + @SuppressWarnings("unchecked") + @Override + public List evaluate(IBase theInput, String thePath, Class theReturnType) { + List result; + try { + result = myEngine.evaluate((Base) theInput, thePath); + } catch (FHIRException e) { + throw new FhirPathExecutionException(Msg.code(198) + e); + } + + for (Base next : result) { + if (!theReturnType.isAssignableFrom(next.getClass())) { + throw new FhirPathExecutionException(Msg.code(199) + "FluentPath expression \"" + thePath + "\" returned unexpected type " + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName()); + } + } + + return (List) result; + } + + @Override + public Optional evaluateFirst(IBase theInput, String thePath, Class theReturnType) { + return evaluate(theInput, thePath, theReturnType).stream().findFirst(); + } + + @Override + public void parse(String theExpression) { + myEngine.parse(theExpression); + } + + +} diff --git a/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/rest/server/R4BBundleFactory.java b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/rest/server/R4BBundleFactory.java new file mode 100644 index 00000000000..fc1ee804e61 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/rest/server/R4BBundleFactory.java @@ -0,0 +1,289 @@ +package org.hl7.fhir.r4b.hapi.rest.server; + +/* + * #%L + * HAPI FHIR Structures - DSTU2 (FHIR v1.0.0) + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.api.BundleInclusionRule; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; +import ca.uhn.fhir.model.valueset.BundleTypeEnum; +import ca.uhn.fhir.rest.api.BundleLinks; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; +import ca.uhn.fhir.rest.server.RestfulServerUtils; +import ca.uhn.fhir.util.ResourceReferenceInfo; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4b.model.Bundle.BundleLinkComponent; +import org.hl7.fhir.r4b.model.Bundle.SearchEntryMode; +import org.hl7.fhir.r4b.model.DomainResource; +import org.hl7.fhir.r4b.model.IdType; +import org.hl7.fhir.r4b.model.Resource; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +@SuppressWarnings("Duplicates") +public class R4BBundleFactory implements IVersionSpecificBundleFactory { + private String myBase; + private Bundle myBundle; + private FhirContext myContext; + + public R4BBundleFactory(FhirContext theContext) { + myContext = theContext; + } + + @Override + public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { + ensureBundle(); + + List includedResources = new ArrayList(); + Set addedResourceIds = new HashSet(); + + for (IBaseResource next : theResult) { + if (next.getIdElement().isEmpty() == false) { + addedResourceIds.add(next.getIdElement()); + } + } + + for (IBaseResource next : theResult) { + + Set containedIds = new HashSet(); + + if (next instanceof DomainResource) { + for (Resource nextContained : ((DomainResource) next).getContained()) { + if (isNotBlank(nextContained.getId())) { + containedIds.add(nextContained.getId()); + } + } + } + + List references = myContext.newTerser().getAllResourceReferences(next); + do { + List addedResourcesThisPass = new ArrayList(); + + for (ResourceReferenceInfo nextRefInfo : references) { + if (theBundleInclusionRule != null && !theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { + continue; + } + + IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource(); + if (nextRes != null) { + if (nextRes.getIdElement().hasIdPart()) { + if (containedIds.contains(nextRes.getIdElement().getValue())) { + // Don't add contained IDs as top level resources + continue; + } + + IIdType id = nextRes.getIdElement(); + if (id.hasResourceType() == false) { + String resName = myContext.getResourceType(nextRes); + id = id.withResourceType(resName); + } + + if (!addedResourceIds.contains(id)) { + addedResourceIds.add(id); + addedResourcesThisPass.add(nextRes); + } + + } + } + } + + includedResources.addAll(addedResourcesThisPass); + + // Linked resources may themselves have linked resources + references = new ArrayList<>(); + for (IAnyResource iResource : addedResourcesThisPass) { + List newReferences = myContext.newTerser().getAllResourceReferences(iResource); + references.addAll(newReferences); + } + } while (references.isEmpty() == false); + + BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); + Resource nextAsResource = (Resource) next; + IIdType id = populateBundleEntryFullUrl(next, entry); + + // Populate Request + String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource); + if (httpVerb != null) { + entry.getRequest().getMethodElement().setValueAsString(httpVerb); + if (id != null) { + entry.getRequest().setUrl(id.getValue()); + } + } + if ("DELETE".equals(httpVerb)) { + entry.setResource(null); + } + + // Populate Bundle.entry.response + if (theBundleType != null) { + switch (theBundleType) { + case BATCH_RESPONSE: + case TRANSACTION_RESPONSE: + case HISTORY: + if ("1".equals(id.getVersionIdPart())) { + entry.getResponse().setStatus("201 Created"); + } else if (isNotBlank(id.getVersionIdPart())) { + entry.getResponse().setStatus("200 OK"); + } + if (isNotBlank(id.getVersionIdPart())) { + entry.getResponse().setEtag(RestfulServerUtils.createEtag(id.getVersionIdPart())); + } + break; + } + } + + // Populate Bundle.entry.search + String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource); + if (searchMode != null) { + entry.getSearch().getModeElement().setValueAsString(searchMode); + } + } + + /* + * Actually add the resources to the bundle + */ + for (IAnyResource next : includedResources) { + BundleEntryComponent entry = myBundle.addEntry(); + entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); + populateBundleEntryFullUrl(next, entry); + } + + } + + @Override + public void addRootPropertiesToBundle(String theId, @Nonnull BundleLinks theBundleLinks, Integer theTotalResults, + IPrimitiveType theLastUpdated) { + ensureBundle(); + + myBase = theBundleLinks.serverBase; + + if (myBundle.getIdElement().isEmpty()) { + myBundle.setId(theId); + } + + if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) { + myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString()); + } + + if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theBundleLinks.getSelf())) { + myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theBundleLinks.getSelf()); + } + if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theBundleLinks.getNext())) { + myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theBundleLinks.getNext()); + } + if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theBundleLinks.getPrev())) { + myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theBundleLinks.getPrev()); + } + + addTotalResultsToBundle(theTotalResults, theBundleLinks.bundleType); + } + + @Override + public void addTotalResultsToBundle(Integer theTotalResults, BundleTypeEnum theBundleType) { + ensureBundle(); + + if (myBundle.getIdElement().isEmpty()) { + myBundle.setId(UUID.randomUUID().toString()); + } + + if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { + myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); + } + + if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) { + myBundle.getTotalElement().setValue(theTotalResults); + } + } + + private void ensureBundle() { + if (myBundle == null) { + myBundle = new Bundle(); + } + } + + @Override + public IBaseResource getResourceBundle() { + return myBundle; + } + + private boolean hasLink(String theLinkType, Bundle theBundle) { + for (BundleLinkComponent next : theBundle.getLink()) { + if (theLinkType.equals(next.getRelation())) { + return true; + } + } + return false; + } + + + @Override + public void initializeWithBundleResource(IBaseResource theBundle) { + myBundle = (Bundle) theBundle; + } + + private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) { + IIdType idElement = null; + if (next.getIdElement().hasBaseUrl()) { + idElement = next.getIdElement(); + entry.setFullUrl(idElement.toVersionless().getValue()); + } else { + if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) { + idElement = next.getIdElement(); + idElement = idElement.withServerBase(myBase, myContext.getResourceType(next)); + entry.setFullUrl(idElement.toVersionless().getValue()); + } + } + return idElement; + } + + @Override + public List toListOfResources() { + ArrayList retVal = new ArrayList(); + for (BundleEntryComponent next : myBundle.getEntry()) { + if (next.getResource() != null) { + retVal.add(next.getResource()); + } else if (next.getResponse().getLocationElement().isEmpty() == false) { + IdType id = new IdType(next.getResponse().getLocation()); + String resourceType = id.getResourceType(); + if (isNotBlank(resourceType)) { + IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance(); + res.setId(id); + retVal.add(res); + } + } + } + return retVal; + } + +} diff --git a/hapi-fhir-structures-r4b/src/main/resources/org/hl7/fhir/r4b/model/fhirversion.properties b/hapi-fhir-structures-r4b/src/main/resources/org/hl7/fhir/r4b/model/fhirversion.properties new file mode 100644 index 00000000000..12e22760df7 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/main/resources/org/hl7/fhir/r4b/model/fhirversion.properties @@ -0,0 +1,215 @@ +# This file contains version definitions +# Generated: 2022-06-21T20:29:17.789-04:00 + +resource.Account=org.hl7.fhir.r4b.model.Account +resource.ActivityDefinition=org.hl7.fhir.r4b.model.ActivityDefinition +resource.AdministrableProductDefinition=org.hl7.fhir.r4b.model.AdministrableProductDefinition +resource.AdverseEvent=org.hl7.fhir.r4b.model.AdverseEvent +resource.AllergyIntolerance=org.hl7.fhir.r4b.model.AllergyIntolerance +resource.Appointment=org.hl7.fhir.r4b.model.Appointment +resource.AppointmentResponse=org.hl7.fhir.r4b.model.AppointmentResponse +resource.AuditEvent=org.hl7.fhir.r4b.model.AuditEvent +resource.Basic=org.hl7.fhir.r4b.model.Basic +resource.Binary=org.hl7.fhir.r4b.model.Binary +resource.BiologicallyDerivedProduct=org.hl7.fhir.r4b.model.BiologicallyDerivedProduct +resource.BodyStructure=org.hl7.fhir.r4b.model.BodyStructure +resource.Bundle=org.hl7.fhir.r4b.model.Bundle +resource.CapabilityStatement=org.hl7.fhir.r4b.model.CapabilityStatement +resource.CarePlan=org.hl7.fhir.r4b.model.CarePlan +resource.CareTeam=org.hl7.fhir.r4b.model.CareTeam +resource.CatalogEntry=org.hl7.fhir.r4b.model.CatalogEntry +resource.ChargeItem=org.hl7.fhir.r4b.model.ChargeItem +resource.ChargeItemDefinition=org.hl7.fhir.r4b.model.ChargeItemDefinition +resource.Citation=org.hl7.fhir.r4b.model.Citation +resource.Claim=org.hl7.fhir.r4b.model.Claim +resource.ClaimResponse=org.hl7.fhir.r4b.model.ClaimResponse +resource.ClinicalImpression=org.hl7.fhir.r4b.model.ClinicalImpression +resource.ClinicalUseDefinition=org.hl7.fhir.r4b.model.ClinicalUseDefinition +resource.CodeSystem=org.hl7.fhir.r4b.model.CodeSystem +resource.Communication=org.hl7.fhir.r4b.model.Communication +resource.CommunicationRequest=org.hl7.fhir.r4b.model.CommunicationRequest +resource.CompartmentDefinition=org.hl7.fhir.r4b.model.CompartmentDefinition +resource.Composition=org.hl7.fhir.r4b.model.Composition +resource.ConceptMap=org.hl7.fhir.r4b.model.ConceptMap +resource.Condition=org.hl7.fhir.r4b.model.Condition +resource.Consent=org.hl7.fhir.r4b.model.Consent +resource.Contract=org.hl7.fhir.r4b.model.Contract +resource.Coverage=org.hl7.fhir.r4b.model.Coverage +resource.CoverageEligibilityRequest=org.hl7.fhir.r4b.model.CoverageEligibilityRequest +resource.CoverageEligibilityResponse=org.hl7.fhir.r4b.model.CoverageEligibilityResponse +resource.DetectedIssue=org.hl7.fhir.r4b.model.DetectedIssue +resource.Device=org.hl7.fhir.r4b.model.Device +resource.DeviceDefinition=org.hl7.fhir.r4b.model.DeviceDefinition +resource.DeviceMetric=org.hl7.fhir.r4b.model.DeviceMetric +resource.DeviceRequest=org.hl7.fhir.r4b.model.DeviceRequest +resource.DeviceUseStatement=org.hl7.fhir.r4b.model.DeviceUseStatement +resource.DiagnosticReport=org.hl7.fhir.r4b.model.DiagnosticReport +resource.DocumentManifest=org.hl7.fhir.r4b.model.DocumentManifest +resource.DocumentReference=org.hl7.fhir.r4b.model.DocumentReference +resource.Encounter=org.hl7.fhir.r4b.model.Encounter +resource.Endpoint=org.hl7.fhir.r4b.model.Endpoint +resource.EnrollmentRequest=org.hl7.fhir.r4b.model.EnrollmentRequest +resource.EnrollmentResponse=org.hl7.fhir.r4b.model.EnrollmentResponse +resource.EpisodeOfCare=org.hl7.fhir.r4b.model.EpisodeOfCare +resource.EventDefinition=org.hl7.fhir.r4b.model.EventDefinition +resource.Evidence=org.hl7.fhir.r4b.model.Evidence +resource.EvidenceReport=org.hl7.fhir.r4b.model.EvidenceReport +resource.EvidenceVariable=org.hl7.fhir.r4b.model.EvidenceVariable +resource.ExampleScenario=org.hl7.fhir.r4b.model.ExampleScenario +resource.ExplanationOfBenefit=org.hl7.fhir.r4b.model.ExplanationOfBenefit +resource.FamilyMemberHistory=org.hl7.fhir.r4b.model.FamilyMemberHistory +resource.Flag=org.hl7.fhir.r4b.model.Flag +resource.Goal=org.hl7.fhir.r4b.model.Goal +resource.GraphDefinition=org.hl7.fhir.r4b.model.GraphDefinition +resource.Group=org.hl7.fhir.r4b.model.Group +resource.GuidanceResponse=org.hl7.fhir.r4b.model.GuidanceResponse +resource.HealthcareService=org.hl7.fhir.r4b.model.HealthcareService +resource.ImagingStudy=org.hl7.fhir.r4b.model.ImagingStudy +resource.Immunization=org.hl7.fhir.r4b.model.Immunization +resource.ImmunizationEvaluation=org.hl7.fhir.r4b.model.ImmunizationEvaluation +resource.ImmunizationRecommendation=org.hl7.fhir.r4b.model.ImmunizationRecommendation +resource.ImplementationGuide=org.hl7.fhir.r4b.model.ImplementationGuide +resource.Ingredient=org.hl7.fhir.r4b.model.Ingredient +resource.InsurancePlan=org.hl7.fhir.r4b.model.InsurancePlan +resource.Invoice=org.hl7.fhir.r4b.model.Invoice +resource.Library=org.hl7.fhir.r4b.model.Library +resource.Linkage=org.hl7.fhir.r4b.model.Linkage +resource.List=org.hl7.fhir.r4b.model.ListResource +resource.Location=org.hl7.fhir.r4b.model.Location +resource.ManufacturedItemDefinition=org.hl7.fhir.r4b.model.ManufacturedItemDefinition +resource.Measure=org.hl7.fhir.r4b.model.Measure +resource.MeasureReport=org.hl7.fhir.r4b.model.MeasureReport +resource.Media=org.hl7.fhir.r4b.model.Media +resource.Medication=org.hl7.fhir.r4b.model.Medication +resource.MedicationAdministration=org.hl7.fhir.r4b.model.MedicationAdministration +resource.MedicationDispense=org.hl7.fhir.r4b.model.MedicationDispense +resource.MedicationKnowledge=org.hl7.fhir.r4b.model.MedicationKnowledge +resource.MedicationRequest=org.hl7.fhir.r4b.model.MedicationRequest +resource.MedicationStatement=org.hl7.fhir.r4b.model.MedicationStatement +resource.MedicinalProductDefinition=org.hl7.fhir.r4b.model.MedicinalProductDefinition +resource.MessageDefinition=org.hl7.fhir.r4b.model.MessageDefinition +resource.MessageHeader=org.hl7.fhir.r4b.model.MessageHeader +resource.MolecularSequence=org.hl7.fhir.r4b.model.MolecularSequence +resource.NamingSystem=org.hl7.fhir.r4b.model.NamingSystem +resource.NutritionOrder=org.hl7.fhir.r4b.model.NutritionOrder +resource.NutritionProduct=org.hl7.fhir.r4b.model.NutritionProduct +resource.Observation=org.hl7.fhir.r4b.model.Observation +resource.ObservationDefinition=org.hl7.fhir.r4b.model.ObservationDefinition +resource.OperationDefinition=org.hl7.fhir.r4b.model.OperationDefinition +resource.OperationOutcome=org.hl7.fhir.r4b.model.OperationOutcome +resource.Organization=org.hl7.fhir.r4b.model.Organization +resource.OrganizationAffiliation=org.hl7.fhir.r4b.model.OrganizationAffiliation +resource.PackagedProductDefinition=org.hl7.fhir.r4b.model.PackagedProductDefinition +resource.Parameters=org.hl7.fhir.r4b.model.Parameters +resource.Patient=org.hl7.fhir.r4b.model.Patient +resource.PaymentNotice=org.hl7.fhir.r4b.model.PaymentNotice +resource.PaymentReconciliation=org.hl7.fhir.r4b.model.PaymentReconciliation +resource.Person=org.hl7.fhir.r4b.model.Person +resource.PlanDefinition=org.hl7.fhir.r4b.model.PlanDefinition +resource.Practitioner=org.hl7.fhir.r4b.model.Practitioner +resource.PractitionerRole=org.hl7.fhir.r4b.model.PractitionerRole +resource.Procedure=org.hl7.fhir.r4b.model.Procedure +resource.Provenance=org.hl7.fhir.r4b.model.Provenance +resource.Questionnaire=org.hl7.fhir.r4b.model.Questionnaire +resource.QuestionnaireResponse=org.hl7.fhir.r4b.model.QuestionnaireResponse +resource.RegulatedAuthorization=org.hl7.fhir.r4b.model.RegulatedAuthorization +resource.RelatedPerson=org.hl7.fhir.r4b.model.RelatedPerson +resource.RequestGroup=org.hl7.fhir.r4b.model.RequestGroup +resource.ResearchDefinition=org.hl7.fhir.r4b.model.ResearchDefinition +resource.ResearchElementDefinition=org.hl7.fhir.r4b.model.ResearchElementDefinition +resource.ResearchStudy=org.hl7.fhir.r4b.model.ResearchStudy +resource.ResearchSubject=org.hl7.fhir.r4b.model.ResearchSubject +resource.RiskAssessment=org.hl7.fhir.r4b.model.RiskAssessment +resource.Schedule=org.hl7.fhir.r4b.model.Schedule +resource.SearchParameter=org.hl7.fhir.r4b.model.SearchParameter +resource.ServiceRequest=org.hl7.fhir.r4b.model.ServiceRequest +resource.Slot=org.hl7.fhir.r4b.model.Slot +resource.Specimen=org.hl7.fhir.r4b.model.Specimen +resource.SpecimenDefinition=org.hl7.fhir.r4b.model.SpecimenDefinition +resource.StructureDefinition=org.hl7.fhir.r4b.model.StructureDefinition +resource.StructureMap=org.hl7.fhir.r4b.model.StructureMap +resource.Subscription=org.hl7.fhir.r4b.model.Subscription +resource.SubscriptionStatus=org.hl7.fhir.r4b.model.SubscriptionStatus +resource.SubscriptionTopic=org.hl7.fhir.r4b.model.SubscriptionTopic +resource.Substance=org.hl7.fhir.r4b.model.Substance +resource.SubstanceDefinition=org.hl7.fhir.r4b.model.SubstanceDefinition +resource.SupplyDelivery=org.hl7.fhir.r4b.model.SupplyDelivery +resource.SupplyRequest=org.hl7.fhir.r4b.model.SupplyRequest +resource.Task=org.hl7.fhir.r4b.model.Task +resource.TerminologyCapabilities=org.hl7.fhir.r4b.model.TerminologyCapabilities +resource.TestReport=org.hl7.fhir.r4b.model.TestReport +resource.TestScript=org.hl7.fhir.r4b.model.TestScript +resource.ValueSet=org.hl7.fhir.r4b.model.ValueSet +resource.VerificationResult=org.hl7.fhir.r4b.model.VerificationResult +resource.VisionPrescription=org.hl7.fhir.r4b.model.VisionPrescription + +datatype.Address=org.hl7.fhir.r4b.model.Address +datatype.Age=org.hl7.fhir.r4b.model.Age +datatype.Annotation=org.hl7.fhir.r4b.model.Annotation +datatype.Attachment=org.hl7.fhir.r4b.model.Attachment +datatype.BackboneElement=org.hl7.fhir.r4b.model.BackboneElement +datatype.BackboneType=org.hl7.fhir.r4b.model.BackboneType +datatype.CodeableConcept=org.hl7.fhir.r4b.model.CodeableConcept +datatype.CodeableReference=org.hl7.fhir.r4b.model.CodeableReference +datatype.Coding=org.hl7.fhir.r4b.model.Coding +datatype.ContactDetail=org.hl7.fhir.r4b.model.ContactDetail +datatype.ContactPoint=org.hl7.fhir.r4b.model.ContactPoint +datatype.Contributor=org.hl7.fhir.r4b.model.Contributor +datatype.Count=org.hl7.fhir.r4b.model.Count +datatype.DataRequirement=org.hl7.fhir.r4b.model.DataRequirement +datatype.DataType=org.hl7.fhir.r4b.model.DataType +datatype.Distance=org.hl7.fhir.r4b.model.Distance +datatype.Dosage=org.hl7.fhir.r4b.model.Dosage +datatype.Duration=org.hl7.fhir.r4b.model.Duration +datatype.ElementDefinition=org.hl7.fhir.r4b.model.ElementDefinition +datatype.Expression=org.hl7.fhir.r4b.model.Expression +datatype.Extension=org.hl7.fhir.r4b.model.Extension +datatype.HumanName=org.hl7.fhir.r4b.model.HumanName +datatype.Identifier=org.hl7.fhir.r4b.model.Identifier +datatype.MarketingStatus=org.hl7.fhir.r4b.model.MarketingStatus +datatype.Meta=org.hl7.fhir.r4b.model.Meta +datatype.Money=org.hl7.fhir.r4b.model.Money +datatype.MoneyQuantity=org.hl7.fhir.r4b.model.MoneyQuantity +datatype.Narrative=org.hl7.fhir.r4b.model.Narrative +datatype.OrderedDistribution=org.hl7.fhir.r4b.model.OrderedDistribution +datatype.ParameterDefinition=org.hl7.fhir.r4b.model.ParameterDefinition +datatype.Period=org.hl7.fhir.r4b.model.Period +datatype.Population=org.hl7.fhir.r4b.model.Population +datatype.ProdCharacteristic=org.hl7.fhir.r4b.model.ProdCharacteristic +datatype.ProductShelfLife=org.hl7.fhir.r4b.model.ProductShelfLife +datatype.Quantity=org.hl7.fhir.r4b.model.Quantity +datatype.Range=org.hl7.fhir.r4b.model.Range +datatype.Ratio=org.hl7.fhir.r4b.model.Ratio +datatype.RatioRange=org.hl7.fhir.r4b.model.RatioRange +datatype.Reference=org.hl7.fhir.r4b.model.Reference +datatype.RelatedArtifact=org.hl7.fhir.r4b.model.RelatedArtifact +datatype.SampledData=org.hl7.fhir.r4b.model.SampledData +datatype.Signature=org.hl7.fhir.r4b.model.Signature +datatype.SimpleQuantity=org.hl7.fhir.r4b.model.SimpleQuantity +datatype.Statistic=org.hl7.fhir.r4b.model.Statistic +datatype.SubstanceAmount=org.hl7.fhir.r4b.model.SubstanceAmount +datatype.Timing=org.hl7.fhir.r4b.model.Timing +datatype.TriggerDefinition=org.hl7.fhir.r4b.model.TriggerDefinition +datatype.UsageContext=org.hl7.fhir.r4b.model.UsageContext +datatype.base64Binary=org.hl7.fhir.r4b.model.Base64BinaryType +datatype.boolean=org.hl7.fhir.r4b.model.BooleanType +datatype.canonical=org.hl7.fhir.r4b.model.CanonicalType +datatype.code=org.hl7.fhir.r4b.model.CodeType +datatype.code.2=org.hl7.fhir.r4b.model.Enumeration +datatype.date=org.hl7.fhir.r4b.model.DateType +datatype.dateTime=org.hl7.fhir.r4b.model.DateTimeType +datatype.decimal=org.hl7.fhir.r4b.model.DecimalType +datatype.id=org.hl7.fhir.r4b.model.IdType +datatype.instant=org.hl7.fhir.r4b.model.InstantType +datatype.integer=org.hl7.fhir.r4b.model.IntegerType +datatype.integer64=org.hl7.fhir.r4b.model.Integer64Type +datatype.markdown=org.hl7.fhir.r4b.model.MarkdownType +datatype.oid=org.hl7.fhir.r4b.model.OidType +datatype.positiveInt=org.hl7.fhir.r4b.model.PositiveIntType +datatype.string=org.hl7.fhir.r4b.model.StringType +datatype.time=org.hl7.fhir.r4b.model.TimeType +datatype.unsignedInt=org.hl7.fhir.r4b.model.UnsignedIntType +datatype.uri=org.hl7.fhir.r4b.model.UriType +datatype.url=org.hl7.fhir.r4b.model.UrlType +datatype.uuid=org.hl7.fhir.r4b.model.UuidType +datatype.xhtml=org.hl7.fhir.utilities.xhtml.XhtmlNode diff --git a/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/client/GenericClientR4BTest.java b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/client/GenericClientR4BTest.java new file mode 100644 index 00000000000..aac112a2ed8 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/client/GenericClientR4BTest.java @@ -0,0 +1,148 @@ +package ca.uhn.fhir.rest.client; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.PreferReturnEnum; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.impl.BaseClient; +import ca.uhn.fhir.util.TestUtil; +import org.apache.commons.io.input.ReaderInputStream; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicStatusLine; +import org.hamcrest.core.StringContains; +import org.hl7.fhir.r4b.model.IdType; +import org.hl7.fhir.r4b.model.InstantType; +import org.hl7.fhir.r4b.model.OperationOutcome; +import org.hl7.fhir.r4b.model.Patient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.StringReader; +import java.nio.charset.StandardCharsets; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class GenericClientR4BTest { + + private static final FhirContext ourCtx = FhirContext.forR4BCached(); + protected int myAnswerCount; + protected HttpClient myHttpClient; + protected HttpResponse myHttpResponse; + + @BeforeEach + public void before() { + myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs()); + ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs()); + myAnswerCount = 0; + System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true"); + } + + @Test + public void testCreateWithPreferRepresentationServerReturnsOO() throws Exception { + final IParser p = ourCtx.newXmlParser(); + + final OperationOutcome resp0 = new OperationOutcome(); + resp0.getText().setDivAsString("OK!"); + + final Patient resp1 = new Patient(); + resp1.getText().setDivAsString("FINAL VALUE"); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() { + @Override + public Header[] answer(InvocationOnMock theInvocation) { + return new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3")}; + } + }); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public ReaderInputStream answer(InvocationOnMock theInvocation) { + if (myAnswerCount++ == 0) { + return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), StandardCharsets.UTF_8); + } else { + return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), StandardCharsets.UTF_8); + } + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + Patient pt = new Patient(); + pt.getText().setDivAsString("A PATIENT"); + + MethodOutcome outcome = client.create().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute(); + + assertEquals(2, myAnswerCount); + assertNotNull(outcome.getOperationOutcome()); + assertNotNull(outcome.getResource()); + + assertEquals("
OK!
", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString()); + assertEquals("
FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString()); + + assertEquals(myAnswerCount, capt.getAllValues().size()); + assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString()); + assertEquals(Constants.CT_FHIR_JSON_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", "")); + + assertEquals("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString()); + } + + @Test + public void testRead() throws Exception { + + String msg = getResourceResult(); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8)); + Header[] headers = new Header[]{new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"), new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),}; + when(myHttpResponse.getAllHeaders()).thenReturn(headers); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + Patient response = client.read().resource(Patient.class).withId(new IdType("Patient/1234")).execute(); + + assertThat(response.getNameFirstRep().getFamily(), StringContains.containsString("Cardinal")); + + assertEquals("http://foo.com/Patient/123/_history/2333", response.getIdElement().getValue()); + + InstantType lm = response.getMeta().getLastUpdatedElement(); + lm.setTimeZoneZulu(true); + assertEquals("1995-11-15T04:58:08.000Z", lm.getValueAsString()); + + } + + private String getResourceResult() { + String msg = "" + "
John Cardinal: 444333333
" + "" + "" + "" + "" + "" + "
" + "
"; + return msg; + } + + @AfterAll + public static void afterClassClearContext() { + TestUtil.randomizeLocaleAndTimezone(); + } + +} diff --git a/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java new file mode 100644 index 00000000000..877583e6186 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java @@ -0,0 +1,179 @@ + +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.*; +import org.hl7.fhir.r4b.model.Organization; +import org.hl7.fhir.r4b.model.Patient; +import org.hl7.fhir.r4b.model.Practitioner; + +import java.util.Set; + +// import ca.uhn.fhir.model.dstu.resource.Binary; +// import ca.uhn.fhir.model.dstu2.resource.Bundle; +// import ca.uhn.fhir.model.api.Bundle; + + +public class PatientResourceProvider implements IResourceProvider + { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Search() + public IBundleProvider search( + javax.servlet.http.HttpServletRequest theServletRequest, + + @Description(shortDefinition="The resource identity") + @OptionalParam(name="_id") + StringAndListParam theId, + + @Description(shortDefinition="Search the contents of the resource's data using a fulltext search") + @OptionalParam(name=Constants.PARAM_CONTENT) + StringAndListParam theFtContent, + + @Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search") + @OptionalParam(name=Constants.PARAM_TEXT) + StringAndListParam theFtText, + + @Description(shortDefinition="Search for resources which have the given tag") + @OptionalParam(name=Constants.PARAM_TAG) + TokenAndListParam theSearchForTag, + + @Description(shortDefinition="Search for resources which have the given security labels") + @OptionalParam(name=Constants.PARAM_SECURITY) + TokenAndListParam theSearchForSecurity, + + @Description(shortDefinition="Search for resources which have the given profile") + @OptionalParam(name=Constants.PARAM_PROFILE) + UriAndListParam theSearchForProfile, + + + @Description(shortDefinition="A patient identifier") + @OptionalParam(name="identifier") + TokenAndListParam theIdentifier, + + @Description(shortDefinition="A portion of either family or given name of the patient") + @OptionalParam(name="name") + StringAndListParam theName, + + @Description(shortDefinition="A portion of the family name of the patient") + @OptionalParam(name="family") + StringAndListParam theFamily, + + @Description(shortDefinition="A portion of the given name of the patient") + @OptionalParam(name="given") + StringAndListParam theGiven, + + @Description(shortDefinition="A portion of either family or given name using some kind of phonetic matching algorithm") + @OptionalParam(name="phonetic") + StringAndListParam thePhonetic, + + @Description(shortDefinition="The value in any kind of telecom details of the patient") + @OptionalParam(name="telecom") + TokenAndListParam theTelecom, + + @Description(shortDefinition="A value in a phone contact") + @OptionalParam(name="phone") + TokenAndListParam thePhone, + + @Description(shortDefinition="A value in an email contact") + @OptionalParam(name="email") + TokenAndListParam theEmail, + + @Description(shortDefinition="An address in any kind of address/part of the patient") + @OptionalParam(name="address") + StringAndListParam theAddress, + + @Description(shortDefinition="A city specified in an address") + @OptionalParam(name="address-city") + StringAndListParam theAddress_city, + + @Description(shortDefinition="A state specified in an address") + @OptionalParam(name="address-state") + StringAndListParam theAddress_state, + + @Description(shortDefinition="A postalCode specified in an address") + @OptionalParam(name="address-postalcode") + StringAndListParam theAddress_postalcode, + + @Description(shortDefinition="A country specified in an address") + @OptionalParam(name="address-country") + StringAndListParam theAddress_country, + + @Description(shortDefinition="A use code specified in an address") + @OptionalParam(name="address-use") + TokenAndListParam theAddress_use, + + @Description(shortDefinition="Gender of the patient") + @OptionalParam(name="gender") + TokenAndListParam theGender, + + @Description(shortDefinition="Language code (irrespective of use value)") + @OptionalParam(name="language") + TokenAndListParam theLanguage, + + @Description(shortDefinition="The patient's date of birth") + @OptionalParam(name="birthdate") + DateRangeParam theBirthdate, + + @Description(shortDefinition="The organization at which this person is a patient") + @OptionalParam(name="organization", targetTypes={ Organization.class } ) + ReferenceAndListParam theOrganization, + + @Description(shortDefinition="Patient's nominated care provider, could be a care manager, not the organization that manages the record") + @OptionalParam(name="careprovider", targetTypes={ Organization.class , Practitioner.class } ) + ReferenceAndListParam theCareprovider, + + @Description(shortDefinition="Whether the patient record is active") + @OptionalParam(name="active") + TokenAndListParam theActive, + + @Description(shortDefinition="The species for animal patients") + @OptionalParam(name="animal-species") + TokenAndListParam theAnimal_species, + + @Description(shortDefinition="The breed for animal patients") + @OptionalParam(name="animal-breed") + TokenAndListParam theAnimal_breed, + + @Description(shortDefinition="All patients linked to the given patient") + @OptionalParam(name="link", targetTypes={ Patient.class } ) + ReferenceAndListParam theLink, + + @Description(shortDefinition="This patient has been marked as deceased, or as a death date entered") + @OptionalParam(name="deceased") + TokenAndListParam theDeceased, + + @Description(shortDefinition="The date of death has been provided and satisfies this search value") + @OptionalParam(name="deathdate") + DateRangeParam theDeathdate, + + @IncludeParam(reverse=true) + Set theRevIncludes, + @Description(shortDefinition="Only return resources which were last updated as specified by the given range") + @OptionalParam(name="_lastUpdated") + DateRangeParam theLastUpdated, + + @IncludeParam(allow= { + "Patient:careprovider" , "Patient:link" , "Patient:organization" , "Patient:careprovider" , "Patient:link" , "Patient:organization" , "Patient:careprovider" , "Patient:link" , "Patient:organization" , "*" + }) + Set theIncludes, + + @Sort + SortSpec theSort, + + @Count + Integer theCount + ) { + return null; + } + +} diff --git a/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/server/SearchR4BTest.java b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/server/SearchR4BTest.java new file mode 100644 index 00000000000..6ef1d042800 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/server/SearchR4BTest.java @@ -0,0 +1,144 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; +import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.ValidationResult; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4b.model.HumanName; +import org.hl7.fhir.r4b.model.Patient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class SearchR4BTest { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchR4BTest.class); + private static CloseableHttpClient ourClient; + private static FhirContext ourCtx = FhirContext.forR4BCached(); + private static TokenAndListParam ourIdentifiers; + private static String ourLastMethod; + private static int ourPort; + + private static Server ourServer; + + @BeforeEach + public void before() { + ourLastMethod = null; + ourIdentifiers = null; + } + + + @Test + public void testSearch() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_pretty=true"); + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { + String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info(responseContent); +// validate(ourCtx.newJsonParser().parseResource(responseContent)); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertEquals("search", ourLastMethod); + + assertEquals("foo", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getSystem()); + assertEquals("bar", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); + } + + } + + + private void validate(IBaseResource theResource) { + FhirValidator validatorModule = ourCtx.newValidator(); + ValidationResult result = validatorModule.validateWithResult(theResource); + if (!result.isSuccessful()) { + fail(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome())); + } + } + + + public static class DummyPatientResourceProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @SuppressWarnings("rawtypes") + @Search() + public List search( + @RequiredParam(name = "identifier") TokenAndListParam theIdentifiers) { + ourLastMethod = "search"; + ourIdentifiers = theIdentifiers; + ArrayList retVal = new ArrayList<>(); + + for (int i = 0; i < 200; i++) { + Patient patient = new Patient(); + patient.getIdElement().setValue("Patient/" + i + "/_history/222"); + ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(patient, BundleEntrySearchModeEnum.INCLUDE.getCode()); + patient.addName(new HumanName().setFamily("FAMILY")); + patient.setActive(true); + retVal.add(patient); + } + return retVal; + } + + } + + + @AfterAll + public static void afterClassClearContext() throws Exception { + JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); + } + + @BeforeAll + public static void beforeClass() throws Exception { + ourServer = new Server(0); + + DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); + + ServletHandler proxyHandler = new ServletHandler(); + RestfulServer servlet = new RestfulServer(ourCtx); + servlet.setDefaultResponseEncoding(EncodingEnum.JSON); + servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); + + servlet.setResourceProviders(patientProvider); + ServletHolder servletHolder = new ServletHolder(servlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + ourServer.setHandler(proxyHandler); + JettyUtil.startServer(ourServer); + ourPort = JettyUtil.getPortForStartedServer(ourServer); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + ourClient = builder.build(); + + } + +} diff --git a/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4BTest.java b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4BTest.java new file mode 100644 index 00000000000..56ff0caeaf3 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4BTest.java @@ -0,0 +1,1187 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.model.api.annotation.ResourceDef; +import ca.uhn.fhir.model.primitive.InstantDt; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.History; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.IncludeParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.OptionalParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceAndListParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.method.BaseMethodBinding; +import ca.uhn.fhir.rest.server.method.IParameter; +import ca.uhn.fhir.rest.server.method.SearchMethodBinding; +import ca.uhn.fhir.rest.server.method.SearchParameter; +import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.validation.ValidationResult; +import com.google.common.collect.Lists; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.CapabilityStatement; +import org.hl7.fhir.r4b.model.CapabilityStatement.CapabilityStatementRestComponent; +import org.hl7.fhir.r4b.model.CapabilityStatement.CapabilityStatementRestResourceComponent; +import org.hl7.fhir.r4b.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent; +import org.hl7.fhir.r4b.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; +import org.hl7.fhir.r4b.model.CapabilityStatement.ConditionalDeleteStatus; +import org.hl7.fhir.r4b.model.CapabilityStatement.SystemRestfulInteraction; +import org.hl7.fhir.r4b.model.CapabilityStatement.TypeRestfulInteraction; +import org.hl7.fhir.r4b.model.CodeType; +import org.hl7.fhir.r4b.model.DateType; +import org.hl7.fhir.r4b.model.DiagnosticReport; +import org.hl7.fhir.r4b.model.Encounter; +import org.hl7.fhir.r4b.model.Enumerations; +import org.hl7.fhir.r4b.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4b.model.IdType; +import org.hl7.fhir.r4b.model.OperationDefinition; +import org.hl7.fhir.r4b.model.OperationDefinition.OperationDefinitionParameterComponent; +import org.hl7.fhir.r4b.model.OperationDefinition.OperationKind; +import org.hl7.fhir.r4b.model.Patient; +import org.hl7.fhir.r4b.model.StringType; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ServerCapabilityStatementProviderR4BTest { + + private final FhirContext myCtx = FhirContext.forR4BCached(); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProviderR4BTest.class); + + private HttpServletRequest createHttpServletRequest() { + HttpServletRequest req = mock(HttpServletRequest.class); + when(req.getRequestURI()).thenReturn("/FhirStorm/fhir/Patient/_search"); + when(req.getServletPath()).thenReturn("/fhir"); + when(req.getRequestURL()).thenReturn(new StringBuffer().append("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir/Patient/_search")); + when(req.getContextPath()).thenReturn("/FhirStorm"); + return req; + } + + private ServletConfig createServletConfig() { + ServletConfig sc = mock(ServletConfig.class); + when(sc.getServletContext()).thenReturn(null); + return sc; + } + + private CapabilityStatement.CapabilityStatementRestResourceComponent findRestResource(CapabilityStatement conformance, String wantResource) throws Exception { + CapabilityStatement.CapabilityStatementRestResourceComponent resource = null; + for (CapabilityStatement.CapabilityStatementRestResourceComponent next : conformance.getRest().get(0).getResource()) { + if (next.getType().equals(wantResource)) { + resource = next; + } + } + if (resource == null) { + throw new Exception("Could not find resource: " + wantResource); + } + return resource; + } + + @Test + public void testConditionalOperations() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new ConditionalProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + CapabilityStatementRestResourceComponent res = conformance.getRest().get(0).getResource().get(1); + assertEquals("Patient", res.getType()); + + assertTrue(res.getConditionalCreate()); + assertEquals(ConditionalDeleteStatus.MULTIPLE, res.getConditionalDelete()); + assertTrue(res.getConditionalUpdate()); + } + + private RequestDetails createRequestDetails(RestfulServer theServer) { + ServletRequestDetails retVal = new ServletRequestDetails(); + retVal.setServer(theServer); + return retVal; + } + + @Test + public void testExtendedOperationReturningBundle() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new ProviderWithExtendedOperationReturningBundle()); + rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4")); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + List operations = conformance.getRestFirstRep().getResource().stream().filter(t->t.getType().equals("Patient")).findFirst().orElseThrow(()->new IllegalArgumentException()).getOperation(); + assertEquals(1, operations.size()); + assertEquals("everything", operations.get(0).getName()); + + OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs)); + validate(opDef); + assertEquals("everything", opDef.getCode()); + assertThat(opDef.getSystem(), is(false)); + assertThat(opDef.getType(), is(false)); + assertThat(opDef.getInstance(), is(true)); + } + + @Test + public void testExtendedOperationReturningBundleOperation() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new ProviderWithExtendedOperationReturningBundle()); + rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4")); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) { + }; + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs)); + validate(opDef); + + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef); + ourLog.info(conf); + + assertEquals("everything", opDef.getCode()); + assertEquals(false, opDef.getAffectsState()); + } + + @Test + public void testInstanceHistorySupported() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new InstanceHistoryProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance); + assertThat(conf, containsString("")); + } + + @Test + public void testFormatIncludesSpecialNonMediaTypeFormats() throws ServletException { + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new SearchProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + CapabilityStatement serverConformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + List formatCodes = serverConformance.getFormat().stream().map(c -> c.getCode()).collect(Collectors.toList()); + + assertThat(formatCodes, hasItem(Constants.FORMAT_XML)); + assertThat(formatCodes, hasItem(Constants.FORMAT_JSON)); + assertThat(formatCodes, hasItem(Constants.CT_FHIR_JSON_NEW)); + assertThat(formatCodes, hasItem(Constants.CT_FHIR_XML_NEW)); + } + + + @Test + public void testMultiOptionalDocumentation() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new MultiOptionalProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + boolean found = false; + Collection resourceBindings = rs.getResourceBindings(); + for (ResourceBinding resourceBinding : resourceBindings) { + if (resourceBinding.getResourceName().equals("Patient")) { + List methodBindings = resourceBinding.getMethodBindings(); + SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); + SearchParameter param = (SearchParameter) binding.getParameters().iterator().next(); + assertEquals("The patient's identifier", param.getDescription()); + found = true; + } + } + + assertTrue(found); + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + assertThat(conf, containsString("")); + assertThat(conf, containsString("")); + assertThat(conf, containsString("")); + } + + @Test + public void testNonConditionalOperations() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new NonConditionalProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + CapabilityStatementRestResourceComponent res = conformance.getRest().get(0).getResource().get(1); + assertEquals("Patient", res.getType()); + + assertNull(res.getConditionalCreateElement().getValue()); + assertNull(res.getConditionalDeleteElement().getValue()); + assertNull(res.getConditionalUpdateElement().getValue()); + } + + /** See #379 */ + @Test + public void testOperationAcrossMultipleTypes() throws Exception { + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new MultiTypePatientProvider(), new MultiTypeEncounterProvider()); + rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4")); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + List operations; + + operations = conformance.getRestFirstRep().getResource().stream().filter(t->t.getType().equals("Patient")).findFirst().orElseThrow(()->new IllegalArgumentException()).getOperation(); + assertEquals(2, operations.size()); + List operationNames = toOperationNames(operations); + assertThat(operationNames.toString(), operationNames, containsInAnyOrder("someOp", "validate")); + List operationIdParts = toOperationIdParts(operations); + assertThat(operationIdParts.toString(), operationIdParts, containsInAnyOrder("EncounterPatient-i-someOp", "EncounterPatient-i-validate")); + + { + OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/EncounterPatient-i-someOp"), createRequestDetails(rs)); + validate(opDef); + ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef)); + Set types = toStrings(opDef.getResource()); + assertEquals("someOp", opDef.getCode()); + assertEquals(true, opDef.getInstance()); + assertEquals(false, opDef.getSystem()); + assertThat(types, containsInAnyOrder("Patient", "Encounter")); + assertEquals(2, opDef.getParameter().size()); + assertEquals("someOpParam1", opDef.getParameter().get(0).getName()); + assertEquals("date", opDef.getParameter().get(0).getType().toCode()); + assertEquals("someOpParam2", opDef.getParameter().get(1).getName()); + assertEquals("Resource", opDef.getParameter().get(1).getType().toCode()); + } + { + OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType("OperationDefinition/EncounterPatient-i-validate"), createRequestDetails(rs)); + validate(opDef); + ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef)); + Set types = toStrings(opDef.getResource()); + assertEquals("validate", opDef.getCode()); + assertEquals(true, opDef.getInstance()); + assertEquals(false, opDef.getSystem()); + assertThat(types, containsInAnyOrder("Patient", "Encounter")); + assertEquals(1, opDef.getParameter().size()); + assertEquals("resource", opDef.getParameter().get(0).getName()); + assertEquals("Resource", opDef.getParameter().get(0).getType().toCode()); + } + } + + @Test + public void testOperationDocumentation() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new SearchProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + + assertThat(conf, containsString("")); + assertThat(conf, containsString("")); + + } + + @Test + public void testProviderWithRequiredAndOptional() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new ProviderWithRequiredAndOptional()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + CapabilityStatementRestComponent rest = conformance.getRest().get(0); + CapabilityStatementRestResourceComponent res = rest.getResource().get(0); + assertEquals("DiagnosticReport", res.getType()); + + assertEquals("subject.identifier", res.getSearchParam().get(0).getName()); + + assertEquals("code", res.getSearchParam().get(1).getName()); + + assertEquals("date", res.getSearchParam().get(2).getName()); + + assertEquals(1, res.getSearchInclude().size()); + assertEquals("DiagnosticReport.result", res.getSearchInclude().get(0).getValue()); + } + + @Test + public void testReadAndVReadSupported() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new VreadProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance); + assertThat(conf, containsString("")); + assertThat(conf, containsString("")); + } + + @Test + public void testReadSupported() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new ReadProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance); + assertThat(conf, not(containsString(""))); + assertThat(conf, containsString("")); + } + + @Test + public void testSearchParameterDocumentation() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new SearchProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + boolean found = false; + Collection resourceBindings = rs.getResourceBindings(); + for (ResourceBinding resourceBinding : resourceBindings) { + if (resourceBinding.getResourceName().equals("Patient")) { + List methodBindings = resourceBinding.getMethodBindings(); + SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); + for (IParameter next : binding.getParameters()) { + SearchParameter param = (SearchParameter) next; + if (param.getDescription().contains("The patient's identifier (MRN or other card number")) { + found = true; + } + } + found = true; + } + } + assertTrue(found); + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + assertThat(conf, containsString("")); + assertThat(conf, containsString("")); + + } + + /** + * See #286 + */ + @Test + public void testSearchReferenceParameterDocumentation() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new PatientResourceProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + boolean found = false; + Collection resourceBindings = rs.getResourceBindings(); + for (ResourceBinding resourceBinding : resourceBindings) { + if (resourceBinding.getResourceName().equals("Patient")) { + List methodBindings = resourceBinding.getMethodBindings(); + SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); + SearchParameter param = (SearchParameter) binding.getParameters().get(25); + assertEquals("careprovider", param.getName()); + assertEquals("Patient's nominated care provider, could be a care manager, not the organization that manages the record", param.getDescription()); + found = true; + } + } + assertTrue(found); + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + } + + /** + * See #286 + */ + @Test + public void testSearchReferenceParameterWithWhitelistDocumentation() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new SearchProviderWithWhitelist()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + boolean found = false; + Collection resourceBindings = rs.getResourceBindings(); + for (ResourceBinding resourceBinding : resourceBindings) { + if (resourceBinding.getResourceName().equals("Patient")) { + List methodBindings = resourceBinding.getMethodBindings(); + SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); + SearchParameter param = (SearchParameter) binding.getParameters().get(0); + assertEquals("The organization at which this person is a patient", param.getDescription()); + found = true; + } + } + assertTrue(found); + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + CapabilityStatementRestResourceComponent resource = findRestResource(conformance, "Patient"); + + CapabilityStatementRestResourceSearchParamComponent param = resource.getSearchParam().get(0); +// assertEquals("bar", param.getChain().get(0).getValue()); +// assertEquals("foo", param.getChain().get(1).getValue()); +// assertEquals(2, param.getChain().size()); + } + + @Test + public void testSearchReferenceParameterWithList() throws Exception { + + RestfulServer rsNoType = new RestfulServer(myCtx){ + @Override + public RestfulServerConfiguration createConfiguration() { + RestfulServerConfiguration retVal = super.createConfiguration(); + retVal.setConformanceDate(new InstantDt("2011-02-22T11:22:33Z")); + return retVal; + } + }; + rsNoType.registerProvider(new SearchProviderWithListNoType()); + ServerCapabilityStatementProvider scNoType = new ServerCapabilityStatementProvider(rsNoType); + rsNoType.setServerConformanceProvider(scNoType); + rsNoType.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) scNoType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsNoType)); + conformance.setId(""); + String confNoType = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(confNoType); + + RestfulServer rsWithType = new RestfulServer(myCtx){ + @Override + public RestfulServerConfiguration createConfiguration() { + RestfulServerConfiguration retVal = super.createConfiguration(); + retVal.setConformanceDate(new InstantDt("2011-02-22T11:22:33Z")); + return retVal; + } + }; + rsWithType.registerProvider(new SearchProviderWithListWithType()); + ServerCapabilityStatementProvider scWithType = new ServerCapabilityStatementProvider(rsWithType); + rsWithType.setServerConformanceProvider(scWithType); + rsWithType.init(createServletConfig()); + + CapabilityStatement conformanceWithType = (CapabilityStatement) scWithType.getServerConformance(createHttpServletRequest(), createRequestDetails(rsWithType)); + conformanceWithType.setId(""); + String confWithType = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformanceWithType); + ourLog.info(confWithType); + + assertEquals(confNoType, confWithType); + assertThat(confNoType, containsString("")); + } + + @Test + public void testSystemHistorySupported() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new SystemHistoryProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance); + assertThat(conf, containsString("")); + } + + @Test + public void testTypeHistorySupported() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new TypeHistoryProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + conf = myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(conformance); + assertThat(conf, containsString("")); + } + + @Test + @Disabled + public void testValidateGeneratedStatement() throws Exception { + + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new MultiOptionalProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance)); + + ValidationResult result = myCtx.newValidator().validateWithResult(conformance); + assertTrue(result.isSuccessful(), result.getMessages().toString()); + } + + @Test + public void testSystemLevelNamedQueryWithParameters() throws Exception { + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new NamedQueryPlainProvider()); + rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4")); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance)); + + CapabilityStatementRestComponent restComponent = conformance.getRest().get(0); + CapabilityStatementRestResourceOperationComponent operationComponent = restComponent.getOperation().get(0); + assertThat(operationComponent.getName(), is(NamedQueryPlainProvider.QUERY_NAME)); + + String operationReference = operationComponent.getDefinition(); + assertThat(operationReference, not(nullValue())); + + OperationDefinition operationDefinition = (OperationDefinition) sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs)); + ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition)); + validate(operationDefinition); + assertThat(operationDefinition.getCode(), is(NamedQueryPlainProvider.QUERY_NAME)); + assertThat("The operation name should be the description, if a description is set", operationDefinition.getName(), equalTo("TestQuery")); + assertThat(operationDefinition.getStatus(), is(PublicationStatus.ACTIVE)); + assertThat(operationDefinition.getKind(), is(OperationKind.QUERY)); + assertThat(operationDefinition.getDescription(), is(NamedQueryPlainProvider.DESCRIPTION)); + assertThat(operationDefinition.getAffectsState(), is(false)); + assertThat("A system level search has no target resources", operationDefinition.getResource(), is(empty())); + assertThat(operationDefinition.getSystem(), is(true)); + assertThat(operationDefinition.getType(), is(false)); + assertThat(operationDefinition.getInstance(), is(false)); + List parameters = operationDefinition.getParameter(); + assertThat(parameters.size(), is(1)); + OperationDefinitionParameterComponent param = parameters.get(0); + assertThat(param.getName(), is(NamedQueryPlainProvider.SP_QUANTITY)); + assertThat(param.getType().toCode(), is("string")); + assertThat(param.getSearchTypeElement().asStringValue(), is(RestSearchParameterTypeEnum.QUANTITY.getCode())); + assertThat(param.getMin(), is(1)); + assertThat(param.getMax(), is("1")); + assertThat(param.getUse(), is(Enumerations.OperationParameterUse.IN)); + } + + @Test + public void testResourceLevelNamedQueryWithParameters() throws Exception { + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new NamedQueryResourceProvider()); + rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4")); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance)); + + CapabilityStatementRestResourceComponent resource = conformance.getRestFirstRep().getResource().stream().filter(t->t.getType().equals("Patient")).findFirst().orElseThrow(()->new IllegalArgumentException()); + CapabilityStatementRestResourceOperationComponent operationComponent = resource.getOperation().get(0); + String operationReference = operationComponent.getDefinition(); + assertThat(operationReference, not(nullValue())); + + OperationDefinition operationDefinition = (OperationDefinition) sc.readOperationDefinition(new IdType(operationReference), createRequestDetails(rs)); + ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationDefinition)); + validate(operationDefinition); + assertThat("The operation name should be the code if no description is set", operationDefinition.getName(), equalTo("TestQuery")); + String patientResourceName = "Patient"; + assertThat("A resource level search targets the resource of the provider it's defined in", operationDefinition.getResource().get(0).getValue(), is(patientResourceName)); + assertThat(operationDefinition.getSystem(), is(false)); + assertThat(operationDefinition.getType(), is(true)); + assertThat(operationDefinition.getInstance(), is(false)); + List parameters = operationDefinition.getParameter(); + assertThat(parameters.size(), is(1)); + OperationDefinitionParameterComponent param = parameters.get(0); + assertThat(param.getName(), is(NamedQueryResourceProvider.SP_PARAM)); + assertThat(param.getType().toCode(), is("string")); + assertThat(param.getSearchTypeElement().asStringValue(), is(RestSearchParameterTypeEnum.STRING.getCode())); + assertThat(param.getMin(), is(0)); + assertThat(param.getMax(), is("1")); + assertThat(param.getUse(), is(Enumerations.OperationParameterUse.IN)); + + assertThat("Named query parameters should not appear in the resource search params", resource.getSearchParam(), is(empty())); + } + + @Test + public void testExtendedOperationAtTypeLevel() throws Exception { + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new TypeLevelOperationProvider()); + rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4")); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); + ourLog.info(conf); + + List operations = conformance.getRestFirstRep().getResource().stream().filter(t->t.getType().equals("Patient")).findFirst().orElseThrow(()->new IllegalArgumentException()).getOperation(); + assertThat(operations.size(), is(1)); + assertThat(operations.get(0).getName(), is(TypeLevelOperationProvider.OPERATION_NAME)); + + OperationDefinition opDef = (OperationDefinition) sc.readOperationDefinition(new IdType(operations.get(0).getDefinition()), createRequestDetails(rs)); + validate(opDef); + assertEquals(TypeLevelOperationProvider.OPERATION_NAME, opDef.getCode()); + assertThat(opDef.getSystem(), is(false)); + assertThat(opDef.getType(), is(true)); + assertThat(opDef.getInstance(), is(false)); + } + + @Test + public void testProfiledResourceStructureDefinitionLinks() throws Exception { + RestfulServer rs = new RestfulServer(myCtx); + rs.setResourceProviders(new ProfiledPatientProvider(), new MultipleProfilesPatientProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + ourLog.info(myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance)); + + List resources = conformance.getRestFirstRep().getResource(); + CapabilityStatementRestResourceComponent patientResource = resources.stream() + .filter(resource -> "Patient".equals(resource.getType())) + .findFirst().get(); + assertThat(patientResource.getProfile(), containsString(PATIENT_SUB)); + } + + private List toOperationIdParts(List theOperation) { + ArrayList retVal = Lists.newArrayList(); + for (CapabilityStatementRestResourceOperationComponent next : theOperation) { + retVal.add(new IdType(next.getDefinition()).getIdPart()); + } + return retVal; + } + + private List toOperationNames(List theOperation) { + ArrayList retVal = Lists.newArrayList(); + for (CapabilityStatementRestResourceOperationComponent next : theOperation) { + retVal.add(next.getName()); + } + return retVal; + } + + private Set toStrings(List theType) { + HashSet retVal = new HashSet(); + for (CodeType next : theType) { + retVal.add(next.getValueAsString()); + } + return retVal; + } + + private void validate(OperationDefinition theOpDef) { + String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(theOpDef); + ourLog.info("Def: {}", conf); + } + + @AfterAll + public static void afterClassClearContext() { + TestUtil.randomizeLocaleAndTimezone(); + } + + @SuppressWarnings("unused") + public static class ConditionalProvider implements IResourceProvider { + + @Create + public MethodOutcome create(@ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) { + return null; + } + + @Delete + public MethodOutcome delete(@IdParam IdType theId, @ConditionalUrlParam(supportsMultiple = true) String theConditionalUrl) { + return null; + } + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Update + public MethodOutcome update(@IdParam IdType theId, @ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class InstanceHistoryProvider implements IResourceProvider { + @Override + public Class getResourceType() { + return Patient.class; + } + + @History + public List history(@IdParam IdType theId) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class MultiOptionalProvider { + + @Search(type = Patient.class) + public Patient findPatient(@Description(shortDefinition = "The patient's identifier") @OptionalParam(name = "identifier") TokenParam theIdentifier, + @Description(shortDefinition = "The patient's name") @OptionalParam(name = "name") StringParam theName) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class MultiTypeEncounterProvider implements IResourceProvider { + + @Operation(name = "someOp") + public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId, + @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) { + return null; + } + + @Override + public Class getResourceType() { + return Encounter.class; + } + + @Validate + public IBundleProvider validate(HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Encounter thePatient) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class MultiTypePatientProvider implements IResourceProvider { + + @Operation(name = "someOp") + public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId, + @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Patient theEnd) { + return null; + } + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Validate + public IBundleProvider validate(HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Patient thePatient) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class NonConditionalProvider implements IResourceProvider { + + @Create + public MethodOutcome create(@ResourceParam Patient thePatient) { + return null; + } + + @Delete + public MethodOutcome delete(@IdParam IdType theId) { + return null; + } + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Update + public MethodOutcome update(@IdParam IdType theId, @ResourceParam Patient thePatient) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class PlainProviderWithExtendedOperationOnNoType { + + @Operation(name = "plain", idempotent = true, returnParameters = { @OperationParam(min = 1, max = 2, name = "out1", type = StringType.class) }) + public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, + @OperationParam(name = "end") DateType theEnd) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider { + + @Operation(name = "everything", idempotent = true) + public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, + @OperationParam(name = "end") DateType theEnd) { + return null; + } + + @Override + public Class getResourceType() { + return Patient.class; + } + + } + + @SuppressWarnings("unused") + public static class ProviderWithRequiredAndOptional { + + @Description(shortDefinition = "This is a search for stuff!") + @Search + public List findDiagnosticReportsByPatient(@RequiredParam(name = "subject" + '.' + "identifier") TokenParam thePatientId, + @OptionalParam(name = "code") TokenOrListParam theNames, @OptionalParam(name = "date") DateRangeParam theDateRange, + @IncludeParam(allow = { "DiagnosticReport.result" }) Set theIncludes) throws Exception { + return null; + } + + } + + @SuppressWarnings("unused") + public static class ReadProvider { + + @Search(type = Patient.class) + public Patient findPatient(@Description(shortDefinition = "The patient's identifier (MRN or other card number)") @RequiredParam(name = "identifier") TokenParam theIdentifier) { + return null; + } + + @Read(version = false) + public Patient readPatient(@IdParam IdType theId) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class SearchProvider { + + @Search(type = Patient.class) + public Patient findPatient1(@Description(shortDefinition = "The patient's identifier (MRN or other card number)") @RequiredParam(name = "identifier") TokenParam theIdentifier) { + return null; + } + + @Search(type = Patient.class) + public Patient findPatient2( + @Description(shortDefinition = "All patients linked to the given patient") @OptionalParam(name = "link", targetTypes = { Patient.class }) ReferenceAndListParam theLink) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class SearchProviderWithWhitelist { + + @Search(type = Patient.class) + public Patient findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = "organization", chainWhitelist = { "foo", + "bar" }) ReferenceAndListParam theIdentifier) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class SearchProviderWithListNoType implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + + + @Search() + public List findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = "organization") ReferenceAndListParam theIdentifier) { + return null; + } + + } + + @SuppressWarnings("unused") + public static class SearchProviderWithListWithType implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + + + @Search(type=Patient.class) + public List findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = "organization") ReferenceAndListParam theIdentifier) { + return null; + } + + } + + + public static class SystemHistoryProvider { + + @History + public List history() { + return null; + } + + } + + public static class TypeHistoryProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @History + public List history() { + return null; + } + + } + + @SuppressWarnings("unused") + public static class VreadProvider { + + @Search(type = Patient.class) + public Patient findPatient(@Description(shortDefinition = "The patient's identifier (MRN or other card number)") @RequiredParam(name = "identifier") TokenParam theIdentifier) { + return null; + } + + @Read(version = true) + public Patient readPatient(@IdParam IdType theId) { + return null; + } + + } + + public static class TypeLevelOperationProvider implements IResourceProvider { + + public static final String OPERATION_NAME = "op"; + + @Operation(name = OPERATION_NAME, idempotent = true) + public IBundleProvider op() { + return null; + } + + @Override + public Class getResourceType() { + return Patient.class; + } + + } + + public static class NamedQueryPlainProvider { + + public static final String QUERY_NAME = "testQuery"; + public static final String DESCRIPTION = "A query description"; + public static final String SP_QUANTITY = "quantity"; + + @Search(queryName = QUERY_NAME) + @Description(formalDefinition = DESCRIPTION) + public Bundle findAllGivenParameter(@RequiredParam(name = SP_QUANTITY) QuantityParam quantity) { + return null; + } + } + + public static class NamedQueryResourceProvider implements IResourceProvider { + + public static final String QUERY_NAME = "testQuery"; + public static final String SP_PARAM = "param"; + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Search(queryName = QUERY_NAME) + public Bundle findAllGivenParameter(@OptionalParam(name = SP_PARAM) StringParam param) { + return null; + } + + } + + public static class ProfiledPatientProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return PatientSubSub2.class; + } + + @Search + public List find() { + return null; + } + } + + public static class MultipleProfilesPatientProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return PatientSubSub.class; + } + + @Read(type = PatientTripleSub.class) + public PatientTripleSub read(@IdParam IdType theId) { + return null; + } + + } + + public static final String PATIENT_SUB = "PatientSub"; + public static final String PATIENT_SUB_SUB = "PatientSubSub"; + public static final String PATIENT_SUB_SUB_2 = "PatientSubSub2"; + public static final String PATIENT_TRIPLE_SUB = "PatientTripleSub"; + + @ResourceDef(id = PATIENT_SUB) + public static class PatientSub extends Patient {} + + @ResourceDef(id = PATIENT_SUB_SUB) + public static class PatientSubSub extends PatientSub {} + + @ResourceDef(id = PATIENT_SUB_SUB_2) + public static class PatientSubSub2 extends PatientSub {} + + @ResourceDef(id = PATIENT_TRIPLE_SUB) + public static class PatientTripleSub extends PatientSubSub {} + +} diff --git a/hapi-fhir-structures-r4b/src/test/java/org/hl7/fhir/r4b/model/ModelR4BTest.java b/hapi-fhir-structures-r4b/src/test/java/org/hl7/fhir/r4b/model/ModelR4BTest.java new file mode 100644 index 00000000000..d3efff8191b --- /dev/null +++ b/hapi-fhir-structures-r4b/src/test/java/org/hl7/fhir/r4b/model/ModelR4BTest.java @@ -0,0 +1,52 @@ +package org.hl7.fhir.r4b.model; + +import ca.uhn.fhir.context.FhirContext; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class ModelR4BTest { + + private static FhirContext ourCtx = FhirContext.forR4BCached(); + + @Test + public void testbase64BinaryName() { + assertEquals("base64Binary", ourCtx.getElementDefinition("base64binary").getName()); + assertEquals("base64Binary", ourCtx.getElementDefinition("base64Binary").getName()); + } + + @Test + public void testInstantPrecision() { + new InstantType("2019-01-01T00:00:00Z"); + new InstantType("2019-01-01T00:00:00.0Z"); + new InstantType("2019-01-01T00:00:00.000Z"); + try { + new InstantType("2019-01-01T00:00Z"); + fail(); + } catch (IllegalArgumentException e) { + // good + } + } + + + @Test + public void testCompartmentsPopulated() { + Set compartments = ourCtx + .getResourceDefinition("Observation") + .getSearchParam("performer") + .getProvidesMembershipInCompartments(); + assertThat(compartments.toString(), compartments, containsInAnyOrder( + "Practitioner", + "Patient", + "RelatedPerson" + )); + } + + +} diff --git a/hapi-fhir-structures-r4b/src/test/java/org/hl7/fhir/r4b/utils/GraphQLEngineTest.java b/hapi-fhir-structures-r4b/src/test/java/org/hl7/fhir/r4b/utils/GraphQLEngineTest.java new file mode 100644 index 00000000000..95ddfe0ce33 --- /dev/null +++ b/hapi-fhir-structures-r4b/src/test/java/org/hl7/fhir/r4b/utils/GraphQLEngineTest.java @@ -0,0 +1,184 @@ +package org.hl7.fhir.r4b.utils; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.util.TestUtil; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext; +import org.hl7.fhir.r4b.model.DateTimeType; +import org.hl7.fhir.r4b.model.Observation; +import org.hl7.fhir.r4b.model.Patient; +import org.hl7.fhir.r4b.model.Period; +import org.hl7.fhir.r4b.model.Quantity; +import org.hl7.fhir.r4b.model.Reference; +import org.hl7.fhir.r4b.model.Resource; +import org.hl7.fhir.utilities.graphql.EGraphEngine; +import org.hl7.fhir.utilities.graphql.EGraphQLException; +import org.hl7.fhir.utilities.graphql.GraphQLResponse; +import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices; +import org.hl7.fhir.utilities.graphql.Parser; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class GraphQLEngineTest { + private static HapiWorkerContext ourWorkerCtx; + private static FhirContext ourCtx; + private org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GraphQLEngineTest.class); + + private Observation createObservation() { + Observation obs = new Observation(); + obs.setId("http://foo.com/Patient/PATA"); + obs.setValue(new Quantity().setValue(123).setUnit("cm")); + obs.setSubject(new Reference("Patient/123")); + return obs; + } + + private IGraphQLStorageServices createStorageServices() throws FHIRException { + IGraphQLStorageServices retVal = mock(IGraphQLStorageServices.class); + when(retVal.lookup(nullable(Object.class), nullable(Resource.class), nullable(Reference.class))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + Object appInfo = invocation.getArguments()[0]; + Resource context = (Resource) invocation.getArguments()[1]; + Reference reference = (Reference) invocation.getArguments()[2]; + ourLog.info("AppInfo: {} / Context: {} / Reference: {}", appInfo, context.getId(), reference.getReference()); + + if (reference.getReference().equalsIgnoreCase("Patient/123")) { + Patient p = new Patient(); + p.getBirthDateElement().setValueAsString("2011-02-22"); + return new IGraphQLStorageServices.ReferenceResolution(context, p); + } + + ourLog.info("Not found!"); + return null; + } + }); + + return retVal; + } + + @Test + public void testGraphSimple() throws EGraphQLException, EGraphEngine, IOException, FHIRException { + + Observation obs = createObservation(); + + GraphQLEngine engine = new GraphQLEngine(ourWorkerCtx); + engine.setFocus(obs); + engine.setGraphQL(Parser.parse("{valueQuantity{value,unit}}")); + engine.execute(); + + GraphQLResponse output = engine.getOutput(); + output.setWriteWrapper(false); + StringBuilder outputBuilder = new StringBuilder(); + output.write(outputBuilder, 0, "\n"); + + String expected = "{\n" + + " \"valueQuantity\":{\n" + + " \"value\":123,\n" + + " \"unit\":\"cm\"\n" + + " }\n" + + "}"; + assertEquals(TestUtil.stripReturns(expected), TestUtil.stripReturns(outputBuilder.toString())); + + } + + + @Test + public void testChoiceType_SelectDifferentType() throws EGraphEngine, EGraphQLException, IOException { + Observation obs = new Observation(); + obs.setId("http://foo.com/Patient/PATA"); + obs.setEffective(new Period().setStartElement(new DateTimeType("2022-01-01T00:00:00Z")).setEndElement(new DateTimeType("2022-01-01T05:00:00Z"))); + + GraphQLEngine engine = new GraphQLEngine(ourWorkerCtx); + engine.setFocus(obs); + engine.setGraphQL(Parser.parse("{id, effectiveDateTime}")); + engine.execute(); + + GraphQLResponse output = engine.getOutput(); + output.setWriteWrapper(false); + StringBuilder outputBuilder = new StringBuilder(); + output.write(outputBuilder, 0, "\n"); + + String expected = "{\n" + + " \"id\":\"http://foo.com/Patient/PATA\"\n" + + "}"; + assertEquals(TestUtil.stripReturns(expected), TestUtil.stripReturns(outputBuilder.toString())); + } + + @Test + public void testChoiceType_SelectSameType() throws EGraphEngine, EGraphQLException, IOException { + Observation obs = new Observation(); + obs.setId("http://foo.com/Patient/PATA"); + obs.setEffective(new DateTimeType("2022-01-01T12:12:12Z")); + + GraphQLEngine engine = new GraphQLEngine(ourWorkerCtx); + engine.setFocus(obs); + engine.setGraphQL(Parser.parse("{id, effectiveDateTime}")); + engine.execute(); + + GraphQLResponse output = engine.getOutput(); + output.setWriteWrapper(false); + StringBuilder outputBuilder = new StringBuilder(); + output.write(outputBuilder, 0, "\n"); + + String expected = "{\n" + + " \"id\":\"http://foo.com/Patient/PATA\",\n" + + " \"effectiveDateTime\":\"2022-01-01T12:12:12Z\"\n" + + "}"; + assertEquals(TestUtil.stripReturns(expected), TestUtil.stripReturns(outputBuilder.toString())); + } + + + @Test + public void testReferences() throws EGraphQLException, EGraphEngine, IOException, FHIRException { + + String graph = " { \n" + + " id\n" + + " subject { \n" + + " reference\n" + + " resource(type : Patient) { birthDate }\n" + + " resource(type : Practioner) { practitionerRole { speciality } }\n" + + " } \n" + + " code {coding {system code} }\n" + + " }\n" + + " "; + + GraphQLEngine engine = new GraphQLEngine(ourWorkerCtx); + engine.setFocus(createObservation()); + engine.setGraphQL(Parser.parse(graph)); + engine.setServices(createStorageServices()); + engine.execute(); + + GraphQLResponse output = engine.getOutput(); + output.setWriteWrapper(false); + StringBuilder outputBuilder = new StringBuilder(); + output.write(outputBuilder, 0, "\n"); + + String expected = "{\n" + + " \"id\":\"http://foo.com/Patient/PATA\",\n" + + " \"subject\":{\n" + + " \"reference\":\"Patient/123\",\n" + + " \"resource\":{\n" + + " \"birthDate\":\"2011-02-22\"\n" + + " }\n" + + " }\n" + + "}"; + assertEquals(TestUtil.stripReturns(expected), TestUtil.stripReturns(outputBuilder.toString())); + + } + + @BeforeAll + public static void beforeClass() { + ourCtx = FhirContext.forR4BCached(); + ourWorkerCtx = new HapiWorkerContext(ourCtx, ourCtx.getValidationSupport()); + } + +} diff --git a/hapi-fhir-structures-r4b/src/test/resources/logback-test.xml b/hapi-fhir-structures-r4b/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..e5cbbb9c22e --- /dev/null +++ b/hapi-fhir-structures-r4b/src/test/resources/logback-test.xml @@ -0,0 +1,30 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n + + + + + + + + + + + + + + + + + + + + + diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index af1d54274e7..d1a8a5c971b 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 7b35a1cc1c7..384b3eb974d 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index dca7af231c9..40b45596ad7 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../pom.xml @@ -39,6 +39,11 @@ hapi-fhir-server ${project.version} + + ca.uhn.hapi.fhir + hapi-fhir-converter + ${project.version} + ca.uhn.hapi.fhir hapi-fhir-jpaserver-base @@ -60,6 +65,11 @@ hapi-fhir-structures-r4 ${project.version} + + ca.uhn.hapi.fhir + hapi-fhir-structures-r4b + ${project.version} + ca.uhn.hapi.fhir hapi-fhir-structures-r5 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 8866a451815..a5e83c74644 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 @@ -4,10 +4,7 @@ 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.model.api.ExtensionDt; import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu2.resource.Conformance; -import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.api.IClientInterceptor; @@ -16,20 +13,18 @@ import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.client.impl.GenericClient; import ca.uhn.fhir.to.model.HomeRequest; import ca.uhn.fhir.util.ExtensionConstants; +import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.entity.ContentType; import org.apache.http.message.BasicHeader; -import org.hl7.fhir.dstu3.model.CapabilityStatement; -import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent; -import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceComponent; -import org.hl7.fhir.dstu3.model.DecimalType; -import org.hl7.fhir.dstu3.model.Extension; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.CapabilityStatement; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ui.ModelMap; import org.thymeleaf.ITemplateEngine; @@ -51,7 +46,8 @@ public class BaseController { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseController.class); @Autowired protected TesterConfig myConfig; - private final Map myContexts = new HashMap(); + private final Map myContexts = new HashMap<>(); + private final Map myCanonicalizers = new HashMap<>(); private List myFilterHeaders; @Autowired private ITemplateEngine myTemplateEngine; @@ -60,7 +56,7 @@ public class BaseController { super(); } - protected IBaseResource addCommonParams(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { + protected CapabilityStatement addCommonParams(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { final String serverId = theRequest.getServerIdWithDefault(myConfig); final String serverBase = theRequest.getServerBase(theServletRequest, myConfig); final String serverName = theRequest.getServerName(myConfig); @@ -262,6 +258,16 @@ public class BaseController { return retVal; } + protected VersionCanonicalizer getVersionCanonicalizer(HomeRequest theRequest) { + FhirVersionEnum version = theRequest.getFhirVersion(myConfig); + VersionCanonicalizer retVal = myCanonicalizers.get(version); + if (retVal == null) { + retVal = new VersionCanonicalizer(version); + myCanonicalizers.put(version, retVal); + } + return retVal; + } + protected RuntimeResourceDefinition getResourceType(HomeRequest theRequest, HttpServletRequest theReq) throws ServletException { String resourceName = sanitizeUrlPart(defaultString(theReq.getParameter(PARAM_RESOURCE))); RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(resourceName); @@ -283,218 +289,28 @@ public class BaseController { return returnsResource; } - private IBaseResource loadAndAddConf(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { - switch (theRequest.getFhirVersion(myConfig)) { - case DSTU2: - return loadAndAddConfDstu2(theServletRequest, theRequest, theModel); - case DSTU3: - return loadAndAddConfDstu3(theServletRequest, theRequest, theModel); - case R4: - return loadAndAddConfR4(theServletRequest, theRequest, theModel); - case R5: - return loadAndAddConfR5(theServletRequest, theRequest, theModel); - case DSTU2_1: - case DSTU2_HL7ORG: - break; - } - throw new IllegalStateException(Msg.code(193) + "Unknown version: " + theRequest.getFhirVersion(myConfig)); - } - - private IResource loadAndAddConfDstu2(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { + private CapabilityStatement loadAndAddConf(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); - ca.uhn.fhir.model.dstu2.resource.Conformance conformance; + IBaseResource fetchedCapabilityStatement; + FhirContext ctx = getContext(theRequest); + String name = "CapabilityStatement"; + if (ctx.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) { + name = "Conformance"; + } try { - conformance = client.fetchConformance().ofType(Conformance.class).execute(); - } catch (Exception e) { - ourLog.warn("Failed to load conformance statement, error was: {}", e.toString()); - theModel.put("errorMsg", toDisplayError("Failed to load conformance statement, error was: " + e.toString(), e)); - conformance = new ca.uhn.fhir.model.dstu2.resource.Conformance(); - } - - theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance)); - - Map resourceCounts = new HashMap<>(); - long total = 0; - for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) { - for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextResource : nextRest.getResource()) { - List exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (exts != null && exts.size() > 0) { - Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber(); - resourceCounts.put(nextResource.getTypeElement().getValue(), nextCount); - total += nextCount.longValue(); - } - } - } - theModel.put("resourceCounts", resourceCounts); - - if (total > 0) { - for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) { - Collections.sort(nextRest.getResource(), new Comparator() { - @Override - public int compare(ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO1, ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO2) { - DecimalDt count1 = new DecimalDt(); - List count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count1exts != null && count1exts.size() > 0) { - count1 = (DecimalDt) count1exts.get(0).getValue(); - } - DecimalDt count2 = new DecimalDt(); - List count2exts = theO2.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count2exts != null && count2exts.size() > 0) { - count2 = (DecimalDt) count2exts.get(0).getValue(); - } - int retVal = count2.compareTo(count1); - if (retVal == 0) { - retVal = theO1.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue()); - } - return retVal; - } - }); - } - } - - theModel.put("conf", conformance); - theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED); - - return conformance; - } - - private IBaseResource loadAndAddConfDstu3(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { - CaptureInterceptor interceptor = new CaptureInterceptor(); - GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); - - org.hl7.fhir.dstu3.model.CapabilityStatement capabilityStatement = new CapabilityStatement(); - try { - capabilityStatement = client.fetchConformance().ofType(org.hl7.fhir.dstu3.model.CapabilityStatement.class).execute(); + Class type = (Class) ctx.getResourceDefinition(name).getImplementingClass(); + fetchedCapabilityStatement = client.fetchConformance().ofType(type).execute(); } catch (Exception ex) { ourLog.warn("Failed to load conformance statement, error was: {}", ex.toString()); - theModel.put("errorMsg", toDisplayError("Failed to load conformance statement, error was: " + ex.toString(), ex)); + theModel.put("errorMsg", toDisplayError("Failed to load conformance statement, error was: " + ex, ex)); + fetchedCapabilityStatement = ctx.getResourceDefinition(name).newInstance(); } - theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(capabilityStatement)); + theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(fetchedCapabilityStatement)); - Map resourceCounts = new HashMap<>(); - long total = 0; - - for (CapabilityStatementRestComponent nextRest : capabilityStatement.getRest()) { - for (CapabilityStatementRestResourceComponent nextResource : nextRest.getResource()) { - List exts = nextResource.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (exts != null && exts.size() > 0) { - Number nextCount = ((DecimalType) (exts.get(0).getValue())).getValueAsNumber(); - resourceCounts.put(nextResource.getTypeElement().getValue(), nextCount); - total += nextCount.longValue(); - } - } - } - - theModel.put("resourceCounts", resourceCounts); - - if (total > 0) { - for (CapabilityStatementRestComponent nextRest : capabilityStatement.getRest()) { - Collections.sort(nextRest.getResource(), new Comparator() { - @Override - public int compare(CapabilityStatementRestResourceComponent theO1, CapabilityStatementRestResourceComponent theO2) { - DecimalType count1 = new DecimalType(); - List count1exts = theO1.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count1exts != null && count1exts.size() > 0) { - count1 = (DecimalType) count1exts.get(0).getValue(); - } - DecimalType count2 = new DecimalType(); - List count2exts = theO2.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count2exts != null && count2exts.size() > 0) { - count2 = (DecimalType) count2exts.get(0).getValue(); - } - int retVal = count2.compareTo(count1); - if (retVal == 0) { - retVal = theO1.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue()); - } - return retVal; - } - }); - } - } - - theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED); - - theModel.put("conf", capabilityStatement); - return capabilityStatement; - } - - private IBaseResource loadAndAddConfR4(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { - CaptureInterceptor interceptor = new CaptureInterceptor(); - GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); - - org.hl7.fhir.r4.model.CapabilityStatement capabilityStatement = new org.hl7.fhir.r4.model.CapabilityStatement(); - try { - capabilityStatement = client.fetchConformance().ofType(org.hl7.fhir.r4.model.CapabilityStatement.class).execute(); - } catch (Exception ex) { - ourLog.warn("Failed to load conformance statement, error was: {}", ex.toString()); - theModel.put("errorMsg", toDisplayError("Failed to load conformance statement, error was: " + ex.toString(), ex)); - } - - theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(capabilityStatement)); - - Map resourceCounts = new HashMap<>(); - long total = 0; - - for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent nextRest : capabilityStatement.getRest()) { - for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent nextResource : nextRest.getResource()) { - List exts = nextResource.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (exts != null && exts.size() > 0) { - Number nextCount = ((org.hl7.fhir.r4.model.DecimalType) (exts.get(0).getValue())).getValueAsNumber(); - resourceCounts.put(nextResource.getTypeElement().getValue(), nextCount); - total += nextCount.longValue(); - } - } - } - - theModel.put("resourceCounts", resourceCounts); - - if (total > 0) { - for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent nextRest : capabilityStatement.getRest()) { - Collections.sort(nextRest.getResource(), new Comparator() { - @Override - public int compare(org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent theO1, org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent theO2) { - org.hl7.fhir.r4.model.DecimalType count1 = new org.hl7.fhir.r4.model.DecimalType(); - List count1exts = theO1.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count1exts != null && count1exts.size() > 0) { - count1 = (org.hl7.fhir.r4.model.DecimalType) count1exts.get(0).getValue(); - } - org.hl7.fhir.r4.model.DecimalType count2 = new org.hl7.fhir.r4.model.DecimalType(); - List count2exts = theO2.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count2exts != null && count2exts.size() > 0) { - count2 = (org.hl7.fhir.r4.model.DecimalType) count2exts.get(0).getValue(); - } - int retVal = count2.compareTo(count1); - if (retVal == 0) { - retVal = theO1.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue()); - } - return retVal; - } - }); - } - } - - theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED); - - theModel.put("conf", capabilityStatement); - return capabilityStatement; - } - - private IBaseResource loadAndAddConfR5(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { - CaptureInterceptor interceptor = new CaptureInterceptor(); - GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); - - org.hl7.fhir.r5.model.CapabilityStatement capabilityStatement = new org.hl7.fhir.r5.model.CapabilityStatement(); - try { - capabilityStatement = client.fetchConformance().ofType(org.hl7.fhir.r5.model.CapabilityStatement.class).execute(); - } catch (Exception ex) { - ourLog.warn("Failed to load conformance statement, error was: {}", ex.toString()); - theModel.put("errorMsg", toDisplayError("Failed to load conformance statement, error was: " + ex.toString(), ex)); - } - - theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(capabilityStatement)); + org.hl7.fhir.r5.model.CapabilityStatement capabilityStatement = getVersionCanonicalizer(theRequest).capabilityStatementToCanonical(fetchedCapabilityStatement); Map resourceCounts = new HashMap<>(); long total = 0; @@ -514,25 +330,22 @@ public class BaseController { if (total > 0) { for (org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent nextRest : capabilityStatement.getRest()) { - Collections.sort(nextRest.getResource(), new Comparator() { - @Override - public int compare(org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent theO1, org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent theO2) { - org.hl7.fhir.r5.model.DecimalType count1 = new org.hl7.fhir.r5.model.DecimalType(); - List count1exts = theO1.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count1exts != null && count1exts.size() > 0) { - count1 = (org.hl7.fhir.r5.model.DecimalType) count1exts.get(0).getValue(); - } - org.hl7.fhir.r5.model.DecimalType count2 = new org.hl7.fhir.r5.model.DecimalType(); - List count2exts = theO2.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count2exts != null && count2exts.size() > 0) { - count2 = (org.hl7.fhir.r5.model.DecimalType) count2exts.get(0).getValue(); - } - int retVal = count2.compareTo(count1); - if (retVal == 0) { - retVal = theO1.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue()); - } - return retVal; + Collections.sort(nextRest.getResource(), (theO1, theO2) -> { + org.hl7.fhir.r5.model.DecimalType count1 = new org.hl7.fhir.r5.model.DecimalType(); + List count1exts = theO1.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); + if (count1exts != null && count1exts.size() > 0) { + count1 = (org.hl7.fhir.r5.model.DecimalType) count1exts.get(0).getValue(); } + org.hl7.fhir.r5.model.DecimalType count2 = new org.hl7.fhir.r5.model.DecimalType(); + List count2exts = theO2.getExtensionsByUrl(RESOURCE_COUNT_EXT_URL); + if (count2exts != null && count2exts.size() > 0) { + count2 = (org.hl7.fhir.r5.model.DecimalType) count2exts.get(0).getValue(); + } + int retVal = count2.compareTo(count1); + if (retVal == 0) { + retVal = theO1.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue()); + } + return retVal; }); } } @@ -543,6 +356,7 @@ public class BaseController { return capabilityStatement; } + protected String logPrefix(ModelMap theModel) { return "[server=" + theModel.get("serverId") + "] - "; } diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java index f0c29838cca..25cb24bb99f 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.to; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; @@ -78,24 +79,18 @@ public class Controller extends BaseController { addCommonParams(theServletRequest, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); - GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); + FhirContext context = getContext(theRequest); + GenericClient client = theRequest.newClient(theServletRequest, context, myConfig, interceptor); ResultType returnsResource = ResultType.RESOURCE; long start = System.currentTimeMillis(); try { - Class type; - switch (getContext(theRequest).getVersion().getVersion()) { - default: - case DSTU2: - type = ca.uhn.fhir.model.dstu2.resource.Conformance.class; - break; - case DSTU3: - type = org.hl7.fhir.dstu3.model.CapabilityStatement.class; - break; - case R4: - type = org.hl7.fhir.r4.model.CapabilityStatement.class; - break; + String name = "CapabilityStatement"; + if (context.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) { + name = "Conformance"; } + + Class type = (Class) context.getResourceDefinition(name).getImplementingClass(); client.fetchConformance().ofType(type).execute(); } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); @@ -294,7 +289,7 @@ public class Controller extends BaseController { } private void populateModelForResource(HttpServletRequest theServletRequest, HomeRequest theRequest, ModelMap theModel) { - IBaseResource conformance = addCommonParams(theServletRequest, theRequest, theModel); + org.hl7.fhir.r5.model.CapabilityStatement conformance = addCommonParams(theServletRequest, theRequest, theModel); String resourceName = theRequest.getResource(); @@ -304,22 +299,7 @@ public class Controller extends BaseController { boolean haveSearchParams = false; List> queryIncludes = new ArrayList<>(); - switch (theRequest.getFhirVersion(myConfig)) { - case DSTU2: - haveSearchParams = extractSearchParamsDstu2(conformance, resourceName, includes, revIncludes, sortParams, haveSearchParams, queryIncludes); - break; - case DSTU3: - haveSearchParams = extractSearchParamsDstu3CapabilityStatement(conformance, resourceName, includes, revIncludes, sortParams, haveSearchParams, queryIncludes); - break; - case R4: - haveSearchParams = extractSearchParamsR4CapabilityStatement(conformance, resourceName, includes, revIncludes, sortParams, haveSearchParams, queryIncludes); - break; - case R5: - haveSearchParams = extractSearchParamsR5CapabilityStatement(conformance, resourceName, includes, revIncludes, sortParams, haveSearchParams, queryIncludes); - break; - default: - throw new IllegalStateException(Msg.code(190) + "Unknown FHIR version: " + theRequest.getFhirVersion(myConfig)); - } + haveSearchParams = extractSearchParamsR5CapabilityStatement(conformance, resourceName, includes, revIncludes, sortParams, haveSearchParams, queryIncludes); theModel.put("includes", includes); theModel.put("revincludes", revIncludes); diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 085669375ef..daa86a0c736 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 fc7db92bd39..53f213d1419 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 be57f03c245..fac284c51b1 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 9d1ab9790b5..a877d8d89b1 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 abc9d802f6c..31c34970a94 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index c27e7598a0d..014819bd683 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java index 2f58495f05d..24d22aa6fb0 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java @@ -790,6 +790,11 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo break; } + case R4B: { + converter = new VersionTypeConverterR4B(); + break; + } + case R5: { converter = IDENTITY_VERSION_TYPE_CONVERTER; break; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeConverterR4B.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeConverterR4B.java new file mode 100644 index 00000000000..dc35d6a29aa --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeConverterR4B.java @@ -0,0 +1,18 @@ +package org.hl7.fhir.common.hapi.validation.validator; + +import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Resource; + +public class VersionTypeConverterR4B implements VersionSpecificWorkerContextWrapper.IVersionTypeConverter { + @Override + public Resource toCanonical(IBaseResource theNonCanonical) { + return VersionConvertorFactory_43_50.convertResource((org.hl7.fhir.r4b.model.Resource) theNonCanonical, new BaseAdvisor_43_50(false)); + } + + @Override + public IBaseResource fromCanonical(Resource theCanonical) { + return VersionConvertorFactory_43_50.convertResource(theCanonical, new BaseAdvisor_43_50(false)); + } +} diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index e0f1327ef11..d023edfe58e 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../pom.xml @@ -65,37 +65,42 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT + + + ca.uhn.hapi.fhir + hapi-fhir-structures-r4b + 6.2.0-PRE17-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT org.apache.velocity diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ResourceMinimizerMojo.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ResourceMinimizerMojo.java index a5c89247951..bac8a453c02 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ResourceMinimizerMojo.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ResourceMinimizerMojo.java @@ -56,6 +56,8 @@ public class ResourceMinimizerMojo extends AbstractMojo { myCtx = FhirContext.forDstu3(); } else if ("R4".equals(fhirVersion)) { myCtx = FhirContext.forR4(); + } else if ("R4B".equals(fhirVersion)) { + myCtx = FhirContext.forR4B(); } else if ("R5".equals(fhirVersion)) { myCtx = FhirContext.forR5(); } else { @@ -141,11 +143,13 @@ public class ResourceMinimizerMojo extends AbstractMojo { FhirContext ctxDstu2_1; FhirContext ctxDstu3; FhirContext ctxR4; + FhirContext ctxR4B; FhirContext ctxR5; ctxDstu2 = FhirContext.forDstu2(); // ctxDstu2_1 = FhirContext.forDstu2_1(); ctxDstu3 = FhirContext.forDstu3(); ctxR4 = FhirContext.forR4(); + ctxR4B = FhirContext.forR4B(); ctxR5 = FhirContext.forR5(); LoggerContext loggerContext = ((ch.qos.logback.classic.Logger) ourLog).getLoggerContext(); @@ -237,6 +241,42 @@ public class ResourceMinimizerMojo extends AbstractMojo { // byteCount += m.getByteCount(); // fileCount += m.getFileCount(); + m = new ResourceMinimizerMojo(); + m.myCtx = ctxR4B; + m.targetDirectory = new File("./hapi-fhir-validation-resources-r4b/src/main/resources/org/hl7/fhir/r4b/model/profile"); + m.fhirVersion = "R4B"; + m.execute(); + byteCount += m.getByteCount(); + fileCount += m.getFileCount(); + + m = new ResourceMinimizerMojo(); + m.myCtx = ctxR4B; + m.targetDirectory = new File("./hapi-fhir-validation-resources-r4b/src/main/resources/org/hl7/fhir/r4b/model/valueset"); + m.fhirVersion = "R4B"; + m.execute(); + byteCount += m.getByteCount(); + fileCount += m.getFileCount(); + + m = new ResourceMinimizerMojo(); + m.myCtx = ctxR4B; + m.targetDirectory = new File("./hapi-fhir-validation-resources-r4b/src/main/resources/org/hl7/fhir/r4b/model/extension"); + m.fhirVersion = "R4B"; + m.execute(); + byteCount += m.getByteCount(); + fileCount += m.getFileCount(); + + m = new ResourceMinimizerMojo(); + m.myCtx = ctxR4B; + m.targetDirectory = new File("./hapi-fhir-validation-resources-r4b/src/main/resources/org/hl7/fhir/r4b/model/sp"); + m.fhirVersion = "R4B"; + m.execute(); + byteCount += m.getByteCount(); + fileCount += m.getFileCount(); + + + + + m = new ResourceMinimizerMojo(); m.myCtx = ctxR5; m.targetDirectory = new File("./hapi-fhir-validation-resources-r5/src/main/resources/org/hl7/fhir/r5/model/profile"); @@ -269,13 +309,13 @@ public class ResourceMinimizerMojo extends AbstractMojo { byteCount += m.getByteCount(); fileCount += m.getFileCount(); - m = new ResourceMinimizerMojo(); - m.myCtx = ctxR5; - m.targetDirectory = new File("./hapi-fhir-validation-resources-r5/src/main/resources/org/hl7/fhir/r5/model/compartment"); - m.fhirVersion = "R5"; +// m = new ResourceMinimizerMojo(); +// m.myCtx = ctxR5; +// m.targetDirectory = new File("./hapi-fhir-validation-resources-r5/src/main/resources/org/hl7/fhir/r5/model/compartment"); +// m.fhirVersion = "R5"; // m.execute(); - byteCount += m.getByteCount(); - fileCount += m.getFileCount(); +// byteCount += m.getByteCount(); +// fileCount += m.getFileCount(); ourLog.info("Trimmed {} files", fileCount); ourLog.info("Trimmed {} bytes", FileUtils.byteCountToDisplaySize(byteCount)); diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderGenericSingleFileMojo.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderGenericSingleFileMojo.java index ab1ba1cd836..6bf97fdcfc5 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderGenericSingleFileMojo.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderGenericSingleFileMojo.java @@ -368,8 +368,8 @@ public class TinderGenericSingleFileMojo extends AbstractMojo { } String capitalize = WordUtils.capitalize(version); - if ("Dstu".equals(capitalize)) { - capitalize="Dstu1"; + if ("R4b".equals(capitalize)) { + capitalize="R4B"; } ctx.put("versionCapitalized", capitalize); diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java index cd961853a69..2615f49e5dc 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/TinderJpaRestServerMojo.java @@ -80,6 +80,9 @@ public class TinderJpaRestServerMojo extends AbstractMojo { } else if ("r4".equals(version)) { fhirContext = FhirContext.forR4(); packageSuffix = ".r4"; + } else if ("r4b".equals(version)) { + fhirContext = FhirContext.forR4B(); + packageSuffix = ".r4b"; } else if ("r5".equals(version)) { fhirContext = FhirContext.forR5(); packageSuffix = ".r5"; @@ -169,8 +172,8 @@ public class TinderJpaRestServerMojo extends AbstractMojo { } String capitalize = WordUtils.capitalize(version); - if ("Dstu".equals(capitalize)) { - capitalize = "Dstu1"; + if ("R4b".equals(capitalize)) { + capitalize = "R4B"; } ctx.put("versionCapitalized", capitalize); diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/VersionPropertyFileGeneratorMojo.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/VersionPropertyFileGeneratorMojo.java index 3a20c2199a2..1430d11e460 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/VersionPropertyFileGeneratorMojo.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/VersionPropertyFileGeneratorMojo.java @@ -126,13 +126,19 @@ public class VersionPropertyFileGeneratorMojo extends AbstractMojo { } public static void main(String[] theArgs) throws MojoFailureException { + VersionPropertyFileGeneratorMojo m; // VersionPropertyFileGeneratorMojo m = new VersionPropertyFileGeneratorMojo(); // m.packageName = "org.hl7.fhir.r4.model"; // m.targetFile = new File("hapi-fhir-structures-r4/src/main/resources/org/hl7/fhir/r4/model/fhirversion.properties"); // m.execute(); - VersionPropertyFileGeneratorMojo m = new VersionPropertyFileGeneratorMojo(); + m = new VersionPropertyFileGeneratorMojo(); + m.packageName = "org.hl7.fhir.r4b.model"; + m.targetFile = new File("hapi-fhir-structures-r4b/src/main/resources/org/hl7/fhir/r4b/model/fhirversion.properties"); +// m.execute(); + + m = new VersionPropertyFileGeneratorMojo(); m.packageName = "org.hl7.fhir.r5.model"; m.targetFile = new File("hapi-fhir-structures-r5/src/main/resources/org/hl7/fhir/r5/model/fhirversion.properties"); m.execute(); diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ant/TinderGeneratorTask.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ant/TinderGeneratorTask.java index 8d17ca9c4c0..f7896a13a1a 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ant/TinderGeneratorTask.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ant/TinderGeneratorTask.java @@ -432,8 +432,8 @@ public class TinderGeneratorTask extends Task { } String capitalize = WordUtils.capitalize(version); - if ("Dstu".equals(capitalize)) { - capitalize="Dstu1"; + if ("R4b".equals(capitalize)) { + capitalize="R4B"; } ctx.put("versionCapitalized", capitalize); diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureParser.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureParser.java index aae825a6670..f476b82322d 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureParser.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/parser/BaseStructureParser.java @@ -86,13 +86,15 @@ public abstract class BaseStructureParser { myBaseDir = theBaseDir; if (myVersion.equals("r4")) { - myCtx = FhirContext.forR4(); + myCtx = FhirContext.forR4Cached(); } else if (myVersion.equals("dstu3")) { - myCtx = FhirContext.forDstu3(); + myCtx = FhirContext.forDstu3Cached(); } else if (myVersion.equals("dstu2")) { - myCtx = FhirContext.forDstu2(); + myCtx = FhirContext.forDstu2Cached(); + } else if (myVersion.equals("r4b")) { + myCtx = FhirContext.forR4BCached(); } else if (myVersion.equals("r5")) { - myCtx = FhirContext.forR5(); + myCtx = FhirContext.forR5Cached(); } else { throw new MojoFailureException(Msg.code(151) + "Unknown version: " + myVersion); } @@ -547,8 +549,8 @@ public abstract class BaseStructureParser { ctx.put("isRi", determineVersionEnum().isRi()); String capitalize = WordUtils.capitalize(myVersion); - if ("Dstu".equals(capitalize)) { - capitalize = "Dstu1"; + if ("R4b".equals(capitalize)) { + capitalize = "R4B"; } ctx.put("versionCapitalized", capitalize); ctx.put("this", theResource); @@ -692,8 +694,8 @@ public abstract class BaseStructureParser { ctx.put("isRi", determineVersionEnum().isRi()); ctx.put("package_suffix", packageSuffix); String capitalize = WordUtils.capitalize(myVersion); - if ("Dstu".equals(capitalize)) { - capitalize = "Dstu1"; + if ("R4b".equals(capitalize)) { + capitalize = "R4B"; } ctx.put("versionCapitalized", capitalize); @@ -748,6 +750,8 @@ public abstract class BaseStructureParser { versionEnum = FhirVersionEnum.DSTU3; } else if ("r4".equals(version)) { versionEnum = FhirVersionEnum.R4; + } else if ("r4b".equals(version)) { + versionEnum = FhirVersionEnum.R4B; } else if ("r5".equals(version)) { versionEnum = FhirVersionEnum.R5; } else { diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/account.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/account.xlsx new file mode 100644 index 00000000000..511242d9280 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/account.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/activitydefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/activitydefinition.xlsx new file mode 100644 index 00000000000..cd9de702eb9 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/activitydefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/administrableproductdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/administrableproductdefinition.xlsx new file mode 100644 index 00000000000..b2a291ac012 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/administrableproductdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/adverseevent.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/adverseevent.xlsx new file mode 100644 index 00000000000..5a31db4e7a2 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/adverseevent.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/allergyintolerance.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/allergyintolerance.xlsx new file mode 100644 index 00000000000..9fe6509a21e Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/allergyintolerance.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/appointment.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/appointment.xlsx new file mode 100644 index 00000000000..66345147640 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/appointment.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/appointmentresponse.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/appointmentresponse.xlsx new file mode 100644 index 00000000000..0acdbcfdb1c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/appointmentresponse.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/auditevent.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/auditevent.xlsx new file mode 100644 index 00000000000..82112b266c4 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/auditevent.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/basic.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/basic.xlsx new file mode 100644 index 00000000000..dc7aae70939 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/basic.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/binary.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/binary.xlsx new file mode 100644 index 00000000000..d13ccbaaa52 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/binary.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/biologicallyderivedproduct.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/biologicallyderivedproduct.xlsx new file mode 100644 index 00000000000..62cb50e843c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/biologicallyderivedproduct.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/bodystructure.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/bodystructure.xlsx new file mode 100644 index 00000000000..3901089f1e8 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/bodystructure.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/bundle.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/bundle.xlsx new file mode 100644 index 00000000000..24d348541ee Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/bundle.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/capabilitystatement.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/capabilitystatement.xlsx new file mode 100644 index 00000000000..208161f9f79 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/capabilitystatement.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/careplan.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/careplan.xlsx new file mode 100644 index 00000000000..b5c49c55b52 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/careplan.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/careteam.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/careteam.xlsx new file mode 100644 index 00000000000..9cfd0ba2daa Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/careteam.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/catalogentry.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/catalogentry.xlsx new file mode 100644 index 00000000000..d9fa4926f9c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/catalogentry.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/chargeitem.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/chargeitem.xlsx new file mode 100644 index 00000000000..5b579ec5a8c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/chargeitem.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/chargeitemdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/chargeitemdefinition.xlsx new file mode 100644 index 00000000000..ad073f1ceb1 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/chargeitemdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/citation.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/citation.xlsx new file mode 100644 index 00000000000..57e3545b209 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/citation.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/claim.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/claim.xlsx new file mode 100644 index 00000000000..638e44a2f67 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/claim.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/claimresponse.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/claimresponse.xlsx new file mode 100644 index 00000000000..23a4754b9f3 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/claimresponse.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/clinicalimpression.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/clinicalimpression.xlsx new file mode 100644 index 00000000000..5b82b27e08a Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/clinicalimpression.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/clinicalusedefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/clinicalusedefinition.xlsx new file mode 100644 index 00000000000..46bdd5d95a2 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/clinicalusedefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/codesystem.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/codesystem.xlsx new file mode 100644 index 00000000000..c563da4adfa Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/codesystem.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/communication.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/communication.xlsx new file mode 100644 index 00000000000..df49b44e793 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/communication.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/communicationrequest.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/communicationrequest.xlsx new file mode 100644 index 00000000000..1de1dc831f4 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/communicationrequest.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/compartmentdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/compartmentdefinition.xlsx new file mode 100644 index 00000000000..384cc63cab2 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/compartmentdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/composition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/composition.xlsx new file mode 100644 index 00000000000..2bbd76acd0f Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/composition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/conceptmap.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/conceptmap.xlsx new file mode 100644 index 00000000000..922e736b231 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/conceptmap.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/condition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/condition.xlsx new file mode 100644 index 00000000000..adf9a68ef71 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/condition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/consent.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/consent.xlsx new file mode 100644 index 00000000000..b6dcf5ee0dd Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/consent.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/contract.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/contract.xlsx new file mode 100644 index 00000000000..d9cd1d45a8d Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/contract.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/coverage.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/coverage.xlsx new file mode 100644 index 00000000000..464a40b5351 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/coverage.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/coverageeligibilityrequest.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/coverageeligibilityrequest.xlsx new file mode 100644 index 00000000000..7e8a1552f67 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/coverageeligibilityrequest.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/coverageeligibilityresponse.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/coverageeligibilityresponse.xlsx new file mode 100644 index 00000000000..7da40b6a01b Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/coverageeligibilityresponse.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/detectedissue.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/detectedissue.xlsx new file mode 100644 index 00000000000..0ce084f9c89 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/detectedissue.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/device.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/device.xlsx new file mode 100644 index 00000000000..6e5fe7a4c78 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/device.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/devicedefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/devicedefinition.xlsx new file mode 100644 index 00000000000..a93a9be88e3 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/devicedefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/devicemetric.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/devicemetric.xlsx new file mode 100644 index 00000000000..37d240e2f5a Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/devicemetric.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/devicerequest.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/devicerequest.xlsx new file mode 100644 index 00000000000..3bab42885ab Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/devicerequest.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/deviceusestatement.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/deviceusestatement.xlsx new file mode 100644 index 00000000000..6ad38f0d283 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/deviceusestatement.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/diagnosticreport.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/diagnosticreport.xlsx new file mode 100644 index 00000000000..705191e362e Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/diagnosticreport.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/documentmanifest.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/documentmanifest.xlsx new file mode 100644 index 00000000000..48e191b65fa Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/documentmanifest.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/documentreference.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/documentreference.xlsx new file mode 100644 index 00000000000..c02c9421726 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/documentreference.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/encounter.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/encounter.xlsx new file mode 100644 index 00000000000..4b382c935ec Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/encounter.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/endpoint.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/endpoint.xlsx new file mode 100644 index 00000000000..bb14d2dbdc5 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/endpoint.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/enrollmentrequest.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/enrollmentrequest.xlsx new file mode 100644 index 00000000000..95d3af3d31f Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/enrollmentrequest.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/enrollmentresponse.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/enrollmentresponse.xlsx new file mode 100644 index 00000000000..69a0f16737c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/enrollmentresponse.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/episodeofcare.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/episodeofcare.xlsx new file mode 100644 index 00000000000..84ee4378c39 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/episodeofcare.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/eventdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/eventdefinition.xlsx new file mode 100644 index 00000000000..2b397c0636e Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/eventdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/evidence.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/evidence.xlsx new file mode 100644 index 00000000000..40e00c97dea Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/evidence.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/evidencereport.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/evidencereport.xlsx new file mode 100644 index 00000000000..d0958fdd7b5 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/evidencereport.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/evidencevariable.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/evidencevariable.xlsx new file mode 100644 index 00000000000..71a903cfdc3 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/evidencevariable.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/examplescenario.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/examplescenario.xlsx new file mode 100644 index 00000000000..36d6c8aac84 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/examplescenario.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/explanationofbenefit.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/explanationofbenefit.xlsx new file mode 100644 index 00000000000..b0d2792469f Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/explanationofbenefit.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/familymemberhistory.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/familymemberhistory.xlsx new file mode 100644 index 00000000000..1744c8a0f2c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/familymemberhistory.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/flag.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/flag.xlsx new file mode 100644 index 00000000000..b158d70a455 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/flag.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/goal.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/goal.xlsx new file mode 100644 index 00000000000..0793ed84c1c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/goal.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/graphdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/graphdefinition.xlsx new file mode 100644 index 00000000000..2c06e7c8e06 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/graphdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/group.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/group.xlsx new file mode 100644 index 00000000000..aa855247dae Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/group.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/guidanceresponse.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/guidanceresponse.xlsx new file mode 100644 index 00000000000..a237b2524a9 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/guidanceresponse.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/healthcareservice.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/healthcareservice.xlsx new file mode 100644 index 00000000000..341b0760a50 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/healthcareservice.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/imagingstudy.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/imagingstudy.xlsx new file mode 100644 index 00000000000..534e952619a Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/imagingstudy.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/immunization.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/immunization.xlsx new file mode 100644 index 00000000000..02b19408916 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/immunization.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/immunizationevaluation.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/immunizationevaluation.xlsx new file mode 100644 index 00000000000..476d58b576b Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/immunizationevaluation.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/immunizationrecommendation.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/immunizationrecommendation.xlsx new file mode 100644 index 00000000000..fba7b200f5c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/immunizationrecommendation.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/implementationguide.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/implementationguide.xlsx new file mode 100644 index 00000000000..481ca9c172c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/implementationguide.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/ingredient.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/ingredient.xlsx new file mode 100644 index 00000000000..3719b70fc2b Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/ingredient.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/insuranceplan.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/insuranceplan.xlsx new file mode 100644 index 00000000000..0ed301c296d Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/insuranceplan.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/invoice.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/invoice.xlsx new file mode 100644 index 00000000000..de61646bdfe Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/invoice.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/library.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/library.xlsx new file mode 100644 index 00000000000..740dde4d8b2 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/library.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/linkage.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/linkage.xlsx new file mode 100644 index 00000000000..4ad5b73ed89 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/linkage.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/list.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/list.xlsx new file mode 100644 index 00000000000..c330e82ff43 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/list.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/location.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/location.xlsx new file mode 100644 index 00000000000..e009cba15da Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/location.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/manufactureditemdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/manufactureditemdefinition.xlsx new file mode 100644 index 00000000000..3fd91781878 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/manufactureditemdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/measure.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/measure.xlsx new file mode 100644 index 00000000000..e5941546527 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/measure.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/measurereport.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/measurereport.xlsx new file mode 100644 index 00000000000..77f20fb64be Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/measurereport.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/media.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/media.xlsx new file mode 100644 index 00000000000..0bd400728d9 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/media.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/medication.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/medication.xlsx new file mode 100644 index 00000000000..6add392b3b7 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/medication.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/medicationadministration.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationadministration.xlsx new file mode 100644 index 00000000000..9089592317d Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationadministration.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/medicationdispense.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationdispense.xlsx new file mode 100644 index 00000000000..e8f25f02432 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationdispense.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/medicationknowledge.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationknowledge.xlsx new file mode 100644 index 00000000000..2e32837302e Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationknowledge.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/medicationrequest.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationrequest.xlsx new file mode 100644 index 00000000000..f1bdc96af45 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationrequest.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/medicationstatement.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationstatement.xlsx new file mode 100644 index 00000000000..356779c795f Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/medicationstatement.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/medicinalproductdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/medicinalproductdefinition.xlsx new file mode 100644 index 00000000000..875e9f1cc17 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/medicinalproductdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/messagedefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/messagedefinition.xlsx new file mode 100644 index 00000000000..db0b9cbdc32 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/messagedefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/messageheader.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/messageheader.xlsx new file mode 100644 index 00000000000..776bcf36aad Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/messageheader.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/molecularsequence.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/molecularsequence.xlsx new file mode 100644 index 00000000000..2f34e84209e Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/molecularsequence.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/namingsystem.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/namingsystem.xlsx new file mode 100644 index 00000000000..d1504729161 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/namingsystem.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/nutritionorder.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/nutritionorder.xlsx new file mode 100644 index 00000000000..b0d9313cd4b Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/nutritionorder.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/nutritionproduct.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/nutritionproduct.xlsx new file mode 100644 index 00000000000..c1fa5422d37 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/nutritionproduct.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/observation.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/observation.xlsx new file mode 100644 index 00000000000..246f4c80eb7 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/observation.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/observationdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/observationdefinition.xlsx new file mode 100644 index 00000000000..609ec00e6f2 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/observationdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/operationdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/operationdefinition.xlsx new file mode 100644 index 00000000000..e0a69b455a4 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/operationdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/operationoutcome.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/operationoutcome.xlsx new file mode 100644 index 00000000000..12c2bd110ac Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/operationoutcome.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/organization.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/organization.xlsx new file mode 100644 index 00000000000..f0fa969627d Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/organization.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/organizationaffiliation.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/organizationaffiliation.xlsx new file mode 100644 index 00000000000..3c6bf4355c8 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/organizationaffiliation.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/packagedproductdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/packagedproductdefinition.xlsx new file mode 100644 index 00000000000..dc04c7d70bb Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/packagedproductdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/patient.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/patient.xlsx new file mode 100644 index 00000000000..bdd148e6530 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/patient.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/paymentnotice.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/paymentnotice.xlsx new file mode 100644 index 00000000000..73dbbb44f3e Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/paymentnotice.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/paymentreconciliation.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/paymentreconciliation.xlsx new file mode 100644 index 00000000000..9b95044cf40 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/paymentreconciliation.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/person.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/person.xlsx new file mode 100644 index 00000000000..6fffc4c4f6f Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/person.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/plandefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/plandefinition.xlsx new file mode 100644 index 00000000000..d279c5be237 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/plandefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/practitioner.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/practitioner.xlsx new file mode 100644 index 00000000000..df20bab99a9 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/practitioner.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/practitionerrole.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/practitionerrole.xlsx new file mode 100644 index 00000000000..3fed7cd7d57 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/practitionerrole.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/procedure.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/procedure.xlsx new file mode 100644 index 00000000000..c6a6256b335 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/procedure.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/provenance.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/provenance.xlsx new file mode 100644 index 00000000000..18e011e3c29 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/provenance.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/questionnaire.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/questionnaire.xlsx new file mode 100644 index 00000000000..a1b9001dad3 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/questionnaire.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/questionnaireresponse.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/questionnaireresponse.xlsx new file mode 100644 index 00000000000..7a970d0cc0f Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/questionnaireresponse.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/regulatedauthorization.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/regulatedauthorization.xlsx new file mode 100644 index 00000000000..ec901262a1c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/regulatedauthorization.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/relatedperson.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/relatedperson.xlsx new file mode 100644 index 00000000000..cdffaaf8d33 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/relatedperson.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/requestgroup.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/requestgroup.xlsx new file mode 100644 index 00000000000..98829bce551 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/requestgroup.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/researchdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/researchdefinition.xlsx new file mode 100644 index 00000000000..1f6a34e1778 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/researchdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/researchelementdefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/researchelementdefinition.xlsx new file mode 100644 index 00000000000..9542787e935 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/researchelementdefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/researchstudy.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/researchstudy.xlsx new file mode 100644 index 00000000000..ce1911e440d Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/researchstudy.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/researchsubject.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/researchsubject.xlsx new file mode 100644 index 00000000000..23f84c2d200 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/researchsubject.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/riskassessment.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/riskassessment.xlsx new file mode 100644 index 00000000000..e62cba771c7 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/riskassessment.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/schedule.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/schedule.xlsx new file mode 100644 index 00000000000..8120831ddc1 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/schedule.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/searchparameter.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/searchparameter.xlsx new file mode 100644 index 00000000000..545da0a4bf8 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/searchparameter.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/servicerequest.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/servicerequest.xlsx new file mode 100644 index 00000000000..8f0d75cfbcf Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/servicerequest.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/slot.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/slot.xlsx new file mode 100644 index 00000000000..0b9f8e2b086 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/slot.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/specimen.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/specimen.xlsx new file mode 100644 index 00000000000..7e8bdba552b Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/specimen.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/specimendefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/specimendefinition.xlsx new file mode 100644 index 00000000000..c0efd14c7e8 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/specimendefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/structuredefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/structuredefinition.xlsx new file mode 100644 index 00000000000..fa46259b2b0 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/structuredefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/structuremap.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/structuremap.xlsx new file mode 100644 index 00000000000..c11596b5ebb Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/structuremap.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/subscription.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/subscription.xlsx new file mode 100644 index 00000000000..4d06d70482e Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/subscription.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/subscriptionstatus.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/subscriptionstatus.xlsx new file mode 100644 index 00000000000..aceeb5b0519 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/subscriptionstatus.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/subscriptiontopic.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/subscriptiontopic.xlsx new file mode 100644 index 00000000000..b53519ecb3a Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/subscriptiontopic.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/substance.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/substance.xlsx new file mode 100644 index 00000000000..e7393c0d1ac Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/substance.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/substancedefinition.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/substancedefinition.xlsx new file mode 100644 index 00000000000..2d21e159f30 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/substancedefinition.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/supplydelivery.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/supplydelivery.xlsx new file mode 100644 index 00000000000..32ab46509dd Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/supplydelivery.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/supplyrequest.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/supplyrequest.xlsx new file mode 100644 index 00000000000..6d0dfd2f887 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/supplyrequest.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/task.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/task.xlsx new file mode 100644 index 00000000000..680718d5f35 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/task.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/terminologycapabilities.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/terminologycapabilities.xlsx new file mode 100644 index 00000000000..db1aa965124 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/terminologycapabilities.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/testreport.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/testreport.xlsx new file mode 100644 index 00000000000..bbc73ce045e Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/testreport.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/testscript.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/testscript.xlsx new file mode 100644 index 00000000000..e4f4290483c Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/testscript.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/valueset.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/valueset.xlsx new file mode 100644 index 00000000000..ce01cc202c9 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/valueset.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/verificationresult.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/verificationresult.xlsx new file mode 100644 index 00000000000..2a46d71f25b Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/verificationresult.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/res/r4b/visionprescription.xlsx b/hapi-tinder-plugin/src/main/resources/res/r4b/visionprescription.xlsx new file mode 100644 index 00000000000..adeda759787 Binary files /dev/null and b/hapi-tinder-plugin/src/main/resources/res/r4b/visionprescription.xlsx differ diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm index c414c1dd0ed..a49165d01d2 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm @@ -25,10 +25,12 @@ import ca.uhn.fhir.rest.api.SearchContainedModeEnum; public class ${className}ResourceProvider extends ## We have specialized base classes for RPs that handle certain resource types. These ## RPs implement type specific operations -#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem' || ($version != 'dstu2' && ${className} == 'ConceptMap') || ${className} == 'MessageHeader' || ${className} == 'Composition' || ${className} == 'StructureDefinition' || ($version != 'dstu2' && ${className} == 'Observation') )) +#if ( ${className} == 'CodeSystem' || ${className} == 'Composition' || ${className} == 'Encounter' || ${className} == 'Observation' || ${className} == 'Patient' || ${className} == 'StructureDefinition' ) + ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider${className}<${className}> +#elseif ( $version != 'dstu' && (${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem' || ($version != 'dstu2' && ${className} == 'ConceptMap') || ${className} == 'Composition' || ${className} == 'StructureDefinition')) BaseJpaResourceProvider${className}${versionCapitalized} #else - JpaResourceProvider${versionCapitalized}<${className}> + ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider<${className}> #end { diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm index a38d63aa868..9f2613022df 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm @@ -65,10 +65,8 @@ FhirContext myFhirContext; IFhirResourceDaoCodeSystem #elseif ( ${versionCapitalized.startsWith('R')} && ${res.name} == 'CodeSystem' ) IFhirResourceDaoCodeSystem -#elseif ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'ConceptMap' ) - IFhirResourceDaoConceptMap -#elseif ( ${versionCapitalized.startsWith('R')} && ${res.name} == 'ConceptMap' ) - IFhirResourceDaoConceptMap +#elseif ( (${versionCapitalized.startsWith('R')} || ${versionCapitalized} == 'Dstu3') && (${res.name} == 'ConceptMap') ) + IFhirResourceDao${res.name} #elseif ( ${versionCapitalized} != 'Dstu1' && (${res.name} == 'Composition' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'SearchParameter' || ${res.name} == 'MessageHeader' || ${res.name} == 'StructureDefinition')) IFhirResourceDao${res.name}<${resourcePackage}.${res.declaringClassNameComplete}> #elseif ( ${versionCapitalized} != 'Dstu1' && ${versionCapitalized} != 'Dstu2' && (${res.name} == 'Observation')) @@ -78,13 +76,13 @@ FhirContext myFhirContext; #end dao${res.declaringClassNameComplete}${versionCapitalized}() { -#if ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'ConceptMap' ) - ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal; - retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}(); -#elseif ( ${versionCapitalized.startsWith('R')} && ${res.name} == 'ConceptMap' ) - ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal; - retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}(); -#elseif ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem' || ${res.name} == 'MessageHeader' || ${res.name} == 'Composition' || ${res.name} == 'StructureDefinition') +#if ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Patient' ) + ca.uhn.fhir.jpa.dao.JpaResourceDao${res.name}<${resourcePackage}.${res.declaringClassNameComplete}> retVal; + retVal = new ca.uhn.fhir.jpa.dao.JpaResourceDao${res.name}<>(); +#elseif ( (${versionCapitalized.startsWith('R')} || ${versionCapitalized} == 'Dstu3') && (${res.name} == 'ConceptMap') ) + ca.uhn.fhir.jpa.dao.JpaResourceDao${res.name} retVal; + retVal = new ca.uhn.fhir.jpa.dao.JpaResourceDao${res.name}<>(); +#elseif ( ${res.name} == 'Everything' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem' || ${res.name} == 'MessageHeader' || ${res.name} == 'Composition' || ${res.name} == 'StructureDefinition') ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal; retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}(); #elseif ( ${versionCapitalized} != 'Dstu1' && ${versionCapitalized} != 'Dstu2' && ${res.name} == 'Observation') diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 45a98ce830c..5f7d525988b 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 799667a2f6e..0eb566c8acb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io @@ -2014,7 +2014,7 @@ ca.uhn.hapi.fhir hapi-fhir-checkstyle - 6.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT @@ -2101,6 +2101,9 @@ org.apache.maven.plugins maven-failsafe-plugin 3.0.0-M5 + + true + org.apache.maven.plugins @@ -2187,12 +2190,13 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + 0.8.8 ca/uhn/fhir/model/dstu2/**/*.class ca/uhn/fhir/jpa/rp/r5/*.class ca/uhn/fhir/jpa/rp/r4/*.class + ca/uhn/fhir/jpa/rp/r4b/*.class ca/uhn/fhir/jpa/rp/dstu3/*.class ca/uhn/fhir/jpa/rp/dstu2/*.class @@ -2747,6 +2751,7 @@ hapi-fhir-jpaserver-test-dstu2 hapi-fhir-jpaserver-test-dstu3 hapi-fhir-jpaserver-test-r4 + hapi-fhir-jpaserver-test-r4b hapi-fhir-jpaserver-test-r5 hapi-fhir-jpaserver-elastic-test-utilities hapi-tinder-plugin @@ -2766,6 +2771,7 @@ hapi-fhir-validation-resources-dstu3 hapi-fhir-structures-r4 hapi-fhir-validation-resources-r4 + hapi-fhir-structures-r4b hapi-fhir-structures-r5 hapi-fhir-validation-resources-r5 hapi-fhir-jpa diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index 0dcb69fdb1b..12e9ebcbc84 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 649763bc4e4..8b86232f578 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-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 362c4409a6a..4f47843a1d4 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.2.0-PRE16-SNAPSHOT + 6.2.0-PRE17-SNAPSHOT ../../pom.xml