diff --git a/hapi-fhir-android/.gitignore b/hapi-fhir-android/.gitignore index e69de29bb2d1..8b137891791f 100644 --- a/hapi-fhir-android/.gitignore +++ b/hapi-fhir-android/.gitignore @@ -0,0 +1 @@ + diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java index 5b4b7d65c10b..a826e990f609 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java @@ -57,6 +57,7 @@ import com.google.common.annotations.VisibleForTesting; import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; +import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -129,6 +130,9 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { @Autowired private VersionCanonicalizer myVersionCanonicalizer; + @Autowired + private CommonCodeSystemsTerminologyService myCommonCodeSystemsTerminologyService; + /** * Constructor */ @@ -565,6 +569,14 @@ boolean validForUpload(IBaseResource theResource) { return false; } + if ("CodeSystem".equals(resourceType) && isEmbeddedCodeSystem(theResource)) { + return false; + } + + if ("ValueSet".equals(resourceType) && isEmbeddedValueSet(theResource)) { + return false; + } + if (!isValidResourceStatusForPackageUpload(theResource)) { ourLog.warn( "Failed to validate resource of type {} with ID {} - Error: Resource status not accepted value.", @@ -576,6 +588,19 @@ boolean validForUpload(IBaseResource theResource) { return true; } + private boolean isEmbeddedValueSet(IBaseResource theResource) { + org.hl7.fhir.r4.model.ValueSet valueSet = myVersionCanonicalizer.valueSetToCanonical(theResource); + if (!valueSet.hasUrl()) return false; + return myCommonCodeSystemsTerminologyService.isValueSetSupported(null, valueSet.getUrl()); + } + + private boolean isEmbeddedCodeSystem(IBaseResource theResource) { + + org.hl7.fhir.r4.model.CodeSystem codeSystem = myVersionCanonicalizer.codeSystemToCanonical(theResource); + if (!codeSystem.hasUrl()) return false; + return myCommonCodeSystemsTerminologyService.isCodeSystemSupported(null, codeSystem.getUrl()); + } + private boolean isValidSearchParameter(IBaseResource theResource) { try { org.hl7.fhir.r5.model.SearchParameter searchParameter = diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java index 8357c11618e5..f3b2825a6662 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java @@ -24,6 +24,7 @@ import ca.uhn.test.util.LogbackTestExtensionAssert; import ch.qos.logback.classic.Logger; import jakarta.annotation.Nonnull; +import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeType; @@ -34,6 +35,7 @@ import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.SearchParameter; import org.hl7.fhir.r4.model.Subscription; +import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.npm.PackageGenerator; import org.junit.jupiter.api.Nested; @@ -118,7 +120,8 @@ public class PackageInstallerSvcImplTest { private PackageResourceParsingSvc myPackageResourceParsingSvc = new PackageResourceParsingSvc(myCtx); @Spy private PartitionSettings myPartitionSettings = new PartitionSettings(); - + @Spy + private CommonCodeSystemsTerminologyService myCommonCodeSystemsTerminologyService = new CommonCodeSystemsTerminologyService(myCtx); @InjectMocks private PackageInstallerSvcImpl mySvc; @@ -166,7 +169,12 @@ public static Stream parametersIsValidForUpload() { arguments(createDocumentReference(null), false), arguments(createCommunication(Communication.CommunicationStatus.NOTDONE), true), arguments(createCommunication(Communication.CommunicationStatus.NULL), false), - arguments(createCommunication(null), false)); + arguments(createCommunication(null), false), + arguments(createValueSet("http://test/VS"),true), + arguments(createCodeSystem("http://test/CS"),true), + arguments(createValueSet(CommonCodeSystemsTerminologyService.LANGUAGES_VALUESET_URL),false), + arguments(createCodeSystem(CommonCodeSystemsTerminologyService.LANGUAGES_CODESYSTEM_URL),false) + ); } @ParameterizedTest @@ -175,20 +183,38 @@ public void testValidForUpload_WhenStatusValidationSettingIsEnabled_ValidatesRes boolean theExpectedResultForStatusValidation) { if (theResource.fhirType().equals("SearchParameter")) { setupSearchParameterValidationMocksForSuccess(); + when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(true); + } + else if (theResource.fhirType().equals("CodeSystem")) { + when(myVersionCanonicalizerMock.codeSystemToCanonical(any())).thenReturn((CodeSystem) theResource); + } + else if (theResource.fhirType().equals("ValueSet")) { + when(myVersionCanonicalizerMock.valueSetToCanonical(any())).thenReturn((ValueSet) theResource); } - when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(true); + else + when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(true); + assertEquals(theExpectedResultForStatusValidation, mySvc.validForUpload(theResource)); } @ParameterizedTest @MethodSource(value = "parametersIsValidForUpload") - public void testValidForUpload_WhenStatusValidationSettingIsDisabled_DoesNotValidateResourceStatus(IBaseResource theResource) { + public void testValidForUpload_WhenStatusValidationSettingIsDisabled_DoesNotValidateResourceStatus(IBaseResource theResource, boolean theExpectedResultForStatusValidation) { if (theResource.fhirType().equals("SearchParameter")) { setupSearchParameterValidationMocksForSuccess(); + when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(false); + assertTrue(mySvc.validForUpload(theResource)); + } + else if (theResource.fhirType().equals("CodeSystem")) { + when(myVersionCanonicalizerMock.codeSystemToCanonical(any())).thenReturn((CodeSystem) theResource); + assertEquals(theExpectedResultForStatusValidation, mySvc.validForUpload(theResource)); } - when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(false); - //all resources should pass status validation in this case, so expect true always - assertTrue(mySvc.validForUpload(theResource)); + else if (theResource.fhirType().equals("ValueSet")) { + when(myVersionCanonicalizerMock.valueSetToCanonical(any())).thenReturn((ValueSet) theResource); + assertEquals(theExpectedResultForStatusValidation, mySvc.validForUpload(theResource)); + } + else + assertTrue(mySvc.validForUpload(theResource)); } @Test @@ -227,9 +253,6 @@ public void testValidForUpload_WhenSearchParameterValidatorThrowsAnExceptionOthe } } - - - @Test public void testDontTryToInstallDuplicateCodeSystem_CodeSystemAlreadyExistsWithDifferentId() throws IOException { // Setup @@ -249,6 +272,9 @@ public void testDontTryToInstallDuplicateCodeSystem_CodeSystemAlreadyExistsWithD PackageInstallationSpec spec = setupResourceInPackage(existingCs, cs, myCodeSystemDao); + when(myVersionCanonicalizerMock.codeSystemToCanonical(any())).thenReturn(cs); + + when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(true); // Test mySvc.install(spec); @@ -396,4 +422,13 @@ private static Communication createCommunication(Communication.CommunicationStat return communication; } + private static CodeSystem createCodeSystem(String canonicalUrl) { + return new CodeSystem().setUrl(canonicalUrl).setStatus(Enumerations.PublicationStatus.ACTIVE); + } + + private static ValueSet createValueSet(String canonicalUrl) { + return new ValueSet().setUrl(canonicalUrl).setStatus(Enumerations.PublicationStatus.ACTIVE); + } + + }