From 97cfb6de37719c94c24255aaaeb091a521486ff3 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 20 May 2024 17:00:18 -0400 Subject: [PATCH] Add utility method to convert Bundle into transaction (#5945) * Add utility method to convert Bundle into transaction * Add utility method * Spotless * Build fix --- .../java/ca/uhn/fhir/util/BundleUtil.java | 68 ++++++++++++++++++ ...-utility-transaction-converter-method.yaml | 6 ++ .../ips/generator/IpsGenerationR4Test.java | 46 ++++++------ .../large-patient-everything-4.json.gz | Bin 0 -> 5797 bytes .../uhn/fhir/util/bundle/BundleUtilTest.java | 50 +++++++++++++ 5 files changed, 150 insertions(+), 20 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5945-add-bundleutil-utility-transaction-converter-method.yaml create mode 100644 hapi-fhir-jpaserver-ips/src/test/resources/large-patient-everything-4.json.gz diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java index 770aa1498c9c..f0872b622877 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java @@ -39,6 +39,7 @@ import com.google.common.collect.Sets; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.tuple.Pair; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBinary; @@ -59,6 +60,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; +import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hl7.fhir.instance.model.api.IBaseBundle.LINK_PREV; @@ -67,6 +69,12 @@ * Fetch resources from a bundle */ public class BundleUtil { + + /** Non instantiable */ + private BundleUtil() { + // nothing + } + private static final Logger ourLog = LoggerFactory.getLogger(BundleUtil.class); private static final String PREVIOUS = LINK_PREV; @@ -339,6 +347,66 @@ public static void sortEntriesIntoProcessingOrder(FhirContext theContext, IBaseB TerserUtil.setField(theContext, "entry", theBundle, retVal.toArray(new IBase[0])); } + /** + * Converts a Bundle containing resources into a FHIR transaction which + * creates/updates the resources. This method does not modify the original + * bundle, but returns a new copy. + *

+ * This method is mostly intended for test scenarios where you have a Bundle + * containing search results or other sourced resources, and want to upload + * these resources to a server using a single FHIR transaction. + *

+ *

+ * The Bundle is converted using the following logic: + *

+ *

+ * + * @param theContext The FhirContext to use with the bundle + * @param theBundle The Bundle to modify. All resources in the Bundle should have an ID. + * @param thePrefixIdsOrNull If not null, all resource IDs and all references in the Bundle will be + * modified to such that their IDs contain the given prefix. For example, for a value + * of "A", the resource "Patient/123" will be changed to be "Patient/A123". If set to + * null, resource IDs are unchanged. + * @since 7.4.0 + */ + public static T convertBundleIntoTransaction( + @Nonnull FhirContext theContext, @Nonnull T theBundle, @Nullable String thePrefixIdsOrNull) { + String prefix = defaultString(thePrefixIdsOrNull); + + BundleBuilder bb = new BundleBuilder(theContext); + + FhirTerser terser = theContext.newTerser(); + List entries = terser.getValues(theBundle, "Bundle.entry"); + for (var entry : entries) { + IBaseResource resource = terser.getSingleValueOrNull(entry, "resource", IBaseResource.class); + if (resource != null) { + Validate.isTrue(resource.getIdElement().hasIdPart(), "Resource in bundle has no ID"); + String newId = theContext.getResourceType(resource) + "/" + prefix + + resource.getIdElement().getIdPart(); + + IBaseResource resourceClone = terser.clone(resource); + resourceClone.setId(newId); + + if (isNotBlank(prefix)) { + for (var ref : terser.getAllResourceReferences(resourceClone)) { + var refElement = ref.getResourceReference().getReferenceElement(); + ref.getResourceReference() + .setReference(refElement.getResourceType() + "/" + prefix + refElement.getIdPart()); + } + } + + bb.addTransactionUpdateEntry(resourceClone); + } + } + + return bb.getBundleTyped(); + } + private static void validatePartsNotNull(LinkedHashSet theDeleteParts) { if (theDeleteParts == null) { throw new IllegalStateException( diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5945-add-bundleutil-utility-transaction-converter-method.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5945-add-bundleutil-utility-transaction-converter-method.yaml new file mode 100644 index 000000000000..a397012f3041 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5945-add-bundleutil-utility-transaction-converter-method.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5945 +title: "A new utility method has been added to `BundleUtil` which converts a FHIR Bundle + containing resources (e.g. a search result bundle) into a FHIR transaction bundle which + could be used to upload those resources to a server." diff --git a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java index e697ef20b2a6..34cf71c0435d 100644 --- a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java +++ b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java @@ -47,6 +47,7 @@ import java.util.Optional; import java.util.stream.Collectors; +import static ca.uhn.fhir.util.BundleUtil.convertBundleIntoTransaction; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.stringContainsInOrder; @@ -81,11 +82,7 @@ public void afterEach() { @Test public void testGenerateLargePatientSummary() throws IOException { Bundle sourceData = ClasspathUtil.loadCompressedResource(myFhirContext, Bundle.class, "/large-patient-everything.json.gz"); - sourceData.setType(Bundle.BundleType.TRANSACTION); - for (Bundle.BundleEntryComponent nextEntry : sourceData.getEntry()) { - nextEntry.getRequest().setMethod(Bundle.HTTPVerb.PUT); - nextEntry.getRequest().setUrl(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue()); - } + sourceData = convertBundleIntoTransaction(myFhirContext, sourceData, null); Bundle outcome = mySystemDao.transaction(mySrd, sourceData); ourLog.info("Created {} resources", outcome.getEntry().size()); @@ -119,11 +116,7 @@ public void testGenerateLargePatientSummary2() { myStorageSettings.setResourceClientIdStrategy(JpaStorageSettings.ClientIdStrategyEnum.ANY); Bundle sourceData = ClasspathUtil.loadCompressedResource(myFhirContext, Bundle.class, "/large-patient-everything-2.json.gz"); - sourceData.setType(Bundle.BundleType.TRANSACTION); - for (Bundle.BundleEntryComponent nextEntry : sourceData.getEntry()) { - nextEntry.getRequest().setMethod(Bundle.HTTPVerb.PUT); - nextEntry.getRequest().setUrl(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue()); - } + sourceData = convertBundleIntoTransaction(myFhirContext, sourceData, null); Bundle outcome = mySystemDao.transaction(mySrd, sourceData); ourLog.info("Created {} resources", outcome.getEntry().size()); @@ -145,11 +138,7 @@ public void testGenerateLargePatientSummary3() { myStorageSettings.setResourceClientIdStrategy(JpaStorageSettings.ClientIdStrategyEnum.ANY); Bundle sourceData = ClasspathUtil.loadCompressedResource(myFhirContext, Bundle.class, "/large-patient-everything-3.json.gz"); - sourceData.setType(Bundle.BundleType.TRANSACTION); - for (Bundle.BundleEntryComponent nextEntry : sourceData.getEntry()) { - nextEntry.getRequest().setMethod(Bundle.HTTPVerb.PUT); - nextEntry.getRequest().setUrl(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue()); - } + sourceData = convertBundleIntoTransaction(myFhirContext, sourceData, null); Bundle outcome = mySystemDao.transaction(mySrd, sourceData); ourLog.info("Created {} resources", outcome.getEntry().size()); @@ -166,16 +155,33 @@ public void testGenerateLargePatientSummary3() { assertEquals(80, output.getEntry().size()); } + @Test + public void testGenerateLargePatientSummary4() { + Bundle sourceData = ClasspathUtil.loadCompressedResource(myFhirContext, Bundle.class, "/large-patient-everything-4.json.gz"); + sourceData = convertBundleIntoTransaction(myFhirContext, sourceData, "EPD"); + + Bundle outcome = mySystemDao.transaction(mySrd, sourceData); + ourLog.info("Created {} resources", outcome.getEntry().size()); + + Bundle output = myClient + .operation() + .onInstance("Patient/EPD2223") + .named(JpaConstants.OPERATION_SUMMARY) + .withNoParameters(Parameters.class) + .returnResourceType(Bundle.class) + .execute(); + ourLog.info("Output: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output)); + + // Verify + assertEquals(55, output.getEntry().size()); + } + @Test public void testGenerateTinyPatientSummary() throws IOException { myStorageSettings.setResourceClientIdStrategy(JpaStorageSettings.ClientIdStrategyEnum.ANY); Bundle sourceData = ClasspathUtil.loadCompressedResource(myFhirContext, Bundle.class, "/tiny-patient-everything.json.gz"); - sourceData.setType(Bundle.BundleType.TRANSACTION); - for (Bundle.BundleEntryComponent nextEntry : sourceData.getEntry()) { - nextEntry.getRequest().setMethod(Bundle.HTTPVerb.PUT); - nextEntry.getRequest().setUrl(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue()); - } + sourceData = convertBundleIntoTransaction(myFhirContext, sourceData, null); Bundle outcome = mySystemDao.transaction(mySrd, sourceData); ourLog.info("Created {} resources", outcome.getEntry().size()); diff --git a/hapi-fhir-jpaserver-ips/src/test/resources/large-patient-everything-4.json.gz b/hapi-fhir-jpaserver-ips/src/test/resources/large-patient-everything-4.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..cc2fd1e4eaa59c19648a483421c40745d0901f7d GIT binary patch literal 5797 zcmV;W7Fy{aiwFo607_;818iY(XJsvLVRUI_ZgeeWc4cyTbZBXAXDu`?YIARH0PS6C zbK5wU{ha<4EYH?VcWuMQn?x$pvy$xCil33++1Z-HixzE)R7lFUGS&ZmK|LHNF%$QQ z-7(wx;D}g98QW4Y$2V%zkhh~VkMS&aXd7#u#F8Hzae}H;mQv$el6vTSq*Y4Gu_W^F}Z7z7BY5UGhBX(qvXnc_}}SMG8@YxN|UKn zJgLTul~GofU+EXc@a5WCKlVX1%t3CmYn!dQkE>~Ru@?5DZ0*2F+oV{-?)BFG%ew31 zq8}v}|M-_qM@jK_1R?zEzrr*d$l~vpxqFbSs`r0oMVweR{#cW66vxN^(Q~JDp7+A6 z{l4s$naHH{yl3vjKWTgS$vrT4&C;EeLenDEeG(jD^pc2|gsc)OW_>b~xvSO6U)0tJ zz1!&>*-Jitce`G8+lxuZxnhOAEj~UzFJv*wOQ8#!*lhB^+5Jp(U4H+{{q=4KagzW2 zpYsO0xw%=rA(KWzLWs57eldvux$?`Ttag>3l$FD1%SvOjBlk#mbzNRt@0I#xeWTsm z-`j5Wj=WcE>X%n*PU@Feg;cTqMED>}hc+w5N9C3B?uAOt*!{`ekNKr*Tq8$eWV2Um z?qm5C!h|yJPd~3s%1z6zyBG7)&{I)X^zVwl%7-%f1w-$ROhz&rdq^+n>4zs$(@$HxWmZw(~A|I8mF%J3Y4us&I>!Zr-_Bl22qm6>Baa?4Lhl^ zhm(S9*BC$yXNokt>7>VZdn?ngoXSuB`E}Nb*D@YWO3a#OTI~rg+|@)5?ENZ_@=4iI zK5{so-652NC@$f%Vr)B0lPj4O^J^E;wM`~BSSdSqW%}@6fBx)#Nu;kPkNQ%~&cNc` zm!DV9sic{->B>OHGwGE{7nc1wc`Mgp03v{AC~ysu6jB$T^n;D9P6zi}{$9fm`)M+- z*uCarjDgt?ySqAjoqp*^c4dw`Hjx<~8_@q}E9j6P0XS(i(dtt=$QIPHe1BVxy~&&`M(K z$ERo6<@HU$ZA;zo&3TbwmWEM0)#ZB)@bQgD*K4jZ)((qoq>E8z*KHUj(m3K$&*fFj6&62bx>S)*y-n*j1pp|9QW z9BCOJ&If?mt9?mRznP`zE-yjD(& z9KjDj9OVUw%DaDt%sIyx``a2UPaI{K)hG$mwOkipJlIE+;{ZZ@cf~~cFqRYAJ;kM?`#-u`Rvptk|myRG)d(Z=EZ(JCLQORMkM z0GSPKW)pqic$wNe6RAI#j`*T<#OtLa0#PL$-Ry*Ky1vHoaqr}+eV(o~jyPDLp%y*{ zoG1&ZVFn1K5KuC3K7(@q3;dLb<14gLJ1R*)9Fu-Dx9)>-s~pGY9sEFTo;b+-MM37* z3o^$1N|4E&>&^M4W7$)05NV-R6Oe`aB2)BEX1{0qSE5gfa{SDx^{h>r)auSCBbV z1rZKm@DR#&?ZA!G-fYmT8uMnQ)}uVlN`Ij_Fbo!jVNfp&16U2iv`2T$hV1TNY_|RM z^=qOROJXzvBxFv`XV?K$ zO8}wrgAj5|v3#xo#Xb|zAyj;0Jk$;r?yUy$5fd`KjlI^6*V)){zgoRccW-~U)f{7$ zphdxn>IElb)eB(V{QRW1e*XTva~xgva?wO^3W$7#fzXBmYJCf&BmoGlv|16LAqbx< zI5`xI1r^}j$n$a#l(AQ2rDNE@VGu8hG+Zyz0$%MF=7-(W-Q$Dg0uPVOY4Ns!c50xp zH5`IaYv`;~>)b*JCD51iz18Gi!8!<^}Occ`TNt`oAtxJ9+R!N>&-(Jaju$mJ{w>GD&=el z6j26Ps40fN^YKFVT#-ewpw5khs0v!^t}0m(=~0~KQ9g0$+`AHjZ#5 z*iK%*-Fp50Y}}&joz31&*1!dGf^Df(fGPLhR1*aRMI5-VN$4Aa*z;X57Z`F@T8kA} z>y*gy4$^o$Y!JQ{4M-upUVH_-8eftcxJ!C{-r;X9E;jcYIG7o%LgvgwjJS4kf&t;| z0pK&qpj07YTrBZbfv^3Y{k?9-YpriIY;JMUcob4=Jc_@HU^T}0Kz45V>+Mc|W9xN$ z@1lWYn8cD=p#Y}J$*FQ>4hapovltOXNE!KTT`>%yAkJdsRUkV~@}bqMcgt|4$!N#c zlZo-9N!-7=1idtu+DdPK-TSMz-=OeIvS>&O3F<{6q1AJbXRj~1Nje@MZfDz81|ugNaR>6$|wd#TOUvq;BunU0PG8>xq!w>{d|##2`YCdQ~YYd9B%pPRF(mx^If* zrl?uiwSpESf&pe!I5SuKKv=4Pw%iYOKpd=_V&*=-Em4o#d~t2>g~Lss5ou4li&+@~ z@ZFKG`QyDKGpmNFf>+yku=pvI*V!a>ps2EK$ zU_55;=5BYh*Xr&yY2zkcG-`?BdO3-R>P6o^d2^AD$6K%8vGGlI@OH^bQ#s=Y0vSnw zGn*Kogcu+UQ2;53h%c3;e91{qm6HVHj8$wZ*dO+bzKl!j=p!Y4(>mIn6$SUi=ifDm znHCN7qNHAC;<$QS!AXZ)$M4Rrk={HT9cIT%W}1bM34>{fFhBy8^E2E6MSKGM&?tj= zXnl*9%=AQ=33HA)Di>8Apr)Q2Cvs%|mV3j#EpL>h$+r_-^jhb8t>g8-9eM{_oP8|iiKL+-jDM0!ap^LhZYSUq_qYQidBTF zW8jZ_tu01c8lHdH4wI9mX*A!!0z)JX4}o>W1E>fsklg3MGNA%1X(*Ih^3aoY+6aaa zvWIBs@G45~=C}W}6trl3CFS)}5aHGN2VpcC#XDQ)M2t?~X4hs(LGyVdVoVTN;7hCl z#YO`Gg8(q@45UCBtM$@5d$OhwbGB=hDb$v^j6H4R_~Typa$-9#x#`<8NDqqt*zRXyGB$w4~GD|zb?i*et)TZ4#e(V$)$)Jt5!SJFMU#Ocn# z_HDZBU!Ly2-{VW-ng#7+K{Q7OC@RWOVa9>0NxC+W?C#0wpR&u&zpr>j&K@#wCC;WqXc1Lj$4P)!6kM7z~4x>1B zc1|H5K}v^KrCHXuA19%jL{DtdaBl|dr6&@Q>N%(zebx4NuY+~JfBSB4u%xG%91FQd zngED212m*1J(cN4p^q>LnY-IEcInB|6A=Nf2oRVWNXE`is46p~K7x=YLbk-(-2z@S zxMD(>0K+1ZFb?ToUA|5ru-39^o2}=$Y{}^Ziee6qkva$LjBM=z0q6O zulw6)WIwvosAo?`+7FdK$sMb#BO@MiAhg0jQWn_Ixp3H+B^f$(n(`bZtZZmP0e!M$o~P=PAxe40gi$-T!@kTOX#m|T-5>&5G(?-xdI^Y- z>Rlu29k#W;m3Fe@XurMN3YP>lGks{_?47^@<-FMPctZ>XF|tf|CAs^hd=)MU=&2Hr zbHs2(mj5G10iILsffptDC@jy(al|ox1Q~VGEOCzcdN%ep#@0I=Cnj_CXkLOKwrK1& zWAzdg6V-u}`&S{nY3*Ha??pR<7$3V0Gv$q>w3~r2EhLY!n&Hh4ayt z1oc#t0F>tjqKajG*Vzt97bRQ#2OL!)+aPvYG_0HPdf6#p)hSBWTSMI+orqy{&{c!o z<+1X2)7actY8hbGOis)+K&l`Fz7R}PC6Ur%$xcr-gA!$vO}7vsfga|l^=y>eq>OP? zQS4qIld(6LWO1|Ko>Q6$?}y$r@mu*{J1zR9S=A&zZ_%i3R%cW<#a|6gIuoqdR&*4f z4x+=`{xYh2CgbxbOq_YiUM3U7`wja7R?ABI8?HWa%)P z#Bj$pEGC&=ACcefLBR&G)1slsM&OrfYwmzyg2JSilU(b&LNN>q}sPIkS3JNECNr~i<=)3@X-E} z*~tLed{ zV4*9`5d%~(_aC+xa7ctQB9sa*a|k?Ho)T`D5f$Ox^V5R9O-+Bk!-saQaoSV%TF<^8 z*}Qx@e{B5s7s8*@7Ot1J$X5=*ghw|vASbP@sDIVAw;P`{`y7CCP|T{CINq7`>onKa zysqkdy^h8?zcN5gs2y(c@e zebIFH&ZY>o2#AKj63YR_+y}yU@y!rkHB)Q-%n%nB#_mCL8tXFvg9Ck zPZ-WKnLhxn&r19b*!p><(lVbm#C_ekiv^kN!T+upi!Ep6Sl^I)2}#+pxi-q)$0P3OXsS0A*AHiKGUS8}05a z8T45dB%hG^*@2|pde|S*{W#4E8Am3q2IaI+|9+`I7?^+Dfpcl{xu5!-kk9KvKCc(~ z^eekmm~Ef4;oEm`PfOZGADWB&QdzK7254wmxvwNFPm6W83zSwORF-km1mq*wYCW8L zS{7+`Ez4R*mA>Z}Wf};3L^!1tn@lFK*UoZseQz`OXK-(s|NFw#~2!bnZ^bwNvUp#d+b-?f0(7iOqNr; zaTWYOFH$f1F@VAEj(*IcUo{tel-WU=JrTSIb>Tgz7w^Kaj>O4|-Qcj(BHcH)y@SC? zgQvsF&7xc>4hZ%ka3jAd7kr>|z;zfp=`ir|?>pRo+!>Uo!@~Q~q}hiwmV?MxZ_O*B zL5aejnmPz$A_88KH=b|A#rTg^<(wCbdSe->cj2fX|^>uGyUcQ zkqLzr6&P@12%r|>(&L7JYwi;$71Go;!0tYTXrp4mCzgd9Qr*z%G^S$Q{k+H$@V_JA zeOMFVeONExsb8H5YamZ<&oUEV)AsF7o~I4Aw28DnXA(%C1ZC*F0s?Uaq)>)%%b5wA z0Qer{t%^x+6P3qa_Qxi3qj~itTiYhmWaGH0h^HvP&eDE3!)Mnaw|v5bAs{Br<%R7g z`9u*vQ(T$vS!=9mr_Mc>@R!M3_0 zfz^&OfP&5=r&v=b8Zf}Efj&k$6s!qTbJLiR2N@%gZtTCU4hi|>dJ~O#3 zl0OMe)D>Fg<9caA@ajZ&>Bif>(pM+Gczb(0j)LaW1XKZo$hQC^-^m;2Qv`+@*AklI zz{oOVW&>zq58`%(CozydLN`b)Za;&f40K%8G|KpbDiuzKRbzl|dQ z%|16)tf5%BQ|=w{a#WrY8@gvZ150@AfY}D0g^-jDg^|BBIsOoX%S{T za4Of3MewtS{ZcpZTu4Abkwq8rSlcWzHus`L$E#ji^lj!%R)+Iw)W42QpbG zl^h{KVAVQ#r=O+e@zcZ9sYhr3q`MA~@bXz^#i}MbxGU{-lQ5Qp!KAalGo{oOQRlOI zDTRyb=|2Bp??ZpxT<(ky-X2`~4GwbEkin2~4G4u2Q0>NAB{c-Z5~z{86$!~7Gt8bu j6U*3J_bx_*L9|##t0i=+q3eqO`osSNr=%+F-qZj9=0#y{ literal 0 HcmV?d00001 diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java index ccbb3c600809..85d6c4b4a96a 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java @@ -680,6 +680,56 @@ public void testGetBundleTypeEnum_withNoBundleType_returnsNull(){ assertNull(BundleUtil.getBundleTypeEnum(ourCtx, bundle)); } + @Test + public void testConvertBundleIntoTransaction() { + Bundle input = createBundleWithPatientAndObservation(); + + Bundle output = BundleUtil.convertBundleIntoTransaction(ourCtx, input, null); + assertEquals(Bundle.BundleType.TRANSACTION, output.getType()); + assertEquals("Patient/123", output.getEntry().get(0).getFullUrl()); + assertEquals("Patient/123", output.getEntry().get(0).getRequest().getUrl()); + assertEquals(Bundle.HTTPVerb.PUT, output.getEntry().get(0).getRequest().getMethod()); + assertTrue(((Patient) output.getEntry().get(0).getResource()).getActive()); + assertEquals("Observation/456", output.getEntry().get(1).getFullUrl()); + assertEquals("Observation/456", output.getEntry().get(1).getRequest().getUrl()); + assertEquals(Bundle.HTTPVerb.PUT, output.getEntry().get(1).getRequest().getMethod()); + assertEquals("Patient/123", ((Observation)output.getEntry().get(1).getResource()).getSubject().getReference()); + assertEquals(Observation.ObservationStatus.AMENDED, ((Observation)output.getEntry().get(1).getResource()).getStatus()); + } + + @Test + public void testConvertBundleIntoTransaction_WithPrefix() { + Bundle input = createBundleWithPatientAndObservation(); + + Bundle output = BundleUtil.convertBundleIntoTransaction(ourCtx, input, "A"); + assertEquals(Bundle.BundleType.TRANSACTION, output.getType()); + assertEquals("Patient/A123", output.getEntry().get(0).getFullUrl()); + assertEquals("Patient/A123", output.getEntry().get(0).getRequest().getUrl()); + assertEquals(Bundle.HTTPVerb.PUT, output.getEntry().get(0).getRequest().getMethod()); + assertTrue(((Patient) output.getEntry().get(0).getResource()).getActive()); + assertEquals("Observation/A456", output.getEntry().get(1).getFullUrl()); + assertEquals("Observation/A456", output.getEntry().get(1).getRequest().getUrl()); + assertEquals(Bundle.HTTPVerb.PUT, output.getEntry().get(1).getRequest().getMethod()); + assertEquals("Patient/A123", ((Observation)output.getEntry().get(1).getResource()).getSubject().getReference()); + assertEquals(Observation.ObservationStatus.AMENDED, ((Observation)output.getEntry().get(1).getResource()).getStatus()); + } + + private static @Nonnull Bundle createBundleWithPatientAndObservation() { + Bundle input = new Bundle(); + input.setType(Bundle.BundleType.COLLECTION); + Patient patient = new Patient(); + patient.setActive(true); + patient.setId("123"); + input.addEntry().setResource(patient); + Observation observation = new Observation(); + observation.setId("456"); + observation.setStatus(Observation.ObservationStatus.AMENDED); + observation.setSubject(new Reference("Patient/123")); + input.addEntry().setResource(observation); + return input; + } + + @Nonnull private static Bundle withBundle(Resource theResource) { final Bundle bundle = new Bundle();