From 756432e61ac60b3ee2e3924a92838f4a0d5b3350 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Wed, 21 Oct 2020 15:47:31 -0400 Subject: [PATCH 1/3] Enable package installer to load resources other than conformance resources. --- .../jpa/packages/PackageInstallerSvcImpl.java | 25 ++++++++++++- .../ca/uhn/fhir/jpa/packages/NpmTestR4.java | 35 ++++++++++++++++++ .../packages/test-organizations-package.tgz | Bin 0 -> 869 bytes 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/packages/test-organizations-package.tgz 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 46256602d0a..4cb69ff28d9 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 @@ -20,6 +20,9 @@ package ca.uhn.fhir.jpa.packages; * #L% */ +import ca.uhn.fhir.context.BaseRuntimeChildDefinition; +import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; +import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; @@ -46,6 +49,7 @@ import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.utilities.cache.IPackageCacheManager; import org.hl7.fhir.utilities.cache.NpmPackage; import org.slf4j.Logger; @@ -384,9 +388,12 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } else if (resource.getClass().getSimpleName().equals("Subscription")) { String id = extractIdFromSubscription(resource); return SearchParameterMap.newSynchronous().add("_id", new TokenParam(id)); - } else { + } else if (resourceHasUrlElement(resource)) { String url = extractUniqueUrlFromMetadataResource(resource); return SearchParameterMap.newSynchronous().add("url", new UriParam(url)); + } else { + TokenParam identifierToken = extractIdentifierFromOtherResourceTypes(resource); + return SearchParameterMap.newSynchronous().add("identifier", identifierToken); } } @@ -412,6 +419,22 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { return (String) asPrimitiveType.getValue(); } + private TokenParam extractIdentifierFromOtherResourceTypes(IBaseResource resource) { + FhirTerser terser = myFhirContext.newTerser(); + Identifier myIdentifier = (Identifier) terser.getSingleValueOrNull(resource, "identifier"); + return new TokenParam(myIdentifier.getSystem(), myIdentifier.getValue()); + } + + private boolean resourceHasUrlElement(IBaseResource resource) { + BaseRuntimeElementDefinition def = myFhirContext.getElementDefinition(resource.getClass()); + if (!(def instanceof BaseRuntimeElementCompositeDefinition)) { + throw new IllegalArgumentException("Resource is not a composite type: " + resource.getClass().getName()); + } + BaseRuntimeElementCompositeDefinition currentDef = (BaseRuntimeElementCompositeDefinition) def; + BaseRuntimeChildDefinition nextDef = currentDef.getChildByName("url"); + return nextDef != null; + } + @VisibleForTesting void setFhirContextForUnitTest(FhirContext theCtx) { myFhirContext = theCtx; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java index bee6c8ffbec..ac78da94fb6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java @@ -48,6 +48,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -236,6 +237,40 @@ public class NpmTestR4 extends BaseJpaR4Test { }); } + @Test + public void testInstallR4Package_NonConformanceResources() throws Exception { + myDaoConfig.setAllowExternalReferences(true); + + byte[] bytes = loadClasspathBytes("/packages/test-organizations-package.tgz"); + myFakeNpmServlet.myResponses.put("/test-organizations/1.0.0", bytes); + + List resourceList = new ArrayList<>(); + resourceList.add("Organization"); + PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-organizations").setVersion("1.0.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); + spec.setInstallResourceTypes(resourceList); + PackageInstallOutcomeJson outcome = igInstaller.install(spec); + assertEquals(3, outcome.getResourcesInstalled().get("Organization")); + + // Be sure no further communication with the server + JettyUtil.closeServer(myServer); + + // Search for the installed resources + runInTransaction(() -> { + SearchParameterMap map = SearchParameterMap.newSynchronous(); + map.add(Organization.SP_IDENTIFIER, new TokenParam("https://github.com/synthetichealth/synthea", "organization1")); + IBundleProvider result = myOrganizationDao.search(map); + assertEquals(1, result.sizeOrThrowNpe()); + map = SearchParameterMap.newSynchronous(); + map.add(Organization.SP_IDENTIFIER, new TokenParam("https://github.com/synthetichealth/synthea", "organization2")); + result = myOrganizationDao.search(map); + assertEquals(1, result.sizeOrThrowNpe()); + map = SearchParameterMap.newSynchronous(); + map.add(Organization.SP_IDENTIFIER, new TokenParam("https://github.com/synthetichealth/synthea", "organization3")); + result = myOrganizationDao.search(map); + assertEquals(1, result.sizeOrThrowNpe()); + }); + + } @Test public void testInstallR4Package_DraftResourcesNotInstalled() throws Exception { diff --git a/hapi-fhir-jpaserver-base/src/test/resources/packages/test-organizations-package.tgz b/hapi-fhir-jpaserver-base/src/test/resources/packages/test-organizations-package.tgz new file mode 100644 index 0000000000000000000000000000000000000000..a3f53110d0c5b5968591bdbc2f42b954d0973868 GIT binary patch literal 869 zcmV-r1DgCFiwFRYS&&}<1MOGebDKC2&U60?4bR1vkPy%}pTk`;b?l7oB zEd~#WOx;ZW_X^=3^y2#JY3j5M-vhE&tJO*?eY;vP{*lc@_v9x6Lg?5w&@HW7Xpy>I z0U+!U0&QX|1&9c;$O*82rc-N_g=D#MC1&ZIb}# zOXJT>##xFwABrq}ygAfLe`Ha*i9fcfioatM=LEpVbK%=~=JCIK0f3Qt*dMjTXfOgW?>56pmt$i%`9O_zGy3?U1l3q9^~vep-=jKf&uYTR2T z0pL!5RbR0zq?lLsA}JTet8RA|%c#8R@NC{KmZ^+{jCmwjBBRxRrmgDHw=5~OlUeh` z8oK*lec$Q%8JF>Gr6%)IXo;-X_8(t(7R2dnBX9QY@Kw1gMLv(yEXiiePLwzu|=$IW?X3eUWFkU-txYu}Ijm>O9jc=PVcCQt7F!%#Dre{p#1d&4x(v zN$wrC-IbKh_4JqhOGG?^tvXmN2-FEE9_~&Or{b%xHfHMXNKtoeg^Nsylm2Q)poNHQI?!^r8ayjgnQMzzlg3(J+&}fL`E#&~|E(kQ zzdDE;{wD-!{>QZC|6|}F{tu{U3CqPMbTKxu1(E3r+crt)QkQugI+#8Q|648pAHx4< zy}{t3H+~Jy{N7-C1}1MNQ~#oloD_*rf!G(eMU{Ma>DfA}^{`pj1!?c3>?n^Q4l5aUv8kmW2q*xNOBU)NT) zEMO32OqcG@=DPiI*j4}GW7mJAasMH1>%Sx7p!&~)JXFs#ZcEEOA=|Od%g>1Vm16#Cc@r>~WE4kA!02}}STAR7B literal 0 HcmV?d00001 From 830ad700898a1076b0d16674b9eba3b92b54bb56 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Wed, 21 Oct 2020 16:18:10 -0400 Subject: [PATCH 2/3] Add changelog for package installer enhancement. --- ...nable-non-conformance-resources-in-package-installer.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2141-enable-non-conformance-resources-in-package-installer.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2141-enable-non-conformance-resources-in-package-installer.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2141-enable-non-conformance-resources-in-package-installer.yaml new file mode 100644 index 00000000000..040775c632d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2141-enable-non-conformance-resources-in-package-installer.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 2141 +title: "The Package Installer has been enhanced to allow resources that do not have a url element to be loaded + from a package. Resources without url will instead use identifier element." From 115b09e4d4dd0bc0bbc0a97444f98b28f0bd0996 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Wed, 21 Oct 2020 17:23:40 -0400 Subject: [PATCH 3/3] Throw exception if any resources in package have no url and no identifier. --- .../jpa/packages/PackageInstallerSvcImpl.java | 8 +++++-- .../ca/uhn/fhir/jpa/packages/NpmTestR4.java | 21 +++++++++++++++++- .../test-missing-identifier-package.tgz | Bin 0 -> 1204 bytes 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/packages/test-missing-identifier-package.tgz 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 4cb69ff28d9..0433f4da73e 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 @@ -421,8 +421,12 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { private TokenParam extractIdentifierFromOtherResourceTypes(IBaseResource resource) { FhirTerser terser = myFhirContext.newTerser(); - Identifier myIdentifier = (Identifier) terser.getSingleValueOrNull(resource, "identifier"); - return new TokenParam(myIdentifier.getSystem(), myIdentifier.getValue()); + Identifier identifier = (Identifier) terser.getSingleValueOrNull(resource, "identifier"); + if (identifier != null) { + return new TokenParam(identifier.getSystem(), identifier.getValue()); + } else { + throw new UnsupportedOperationException("Resources in a package must have a url or identifier to be loaded by the package installer."); + } } private boolean resourceHasUrlElement(IBaseResource resource) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java index ac78da94fb6..0c8726f47cb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java @@ -11,7 +11,6 @@ import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity; import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity; import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.ReferenceParam; @@ -62,6 +61,7 @@ import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; public class NpmTestR4 extends BaseJpaR4Test { @@ -272,6 +272,25 @@ public class NpmTestR4 extends BaseJpaR4Test { } + @Test + public void testInstallR4Package_NoIdentifierNoUrl() throws Exception { + myDaoConfig.setAllowExternalReferences(true); + + byte[] bytes = loadClasspathBytes("/packages/test-missing-identifier-package.tgz"); + myFakeNpmServlet.myResponses.put("/test-organizations/1.0.0", bytes); + + List resourceList = new ArrayList<>(); + resourceList.add("Organization"); + PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-organizations").setVersion("1.0.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); + spec.setInstallResourceTypes(resourceList); + try { + PackageInstallOutcomeJson outcome = igInstaller.install(spec); + fail(); + } catch (ImplementationGuideInstallationException theE) { + assertThat(theE.getMessage(), containsString("Resources in a package must have a url or identifier to be loaded by the package installer.")); + } + } + @Test public void testInstallR4Package_DraftResourcesNotInstalled() throws Exception { myDaoConfig.setAllowExternalReferences(true); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/packages/test-missing-identifier-package.tgz b/hapi-fhir-jpaserver-base/src/test/resources/packages/test-missing-identifier-package.tgz new file mode 100644 index 0000000000000000000000000000000000000000..46025afc0a2ba2d6b487928f819ebb1b5233fb3e GIT binary patch literal 1204 zcmV;l1WWrLiwFQ1qmW+!1MOMeZsSB04oC>mE8qg8y|TjX+L`hE*j{NbLTYHWX%Z!A z0ijhjwa1NF$Byht)l!ugU@v$95aJ1VHlBd7oiu40l15Dv+WM26amL5vGjrygIvUt@zoz_oxSVwiB(WrBaz>ZF4I{Po*$+P=W=KfBjF3(UpX|6A;Q zk29CYmG#GgW&RHkz3KltBv3CY=j)i@GCXttM?mQdzt3Mo3XpCQYyb@*3lozVaA(`v z6?kZj{r};z|5E>lI;FGuU+%*q|8D}9JBm_{c@hm{hj%Xqy!=WjPj80u%T&U5(|$x9 zKt0D*UFJa*apI{awH(z0%mXH{9Mi#Lc^(Si^LZ@q`*A0IqqyA3-z>_>MIv}V9j_2Vf_-snHeM$PU%W5F;}GMe78$`SiVl(MG zg69_|or6er2er|P$efQ&)d0FVk#M46DB^6y!_L%PcCNPVHBSfg`JcWu{)aKl`5!Fe z|3>gg_}?GDe);*m=2>7a7W2RL#DP`#U*`Xk{{dR?zfEA4{}CN(I}tKcr*wYkbnS!Oa)Jk38e);z2E8ccb1Y(*&@wC}xBo43+?`<( zlPGoj>uRIfkQ}pZ+lzRnU2iu#_NqkFpasVqV;uiS?V2Nhj-~t$!pZkvoyho4F#tvU z-vlnFQ;|^&bQ=Mnp3ZFbsq&TGW4@Jm%GZ)|>tB@ckdd+5AuRqW-fH ztW^K8fa5`IIx4let0EU6l`)g5Cj?jyJpm^=@ybR0W%>8_cJrXsRch6?oqkS#qwY79 zT7AE