From 1a03d41db14ce3e1c1e8caf07a89cf493bdaa8a2 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Tue, 14 Oct 2025 18:43:04 +0200 Subject: [PATCH 1/2] removed the option to be able to install embedded resources --- hapi-fhir-android/.gitignore | 1 + .../jpa/packages/PackageInstallerSvcImpl.java | 37 +++++++++++++ .../packages/PackageInstallerSvcImplTest.java | 52 +++++++++++++++---- 3 files changed, 81 insertions(+), 9 deletions(-) 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..25d0a9271f74 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; @@ -565,6 +566,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 +585,34 @@ boolean validForUpload(IBaseResource theResource) { return true; } + private final List embeddedCodeSystems = List.of( + CommonCodeSystemsTerminologyService.LANGUAGES_CODESYSTEM_URL, + CommonCodeSystemsTerminologyService.MIMETYPES_CODESYSTEM_URL, + CommonCodeSystemsTerminologyService.CURRENCIES_CODESYSTEM_URL, + CommonCodeSystemsTerminologyService.COUNTRIES_CODESYSTEM_URL, + CommonCodeSystemsTerminologyService.UCUM_CODESYSTEM_URL, + CommonCodeSystemsTerminologyService.USPS_CODESYSTEM_URL); + + private final List embeddedValueSets = List.of( + CommonCodeSystemsTerminologyService.LANGUAGES_VALUESET_URL, + CommonCodeSystemsTerminologyService.MIMETYPES_VALUESET_URL, + CommonCodeSystemsTerminologyService.CURRENCIES_VALUESET_URL, + CommonCodeSystemsTerminologyService.UCUM_VALUESET_URL, + CommonCodeSystemsTerminologyService.ALL_LANGUAGES_VALUESET_URL, + CommonCodeSystemsTerminologyService.USPS_VALUESET_URL); + + private boolean isEmbeddedValueSet(IBaseResource theResource) { + org.hl7.fhir.r4.model.ValueSet valueSet = myVersionCanonicalizer.valueSetToCanonical(theResource); + if (!valueSet.hasUrl()) return false; + return embeddedValueSets.contains(valueSet.getUrl()); + } + + private boolean isEmbeddedCodeSystem(IBaseResource theResource) { + org.hl7.fhir.r4.model.CodeSystem codeSystem = myVersionCanonicalizer.codeSystemToCanonical(theResource); + if (!codeSystem.hasUrl()) return false; + return embeddedCodeSystems.contains(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..bd382854ca4c 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; @@ -166,7 +168,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 +182,38 @@ public void testValidForUpload_WhenStatusValidationSettingIsEnabled_ValidatesRes boolean theExpectedResultForStatusValidation) { if (theResource.fhirType().equals("SearchParameter")) { setupSearchParameterValidationMocksForSuccess(); + when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(true); } - 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); + } + 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 +252,6 @@ public void testValidForUpload_WhenSearchParameterValidatorThrowsAnExceptionOthe } } - - - @Test public void testDontTryToInstallDuplicateCodeSystem_CodeSystemAlreadyExistsWithDifferentId() throws IOException { // Setup @@ -249,6 +271,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 +421,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); + } + + } From 3b878ff65491ef72d54a95ed87f80dd30a3fe3cb Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Wed, 15 Oct 2025 18:24:00 +0200 Subject: [PATCH 2/2] adjusted to reuse existing methods --- .../jpa/packages/PackageInstallerSvcImpl.java | 24 +++++-------------- .../packages/PackageInstallerSvcImplTest.java | 3 ++- 2 files changed, 8 insertions(+), 19 deletions(-) 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 25d0a9271f74..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 @@ -130,6 +130,9 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { @Autowired private VersionCanonicalizer myVersionCanonicalizer; + @Autowired + private CommonCodeSystemsTerminologyService myCommonCodeSystemsTerminologyService; + /** * Constructor */ @@ -585,32 +588,17 @@ boolean validForUpload(IBaseResource theResource) { return true; } - private final List embeddedCodeSystems = List.of( - CommonCodeSystemsTerminologyService.LANGUAGES_CODESYSTEM_URL, - CommonCodeSystemsTerminologyService.MIMETYPES_CODESYSTEM_URL, - CommonCodeSystemsTerminologyService.CURRENCIES_CODESYSTEM_URL, - CommonCodeSystemsTerminologyService.COUNTRIES_CODESYSTEM_URL, - CommonCodeSystemsTerminologyService.UCUM_CODESYSTEM_URL, - CommonCodeSystemsTerminologyService.USPS_CODESYSTEM_URL); - - private final List embeddedValueSets = List.of( - CommonCodeSystemsTerminologyService.LANGUAGES_VALUESET_URL, - CommonCodeSystemsTerminologyService.MIMETYPES_VALUESET_URL, - CommonCodeSystemsTerminologyService.CURRENCIES_VALUESET_URL, - CommonCodeSystemsTerminologyService.UCUM_VALUESET_URL, - CommonCodeSystemsTerminologyService.ALL_LANGUAGES_VALUESET_URL, - CommonCodeSystemsTerminologyService.USPS_VALUESET_URL); - private boolean isEmbeddedValueSet(IBaseResource theResource) { org.hl7.fhir.r4.model.ValueSet valueSet = myVersionCanonicalizer.valueSetToCanonical(theResource); if (!valueSet.hasUrl()) return false; - return embeddedValueSets.contains(valueSet.getUrl()); + 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 embeddedCodeSystems.contains(codeSystem.getUrl()); + return myCommonCodeSystemsTerminologyService.isCodeSystemSupported(null, codeSystem.getUrl()); } private boolean isValidSearchParameter(IBaseResource theResource) { 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 bd382854ca4c..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 @@ -120,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;