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 122caf5b490..e2219844ebf 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 @@ -497,9 +497,10 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { IIdType id = theResource.getIdElement(); RequestDetails requestDetails = createRequestDetails(); + String query = createSearchParameterMapFor(theResource).toNormalizedQueryString(myFhirContext); try { - outcome = theDao.update(theResource, requestDetails); + outcome = theDao.update(theResource, query, requestDetails); } catch (ResourceVersionConflictException exception) { final Optional optResource = readResourceById(theDao, id); 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 67ea60b3ecd..2f11323de13 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 @@ -18,6 +18,7 @@ import ca.uhn.fhir.jpa.searchparam.util.SearchParameterHelper; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.SimpleBundleProvider; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import ca.uhn.test.util.LogbackTestExtension; @@ -131,6 +132,8 @@ public class PackageInstallerSvcImplTest { private ArgumentCaptor mySearchParameterCaptor; @Captor private ArgumentCaptor myRequestDetailsCaptor; + @Captor + private ArgumentCaptor myMathcUrlCaptor; @Test public void testPackageCompatibility() { @@ -227,7 +230,43 @@ public class PackageInstallerSvcImplTest { } } + @Test + public void testDontTryToInstallDuplicateCodeSystem_CodeSystemAlreadyExistsWithSameIdButDifferentCanonicalUrl() throws IOException { + // Setup + // The CodeSystem that is already saved in the repository + CodeSystem existingCs = new CodeSystem(); + existingCs.setId("CodeSystem/existingcs"); + existingCs.setUrl("http://my-old-code-system"); + existingCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + + // A new code system in a package we're installing that has a + // different URL as the previously saved one, but the same ID. + CodeSystem newCs = new CodeSystem(); + newCs.setId("CodeSystem/existingcs"); + newCs.setUrl("http://my-new-code-system"); + newCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + + PackageInstallationSpec oldSpec = setupResourceInPackage(null, existingCs, myCodeSystemDao); + + // Test + mySvc.install(oldSpec); + + + PackageInstallationSpec newSpec = setupResourceInPackage(null, newCs, myCodeSystemDao); + when(myCodeSystemDao.update(any(), any(), any())).thenThrow(new ResourceVersionConflictException("Resource already exists with that ID but with a different URL")); + mySvc.install(newSpec); + + + // Verify + verify(myCodeSystemDao, times(2)).search(mySearchParameterMapCaptor.capture(), any()); + SearchParameterMap newMap = mySearchParameterMapCaptor.getValue(); + assertEquals("?url=http%3A%2F%2Fmy-new-code-system", newMap.toNormalizedQueryString(myCtx)); + + verify(myCodeSystemDao, times(2)).update(myCodeSystemCaptor.capture(), any(String.class) , any(RequestDetails.class)); + CodeSystem newCodeSystem = myCodeSystemCaptor.getValue(); + assertEquals("existingcs", newCodeSystem.getIdPart()); + } @Test @@ -257,7 +296,7 @@ public class PackageInstallerSvcImplTest { SearchParameterMap map = mySearchParameterMapCaptor.getValue(); assertEquals("?url=http%3A%2F%2Fmy-code-system", map.toNormalizedQueryString(myCtx)); - verify(myCodeSystemDao, times(1)).update(myCodeSystemCaptor.capture(), any(RequestDetails.class)); + verify(myCodeSystemDao, times(1)).update(myCodeSystemCaptor.capture(), any(String.class) , any(RequestDetails.class)); CodeSystem codeSystem = myCodeSystemCaptor.getValue(); assertEquals("existingcs", codeSystem.getIdPart()); } @@ -295,9 +334,9 @@ public class PackageInstallerSvcImplTest { if (theInstallType == InstallType.CREATE) { verify(mySearchParameterDao, times(1)).create(mySearchParameterCaptor.capture(), myRequestDetailsCaptor.capture()); } else if (theInstallType == InstallType.UPDATE_WITH_EXISTING){ - verify(mySearchParameterDao, times(2)).update(mySearchParameterCaptor.capture(), myRequestDetailsCaptor.capture()); + verify(mySearchParameterDao, times(2)).update(mySearchParameterCaptor.capture(), myMathcUrlCaptor.capture(), myRequestDetailsCaptor.capture()); } else { - verify(mySearchParameterDao, times(1)).update(mySearchParameterCaptor.capture(), myRequestDetailsCaptor.capture()); + verify(mySearchParameterDao, times(1)).update(mySearchParameterCaptor.capture(), myMathcUrlCaptor.capture(), myRequestDetailsCaptor.capture()); } Iterator iteratorSP = mySearchParameterCaptor.getAllValues().iterator(); @@ -366,6 +405,7 @@ public class PackageInstallerSvcImplTest { if (theId != null) { searchParameter.setId(new IdType("SearchParameter", theId)); } + searchParameter.setUrl("http://example.com/SearchParameter"); searchParameter.setCode("someCode"); theBase.forEach(base -> searchParameter.getBase().add(new CodeType(base))); searchParameter.setExpression("someExpression");