From 1c66e57465632ad7b134282280a23c5893c3e4bc Mon Sep 17 00:00:00 2001 From: James Agnew Date: Thu, 18 May 2023 13:23:57 -0400 Subject: [PATCH] Update resource provenance indexing strategy (#4883) * Add migration * Update indexes * Update hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java Co-authored-by: michaelabuckley * Address review comments * Test fixes * Test fixes * Test fix --------- Co-authored-by: michaelabuckley --- .../uhn/hapi/fhir/changelog/6_8_0/upgrade.md | 6 + .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 8 +- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 20 +- .../dao/data/IResourceHistoryTableDao.java | 12 +- .../fhir/jpa/entity/ResourceSearchView.java | 4 +- .../tasks/HapiFhirJpaMigrationTasks.java | 19 +- ...esourceDaoR4SearchWithElasticSearchIT.java | 50 +++-- .../ResourceHistoryProvenanceEntity.java | 8 +- .../model/entity/ResourceHistoryTable.java | 53 ++--- .../bulk/imprt2/ConsumeFilesStepR4Test.java | 2 +- .../jpa/dao/r4/BasePartitioningR4Test.java | 8 +- .../r4/FhirResourceDaoR4QueryCountTest.java | 13 +- ...rResourceDaoR4StandardQueriesNoFTTest.java | 7 +- ...itioningNonNullDefaultPartitionR4Test.java | 3 +- .../ThreadSafeResourceDeleterSvcTest.java | 2 +- .../fhir/jpa/delete/job/ReindexJobTest.java | 82 +++++++- .../PartitioningInterceptorR4Test.java | 2 +- ...zationInterceptorMultitenantJpaR4Test.java | 7 +- ...BaseMultitenantResourceProviderR4Test.java | 3 +- .../stresstest/GiantTransactionPerfTest.java | 7 +- .../InstanceReindexServiceImplR5Test.java | 6 +- .../ca/uhn/fhir/jpa/test/BaseJpaR4Test.java | 3 + .../ca/uhn/fhirtest/config/CommonConfig.java | 4 +- ...ncMemoryQueueBackedFhirClientBalpSink.java | 2 +- .../fhir/test/utilities/ITestDataBuilder.java | 184 ++++++++++-------- 25 files changed, 312 insertions(+), 203 deletions(-) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/upgrade.md index 8b137891791..fecfb0ed458 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/upgrade.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/upgrade.md @@ -1 +1,7 @@ +Users of the `Resource.meta.source` field, as well as users of the `_source` parameter should perform a global $reindex after upgrading to this version of HAPI FHIR with the following parameters: +```url +[base]/$reindex?reindexSearchParameters=false&optimizeStorage=ALL_VERSIONS +``` + +The previous mechanism for storing and indexing these parameters is inefficient and will be replaced in a future release of HAPI FHIR. Performing this reindex operation ensures that existing data will continue to be searchable. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index d89407a83ee..c947b7a051c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -1336,10 +1336,14 @@ public abstract class BaseHapiFhirDao extends BaseStora provenance.setResourceTable(theEntity); provenance.setPartitionId(theEntity.getPartitionId()); if (haveRequestId) { - provenance.setRequestId(left(requestId, Constants.REQUEST_ID_LENGTH)); + String persistedRequestId = left(requestId, Constants.REQUEST_ID_LENGTH); + provenance.setRequestId(persistedRequestId); + historyEntry.setRequestId(persistedRequestId); } if (haveSource) { - provenance.setSourceUri(source); + String persistedSource = left(source, ResourceHistoryTable.SOURCE_URI_LENGTH); + provenance.setSourceUri(persistedSource); + historyEntry.setSourceUri(persistedSource); } if (theResource != null) { MetaUtil.populateResourceSource(myFhirContext, shouldStoreSource ? source : null, shouldStoreRequestId ? requestId : null , theResource); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index e7ea644dc87..d2550f1a21c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -1439,8 +1439,8 @@ public abstract class BaseHapiFhirResourceDao extends B reindexOptimizeStorageHistoryEntity(entity, historyEntity); if (theOptimizeStorageMode == ReindexParameters.OptimizeStorageModeEnum.ALL_VERSIONS) { int pageSize = 100; - for (int page = 0; ((long) page * pageSize) < entity.getVersion(); page++) { - Slice historyEntities = myResourceHistoryTableDao.findForResourceIdAndReturnEntities(PageRequest.of(page, pageSize), entity.getId(), historyEntity.getVersion()); + for (int page = 0; ((long)page * pageSize) < entity.getVersion(); page++) { + Slice historyEntities = myResourceHistoryTableDao.findForResourceIdAndReturnEntitiesAndFetchProvenance(PageRequest.of(page, pageSize), entity.getId(), historyEntity.getVersion()); for (ResourceHistoryTable next : historyEntities) { reindexOptimizeStorageHistoryEntity(entity, next); } @@ -1450,16 +1450,30 @@ public abstract class BaseHapiFhirResourceDao extends B } private void reindexOptimizeStorageHistoryEntity(ResourceTable entity, ResourceHistoryTable historyEntity) { + boolean changed = false; if (historyEntity.getEncoding() == ResourceEncodingEnum.JSONC || historyEntity.getEncoding() == ResourceEncodingEnum.JSON) { byte[] resourceBytes = historyEntity.getResource(); if (resourceBytes != null) { String resourceText = decodeResource(resourceBytes, historyEntity.getEncoding()); if (myStorageSettings.getInlineResourceTextBelowSize() > 0 && resourceText.length() < myStorageSettings.getInlineResourceTextBelowSize()) { ourLog.debug("Storing text of resource {} version {} as inline VARCHAR", entity.getResourceId(), historyEntity.getVersion()); - myResourceHistoryTableDao.setResourceTextVcForVersion(historyEntity.getId(), resourceText); + historyEntity.setResourceTextVc(resourceText); + historyEntity.setResource(null); + historyEntity.setEncoding(ResourceEncodingEnum.JSON); + changed = true; } } } + if (isBlank(historyEntity.getSourceUri()) && isBlank(historyEntity.getRequestId())) { + if (historyEntity.getProvenance() != null) { + historyEntity.setSourceUri(historyEntity.getProvenance().getSourceUri()); + historyEntity.setRequestId(historyEntity.getProvenance().getRequestId()); + changed = true; + } + } + if (changed) { + myResourceHistoryTableDao.save(historyEntity); + } } private BaseHasResource readEntity(IIdType theId, boolean theCheckForForcedId, RequestDetails theRequest, RequestPartitionId requestPartitionId) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java index be009259583..0c212e8bb4a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java @@ -44,8 +44,8 @@ public interface IResourceHistoryTableDao extends JpaRepository findForResourceId(Pageable thePage, @Param("resId") Long theId, @Param("dontWantVersion") Long theDontWantVersion); - @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :resId AND t.myResourceVersion != :dontWantVersion") - Slice findForResourceIdAndReturnEntities(Pageable thePage, @Param("resId") Long theId, @Param("dontWantVersion") Long theDontWantVersion); + @Query("SELECT t FROM ResourceHistoryTable t LEFT OUTER JOIN FETCH t.myProvenance WHERE t.myResourceId = :resId AND t.myResourceVersion != :dontWantVersion") + Slice findForResourceIdAndReturnEntitiesAndFetchProvenance(Pageable thePage, @Param("resId") Long theId, @Param("dontWantVersion") Long theDontWantVersion); @Query("" + "SELECT v.myId FROM ResourceHistoryTable v " + @@ -67,13 +67,6 @@ public interface IResourceHistoryTableDao extends JpaRepository findIdsOfPreviousVersionsOfResources(Pageable thePage); - /** - * Sets the inline text and clears the LOB copy of the text - */ - @Modifying - @Query("UPDATE ResourceHistoryTable as t SET t.myResource = null, t.myResourceTextVc = :text WHERE t.myId = :pid") - void setResourceTextVcForVersion(@Param("pid") Long id, @Param("text") String resourceText); - @Modifying @Query("UPDATE ResourceHistoryTable r SET r.myResourceVersion = :newVersion WHERE r.myResourceId = :id AND r.myResourceVersion = :oldVersion") void updateVersion(@Param("id") long theId, @Param("oldVersion") long theOldVersion, @Param("newVersion") long theNewVersion); @@ -81,4 +74,5 @@ public interface IResourceHistoryTableDao extends JpaRepository { .online(false) .withColumns("TAG_TYPE", "TAG_CODE", "TAG_SYSTEM", "TAG_ID", "TAG_VERSION", "TAG_USER_SELECTED"); + version.onTable("HFJ_RES_VER_PROV") + .addIndex("20230510.1", "IDX_RESVERPROV_RES_VER_PID") + .unique(false) + .withColumns("RES_VER_PID") + .failureAllowed(); + version.onTable("HFJ_RES_VER_PROV") + .addIndex("20230510.2", "IDX_RESVERPROV_RES_PID") + .unique(false) + .withColumns("RES_PID"); - + version.onTable(ResourceHistoryTable.HFJ_RES_VER) + .addColumn("20230510.4", "SOURCE_URI") + .nullable() + .type(ColumnTypeEnum.STRING, 100); + version.onTable(ResourceHistoryTable.HFJ_RES_VER) + .addColumn("20230510.5", "REQUEST_ID") + .nullable() + .type(ColumnTypeEnum.STRING, 16); } protected void init660() { diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java index 9c8cc4ac3bb..a11b61be7ad 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java @@ -949,12 +949,6 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl } } - private Consumer[] asArray(Consumer theIBaseResourceConsumer) { - @SuppressWarnings("unchecked") - Consumer[] array = (Consumer[]) new Consumer[]{theIBaseResourceConsumer}; - return array; - } - private List getResultIds(IBundleProvider theResult) { return theResult.getAllResources().stream().map(r -> r.getIdElement().getIdPart()).collect(Collectors.toList()); } @@ -1005,7 +999,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl @Test void secondWordFound() { String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Cloudy, yellow"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "Cloudy, yellow"))).getIdPart(); List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=yellow"); assertThat(resourceIds, hasItem(id1)); @@ -1016,9 +1010,9 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl // smit - matches "smit" and "smith" String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "John Smith"))).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=smit"); assertThat(resourceIds, hasItems(id1, id2)); @@ -1030,9 +1024,9 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl // smit* - matches "smit" and "smith" String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "John Smith"))).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); List resourceIds = myTestDaoSearch.searchForIds("/Observation?_elements=valueString&value-string:text=smit*"); assertThat(resourceIds, hasItems(id1, id2)); @@ -1044,9 +1038,9 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl // "smit"- matches "smit", but not "smith" String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "John Smith"))).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=\"smit\""); assertThat(resourceIds, contains(id2)); @@ -1056,9 +1050,9 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl @Test void stringTokensAreAnded() { String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "John Smith"))).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "Carl Smit"))).getIdPart(); List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=car%20smit"); assertThat(resourceIds, hasItems(id2)); @@ -1072,9 +1066,9 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl // | Fhir Query String | Executed Query | Matches | No Match // | Smit | Smit* | John Smith | John Smi String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "John Smith"))).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smi"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "John Smi"))).getIdPart(); List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=Smit"); assertThat(resourceIds, hasItems(id1)); @@ -1085,9 +1079,9 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl // | Fhir Query String | Executed Query | Matches | No Match | Note // | Jo Smit | Jo* Smit* | John Smith | John Frank | Multiple bare terms are `AND` String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "John Smith"))).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "John Frank"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "John Frank"))).getIdPart(); List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=Jo%20Smit"); assertThat(resourceIds, hasItems(id1)); @@ -1098,9 +1092,9 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl // | Fhir Query String | Executed Query | Matches | No Match | Note // | frank | john | frank | john | Frank Smith | Franklin Smith | SQS characters disable prefix wildcard String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Frank Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "Frank Smith"))).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Franklin Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "Franklin Smith"))).getIdPart(); List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text=frank|john"); assertThat(resourceIds, hasItems(id1)); @@ -1111,9 +1105,9 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl // | Fhir Query String | Executed Query | Matches | No Match | Note // | 'frank' | 'frank' | Frank Smith | Franklin Smith | Quoted terms are exact match String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Frank Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "Frank Smith"))).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "Franklin Smith"))).getIdPart(); + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "Franklin Smith"))).getIdPart(); List resourceIds = myTestDaoSearch.searchForIds("/Observation?value-string:text='frank'"); assertThat(resourceIds, hasItems(id1)); @@ -1805,10 +1799,10 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl @Test public void byValueString() { String id1 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "a-string-value-1") + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "a-string-value-1") )).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "a-string-value-2") + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "a-string-value-2") )).getIdPart(); myCaptureQueriesListener.clear(); @@ -1950,7 +1944,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl myTestDataBuilder.withObservationCode("http://example.com/", "the-code-1") )).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( - myTestDataBuilder.withPrimitiveAttribute("valueString", "a-string-value-2") + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "a-string-value-2") )).getIdPart(); myCaptureQueriesListener.clear(); @@ -2065,13 +2059,13 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl myTestDataBuilder.withObservationCode("http://example.com/", "the-code-1"), myTestDataBuilder.withEffectiveDate("2017-01-20T03:21:47"), myTestDataBuilder.withTag("http://example.org", "aTag"), - myTestDataBuilder.withPrimitiveAttribute("valueString", "a-string-value-1") + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "a-string-value-1") )).getIdPart(); String id2 = myTestDataBuilder.createObservation(List.of( myTestDataBuilder.withObservationCode("http://example.com/", "the-code-2"), myTestDataBuilder.withEffectiveDate("2017-01-24T03:21:47"), myTestDataBuilder.withTag("http://example.org", "aTag"), - myTestDataBuilder.withPrimitiveAttribute("valueString", "a-string-value-2") + myTestDataBuilder.withResourcePrimitiveAttribute("valueString", "a-string-value-2") )).getIdPart(); myCaptureQueriesListener.clear(); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java index b732ad85c1a..bd97959f68e 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java @@ -34,16 +34,20 @@ import javax.persistence.ManyToOne; import javax.persistence.MapsId; import javax.persistence.OneToOne; import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +import static ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable.SOURCE_URI_LENGTH; @Table(name = "HFJ_RES_VER_PROV", indexes = { @Index(name = "IDX_RESVERPROV_SOURCEURI", columnList = "SOURCE_URI"), @Index(name = "IDX_RESVERPROV_REQUESTID", columnList = "REQUEST_ID"), - //@Index(name = "IDX_RESVERPROV_RESID", columnList = "RES_PID") + @Index(name = "IDX_RESVERPROV_RES_PID", columnList = "RES_PID"), + @Index(name = "IDX_RESVERPROV_RES_VER_PID", columnList = "RES_VER_PID") +}, uniqueConstraints = { }) @Entity public class ResourceHistoryProvenanceEntity extends BasePartitionable { - public static final int SOURCE_URI_LENGTH = 100; @Id @Column(name = "RES_VER_PID") diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java index daec68702f2..5d3f7c27f91 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java @@ -25,28 +25,9 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.hibernate.annotations.Columns; import org.hibernate.annotations.OptimisticLock; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; +import javax.persistence.*; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -61,6 +42,7 @@ import java.util.Collection; }) public class ResourceHistoryTable extends BaseHasResource implements Serializable { public static final String IDX_RESVER_ID_VER = "IDX_RESVER_ID_VER"; + public static final int SOURCE_URI_LENGTH = 100; /** * @see ResourceEncodingEnum */ @@ -94,13 +76,18 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl @org.hibernate.annotations.Type(type = JpaConstants.ORG_HIBERNATE_TYPE_TEXT_TYPE) @OptimisticLock(excluded = true) private String myResourceTextVc; - @Column(name = "RES_ENCODING", nullable = false, length = ENCODING_COL_LENGTH) @Enumerated(EnumType.STRING) @OptimisticLock(excluded = true) private ResourceEncodingEnum myEncoding; @OneToOne(mappedBy = "myResourceHistoryTable", cascade = {CascadeType.REMOVE}) private ResourceHistoryProvenanceEntity myProvenance; + // TODO: This was added in 6.8.0 - In the future we should drop ResourceHistoryProvenanceEntity + @Column(name = "SOURCE_URI", length = SOURCE_URI_LENGTH, nullable = true) + private String mySourceUri; + // TODO: This was added in 6.8.0 - In the future we should drop ResourceHistoryProvenanceEntity + @Column(name = "REQUEST_ID", length = Constants.REQUEST_ID_LENGTH, nullable = true) + private String myRequestId; /** * Constructor @@ -109,6 +96,22 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl super(); } + public String getSourceUri() { + return mySourceUri; + } + + public void setSourceUri(String theSourceUri) { + mySourceUri = theSourceUri; + } + + public String getRequestId() { + return myRequestId; + } + + public void setRequestId(String theRequestId) { + myRequestId = theRequestId; + } + @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) @@ -215,6 +218,10 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl return myResourceVersion; } + public void setVersion(long theVersion) { + myResourceVersion = theVersion; + } + @Override public boolean isDeleted() { return getDeleted() != null; @@ -225,10 +232,6 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl setDeleted(null); } - public void setVersion(long theVersion) { - myResourceVersion = theVersion; - } - @Override public JpaPid getPersistentId() { return JpaPid.fromId(myResourceId); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java index 9e41914e8ef..df8530d1097 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java @@ -39,7 +39,7 @@ public class ConsumeFilesStepR4Test extends BasePartitioningR4Test { @BeforeEach @Override - public void before() throws ServletException { + public void before() throws Exception { super.before(); myPartitionSettings.setPartitioningEnabled(false); myStorageSettings.setInlineResourceTextBelowSize(10000); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java index 266c77b4d97..d37069791dc 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java @@ -23,13 +23,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.ServletException; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.function.Consumer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -75,8 +73,10 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest { myStorageSettings.setMatchUrlCacheEnabled(new JpaStorageSettings().getMatchUrlCache()); } + @Override @BeforeEach - public void before() throws ServletException { + public void before() throws Exception { + super.before(); myPartitionSettings.setPartitioningEnabled(true); myPartitionSettings.setIncludePartitionInSearchHashes(new PartitionSettings().isIncludePartitionInSearchHashes()); @@ -183,7 +183,7 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest { when(mySrd.getRequestId()).thenReturn("REQUEST_ID"); } - protected Consumer withPartition(Integer thePartitionId) { + protected ICreationArgument withPartition(Integer thePartitionId) { return t -> { if (thePartitionId != null) { addCreatePartition(thePartitionId, null); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java index 513e4d0c9e9..4155fcf55b4 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java @@ -953,14 +953,14 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test @ParameterizedTest @CsvSource({ // NoOp OptimisticLock OptimizeMode ExpectedSelect ExpectedUpdate - " false, false, CURRENT_VERSION, 2, 10", + " false, false, CURRENT_VERSION, 2, 1", " true, false, CURRENT_VERSION, 2, 0", - " false, true, CURRENT_VERSION, 12, 10", + " false, true, CURRENT_VERSION, 12, 1", " true, true, CURRENT_VERSION, 12, 0", - " false, false, ALL_VERSIONS, 22, 20", - " true, false, ALL_VERSIONS, 22, 0", - " false, true, ALL_VERSIONS, 32, 20", - " true, true, ALL_VERSIONS, 32, 0", + " false, false, ALL_VERSIONS, 12, 10", + " true, false, ALL_VERSIONS, 12, 0", + " false, true, ALL_VERSIONS, 22, 10", + " true, true, ALL_VERSIONS, 22, 0", }) public void testReindexJob_OptimizeStorage(boolean theNoOp, boolean theOptimisticLock, ReindexParameters.OptimizeStorageModeEnum theOptimizeStorageModeEnum, int theExpectedSelectCount, int theExpectedUpdateCount) { // Setup @@ -998,7 +998,6 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test RunOutcome outcome = myReindexStep.doReindex(data, mock(IJobDataSink.class), "123", "456", params); // validate - myCaptureQueriesListener.logSelectQueriesForCurrentThread(); assertEquals(theExpectedSelectCount, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); assertEquals(theExpectedUpdateCount, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java index 0819c4f1318..47355480674 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java @@ -12,7 +12,7 @@ import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig; import ca.uhn.fhir.jpa.test.config.TestR4Config; import ca.uhn.fhir.storage.test.BaseDateSearchDaoTests; import ca.uhn.fhir.storage.test.DaoTestDataBuilder; -import org.hl7.fhir.instance.model.api.IBaseResource; +import ca.uhn.fhir.test.utilities.ITestDataBuilder.ICreationArgument; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Observation; import org.junit.jupiter.api.Disabled; @@ -30,7 +30,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; import java.util.List; -import java.util.function.Consumer; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; @@ -194,7 +193,7 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest { } @SafeVarargs - private IIdType withObservation(Consumer... theBuilder) { + private IIdType withObservation(ICreationArgument... theBuilder) { myObservationId = myDataBuilder.createObservation(theBuilder); return myObservationId; } @@ -270,7 +269,7 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest { IIdType myResourceId; private IIdType withRiskAssessmentWithProbabilty(double theValue) { - myResourceId = myDataBuilder.createResource("RiskAssessment", myDataBuilder.withPrimitiveAttribute("prediction.probabilityDecimal", theValue)); + myResourceId = myDataBuilder.createResource("RiskAssessment", myDataBuilder.withResourcePrimitiveAttribute("prediction.probabilityDecimal", theValue)); return myResourceId; } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningNonNullDefaultPartitionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningNonNullDefaultPartitionR4Test.java index db6b1e6d005..0b2f718215c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningNonNullDefaultPartitionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningNonNullDefaultPartitionR4Test.java @@ -16,7 +16,6 @@ import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.servlet.ServletException; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; @@ -30,7 +29,7 @@ public class PartitioningNonNullDefaultPartitionR4Test extends BasePartitioningR @BeforeEach @Override - public void before() throws ServletException { + public void before() throws Exception { super.before(); myPartitionSettings.setDefaultPartitionId(1); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java index ecca9386af1..7f6ba9f9292 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java @@ -201,7 +201,7 @@ public class ThreadSafeResourceDeleterSvcTest extends BaseJpaR4Test { return myOrganizationDao.search(map).size(); } - private IIdType createPatientWithVersion(Consumer theWithId) { + private IIdType createPatientWithVersion(ICreationArgument theWithId) { Patient patient = new Patient(); patient.addName(new HumanName().setFamily("FAMILY")); theWithId.accept(patient); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java index f6daddcf293..b8218dff168 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java @@ -29,11 +29,13 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; +import javax.persistence.Query; import java.util.Date; import java.util.List; import java.util.stream.Stream; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.blankOrNullString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -44,8 +46,6 @@ import static org.junit.jupiter.api.Assertions.fail; public class ReindexJobTest extends BaseJpaR4Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReindexJobTest.class); - @Autowired private IJobCoordinator myJobCoordinator; @@ -63,6 +63,8 @@ public class ReindexJobTest extends BaseJpaR4Test { public void after() { myInterceptorRegistry.unregisterAllAnonymousInterceptors(); myStorageSettings.setInlineResourceTextBelowSize(new JpaStorageSettings().getInlineResourceTextBelowSize()); + myStorageSettings.setStoreMetaSourceInformation(new JpaStorageSettings().getStoreMetaSourceInformation()); + myStorageSettings.setPreserveRequestIdInResourceBody(new JpaStorageSettings().isPreserveRequestIdInResourceBody()); } @Test @@ -73,7 +75,7 @@ public class ReindexJobTest extends BaseJpaR4Test { Patient p = new Patient(); p.setId(patientId.toUnqualifiedVersionless()); p.setActive(true); - p.addIdentifier().setValue("" + i); + p.addIdentifier().setValue(String.valueOf(i)); myPatientDao.update(p, mySrd); } for (int i = 0; i < 9; i++) { @@ -127,7 +129,7 @@ public class ReindexJobTest extends BaseJpaR4Test { Patient p = new Patient(); p.setId(patientId.toUnqualifiedVersionless()); p.setActive(true); - p.addIdentifier().setValue("" + i); + p.addIdentifier().setValue(String.valueOf(i)); myPatientDao.update(p, mySrd); } for (int i = 0; i < 9; i++) { @@ -168,6 +170,66 @@ public class ReindexJobTest extends BaseJpaR4Test { } + @Test + public void testOptimizeStorage_AllVersions_CopyProvenanceEntityData() { + // Setup + myStorageSettings.setStoreMetaSourceInformation(JpaStorageSettings.StoreMetaSourceInformationEnum.SOURCE_URI_AND_REQUEST_ID); + myStorageSettings.setPreserveRequestIdInResourceBody(true); + + for (int i = 0; i < 10; i++) { + Patient p = new Patient(); + p.setId("PATIENT" + i); + p.getMeta().setSource("http://foo#bar"); + p.addIdentifier().setValue(String.valueOf(i)); + myPatientDao.update(p, mySrd); + + p.addIdentifier().setSystem("http://blah"); + myPatientDao.update(p, mySrd); + } + + runInTransaction(()->{ + assertEquals(20, myResourceHistoryTableDao.count()); + assertEquals(20, myResourceHistoryProvenanceDao.count()); + Query query = myEntityManager.createQuery("UPDATE " + ResourceHistoryTable.class.getSimpleName() + " p SET p.mySourceUri = NULL, p.myRequestId = NULL"); + assertEquals(20, query.executeUpdate()); + }); + + runInTransaction(()-> { + for (var next : myResourceHistoryProvenanceDao.findAll()) { + assertEquals("bar", next.getRequestId()); + assertEquals("http://foo", next.getSourceUri()); + } + for (var next : myResourceHistoryTableDao.findAll()) { + assertThat(next.getRequestId(), blankOrNullString()); + assertThat(next.getSourceUri(), blankOrNullString()); + } + }); + + // execute + JobInstanceStartRequest startRequest = new JobInstanceStartRequest(); + startRequest.setJobDefinitionId(ReindexAppCtx.JOB_REINDEX); + startRequest.setParameters( + new ReindexJobParameters() + .setOptimizeStorage(ReindexParameters.OptimizeStorageModeEnum.ALL_VERSIONS) + .setReindexSearchParameters(ReindexParameters.ReindexSearchParametersEnum.NONE) + ); + Batch2JobStartResponse startResponse = myJobCoordinator.startInstance(startRequest); + myBatch2JobHelper.awaitJobCompletion(startResponse); + + // validate + runInTransaction(()-> { + for (var next : myResourceHistoryProvenanceDao.findAll()) { + assertEquals("bar", next.getRequestId()); + assertEquals("http://foo", next.getSourceUri()); + } + for (var next : myResourceHistoryTableDao.findAll()) { + assertEquals("bar", next.getRequestId()); + assertEquals("http://foo", next.getSourceUri()); + } + }); + + } + @Test public void testOptimizeStorage_DeletedRecords() { // Setup @@ -214,7 +276,7 @@ public class ReindexJobTest extends BaseJpaR4Test { myReindexTestHelper.createAlleleSearchParameter(); - assertEquals(2, myObservationDao.search(SearchParameterMap.newSynchronous()).size()); + assertEquals(2, myObservationDao.search(SearchParameterMap.newSynchronous(), mySrd).size()); // The search param value is on the observation, but it hasn't been indexed yet assertThat(myReindexTestHelper.getAlleleObservationIds(), hasSize(0)); @@ -230,7 +292,7 @@ public class ReindexJobTest extends BaseJpaR4Test { myBatch2JobHelper.awaitJobCompletion(res); // validate - assertEquals(2, myObservationDao.search(SearchParameterMap.newSynchronous()).size()); + assertEquals(2, myObservationDao.search(SearchParameterMap.newSynchronous(), mySrd).size()); // Now one of them should be indexed List alleleObservationIds = myReindexTestHelper.getAlleleObservationIds(); @@ -249,7 +311,7 @@ public class ReindexJobTest extends BaseJpaR4Test { assertThat(entriesInSpIndexTokenTable, equalTo(1)); // simulate resource deletion - ResourceTable resource = myResourceTableDao.findById(obsId.getIdPartAsLong()).get(); + ResourceTable resource = myResourceTableDao.findById(obsId.getIdPartAsLong()).orElseThrow(); Date currentDate = new Date(); resource.setDeleted(currentDate); resource.setUpdated(currentDate); @@ -288,7 +350,7 @@ public class ReindexJobTest extends BaseJpaR4Test { myReindexTestHelper.createAlleleSearchParameter(); mySearchParamRegistry.forceRefresh(); - assertEquals(50, myObservationDao.search(SearchParameterMap.newSynchronous()).size()); + assertEquals(50, myObservationDao.search(SearchParameterMap.newSynchronous(), mySrd).size()); // The search param value is on the observation, but it hasn't been indexed yet assertThat(myReindexTestHelper.getAlleleObservationIds(), hasSize(0)); @@ -300,7 +362,7 @@ public class ReindexJobTest extends BaseJpaR4Test { myBatch2JobHelper.awaitJobCompletion(startResponse); // validate - assertEquals(50, myObservationDao.search(SearchParameterMap.newSynchronous()).size()); + assertEquals(50, myObservationDao.search(SearchParameterMap.newSynchronous(), mySrd).size()); // Now all of them should be indexed assertThat(myReindexTestHelper.getAlleleObservationIds(), hasSize(50)); } @@ -331,7 +393,7 @@ public class ReindexJobTest extends BaseJpaR4Test { // Verify assertEquals(StatusEnum.COMPLETED, outcome.getStatus()); - assertEquals(null, outcome.getErrorMessage()); + assertNull(outcome.getErrorMessage()); } @Test diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java index e6abe1c9b89..8bf59f3fd96 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java @@ -283,7 +283,7 @@ public class PartitioningInterceptorR4Test extends BaseJpaR4SystemTest { when(mySrd.getRequestId()).thenReturn("REQUEST_ID"); } - private Consumer withPartition(Integer thePartitionId) { + private ICreationArgument withPartition(Integer thePartitionId) { return t -> { if (thePartitionId != null) { addCreatePartition(thePartitionId, null); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorMultitenantJpaR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorMultitenantJpaR4Test.java index 5ece82f810b..9b2a77ea080 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorMultitenantJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorMultitenantJpaR4Test.java @@ -7,6 +7,7 @@ import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; import ca.uhn.fhir.test.utilities.ITestDataBuilder; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Observation; @@ -142,7 +143,7 @@ public class AuthorizationInterceptorMultitenantJpaR4Test extends BaseMultitenan Bundle output = myClient .search() .forResource("Observation") - .include(Observation.INCLUDE_ALL) + .include(IBaseResource.INCLUDE_ALL) .returnBundle(Bundle.class) .execute(); assertEquals(2, output.getEntry().size()); @@ -165,7 +166,7 @@ public class AuthorizationInterceptorMultitenantJpaR4Test extends BaseMultitenan myClient .search() .forResource("Observation") - .include(Observation.INCLUDE_ALL) + .include(IBaseResource.INCLUDE_ALL) .returnBundle(Bundle.class) .execute(); fail(); @@ -198,7 +199,7 @@ public class AuthorizationInterceptorMultitenantJpaR4Test extends BaseMultitenan Bundle bundle = myClient .search() .forResource("Observation") - .include(Observation.INCLUDE_ALL) + .include(IBaseResource.INCLUDE_ALL) .sort().ascending(Observation.IDENTIFIER) .returnBundle(Bundle.class) .count(3) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseMultitenantResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseMultitenantResourceProviderR4Test.java index 22b8fb03c5c..fa857889e05 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseMultitenantResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseMultitenantResourceProviderR4Test.java @@ -25,7 +25,6 @@ import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; -import java.util.function.Consumer; import java.util.function.Supplier; import static ca.uhn.fhir.jpa.model.util.JpaConstants.DEFAULT_PARTITION_NAME; @@ -124,7 +123,7 @@ public abstract class BaseMultitenantResourceProviderR4Test extends BaseResource - protected Consumer withTenant(String theTenantId) { + protected ICreationArgument withTenant(String theTenantId) { return t -> myTenantClientInterceptor.setTenantId(theTenantId); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java index d1c92f69e9d..ce60c8fdb52 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java @@ -364,7 +364,7 @@ public class GiantTransactionPerfTest { } @Override - public Slice findForResourceIdAndReturnEntities(Pageable thePage, Long theId, Long theDontWantVersion) { + public Slice findForResourceIdAndReturnEntitiesAndFetchProvenance(Pageable thePage, Long theId, Long theDontWantVersion) { throw new UnsupportedOperationException(); } @@ -383,11 +383,6 @@ public class GiantTransactionPerfTest { throw new UnsupportedOperationException(); } - @Override - public void setResourceTextVcForVersion(Long id, String resourceText) { - throw new UnsupportedOperationException(); - } - @Override public void updateVersion(long theId, long theOldVersion, long theNewVersion) { throw new UnsupportedOperationException(); diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplR5Test.java index 1a09c3f1caf..66f45fca8c7 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplR5Test.java @@ -28,7 +28,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.junit.jupiter.api.Assertions.assertEquals; -@SuppressWarnings({"unchecked", "SqlDialectInspection"}) +@SuppressWarnings({"SqlDialectInspection"}) public class InstanceReindexServiceImplR5Test extends BaseJpaR5Test { @Autowired @@ -125,7 +125,7 @@ public class InstanceReindexServiceImplR5Test extends BaseJpaR5Test { @Test public void testDryRunTypes_Number() { - IIdType id = createResource("ResearchStudy", withPrimitiveAttribute("recruitment.targetNumber", "3")); + IIdType id = createResource("ResearchStudy", withResourcePrimitiveAttribute("recruitment.targetNumber", "3")); logAllNumberIndexes(); @@ -269,7 +269,7 @@ public class InstanceReindexServiceImplR5Test extends BaseJpaR5Test { @Test public void testDryRunTypes_Uri() { - IIdType id = createResource("CodeSystem", withPrimitiveAttribute("url", "http://foo")); + IIdType id = createResource("CodeSystem", withResourcePrimitiveAttribute("url", "http://foo")); Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null); ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java index 68e5cf7d96a..f6ec41098cc 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java @@ -42,6 +42,7 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IMdmLinkJpaRepository; import ca.uhn.fhir.jpa.dao.data.IPartitionDao; +import ca.uhn.fhir.jpa.dao.data.IResourceHistoryProvenanceDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao; @@ -418,6 +419,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @Autowired protected IResourceHistoryTableDao myResourceHistoryTableDao; @Autowired + protected IResourceHistoryProvenanceDao myResourceHistoryProvenanceDao; + @Autowired protected IForcedIdDao myForcedIdDao; @Autowired @Qualifier("myCoverageDaoR4") diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java index d303767322e..7ea303f7fe2 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java @@ -113,8 +113,8 @@ public class CommonConfig { } @Bean - public IBalpAuditEventSink balpAuditEventSink(FhirContext theFhirContext) { - return new AsyncMemoryQueueBackedFhirClientBalpSink(theFhirContext, "http://localhost:8000/baseAudit"); + public IBalpAuditEventSink balpAuditEventSink() { + return new AsyncMemoryQueueBackedFhirClientBalpSink(FhirContext.forR4Cached(), "http://localhost:8000/baseAudit"); } @Bean diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java index 423c5fe3a8c..847687098ce 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java @@ -95,7 +95,7 @@ public class AsyncMemoryQueueBackedFhirClientBalpSink extends FhirClientBalpSink */ public AsyncMemoryQueueBackedFhirClientBalpSink(IGenericClient theClient) { super(theClient); - myThreadPool = ThreadPoolUtil.newThreadPool(1, 1, "BalpClientSink-" + ourNextThreadId.getAndIncrement() + "-", 100); + myThreadPool = ThreadPoolUtil.newThreadPool(1, 1, "BalpClientSink-" + ourNextThreadId.getAndIncrement() + "-", Integer.MAX_VALUE); } @Override diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/ITestDataBuilder.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/ITestDataBuilder.java index e21f5d56c88..59c5814545c 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/ITestDataBuilder.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/ITestDataBuilder.java @@ -26,12 +26,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.MetaUtil; import org.apache.commons.lang3.Validate; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseReference; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.ICompositeType; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.InstantType; import org.slf4j.Logger; @@ -54,21 +49,33 @@ import static org.hamcrest.Matchers.matchesPattern; public interface ITestDataBuilder { Logger ourLog = LoggerFactory.getLogger(ITestDataBuilder.class); + /** + * Name chosen to avoid potential for conflict. This is an internal API to this interface. + */ + static void __setPrimitiveChild(FhirContext theFhirContext, IBase theTarget, String theElementName, String theElementType, String theValue) { + BaseRuntimeElementCompositeDefinition def = (BaseRuntimeElementCompositeDefinition) theFhirContext.getElementDefinition(theTarget.getClass()); + BaseRuntimeChildDefinition activeChild = def.getChildByName(theElementName); + + IPrimitiveType booleanType = (IPrimitiveType) activeChild.getChildByName(theElementName).newInstance(); + booleanType.setValueAsString(theValue); + activeChild.getMutator().addValue(theTarget, booleanType); + } + /** * Set Patient.active = true */ - default Consumer withActiveTrue() { + default ICreationArgument withActiveTrue() { return t -> __setPrimitiveChild(getFhirContext(), t, "active", "boolean", "true"); } /** * Set Patient.active = false */ - default Consumer withActiveFalse() { + default ICreationArgument withActiveFalse() { return t -> __setPrimitiveChild(getFhirContext(), t, "active", "boolean", "false"); } - default Consumer withFamily(String theFamily) { + default ICreationArgument withFamily(String theFamily) { return t -> { IPrimitiveType family = (IPrimitiveType) getFhirContext().getElementDefinition("string").newInstance(); family.setValueAsString(theFamily); @@ -77,50 +84,50 @@ public interface ITestDataBuilder { ICompositeType humanName = (ICompositeType) humanNameDef.newInstance(); humanNameDef.getChildByName("family").getMutator().addValue(humanName, family); - RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition(t.getClass()); + BaseRuntimeElementCompositeDefinition resourceDef = (BaseRuntimeElementCompositeDefinition) getFhirContext().getElementDefinition(t.getClass()); resourceDef.getChildByName("name").getMutator().addValue(t, humanName); }; } - - /** Patient.name.given */ - default Consumer withGiven(String theName) { - return withPrimitiveAttribute("name.given", theName); + /** + * Patient.name.given + */ + default ICreationArgument withGiven(String theName) { + return withResourcePrimitiveAttribute("name.given", theName); } - /** * Set Patient.birthdate */ - default Consumer withBirthdate(String theBirthdate) { + default ICreationArgument withBirthdate(String theBirthdate) { return t -> __setPrimitiveChild(getFhirContext(), t, "birthDate", "dateTime", theBirthdate); } /** * Set Observation.status */ - default Consumer withStatus(String theStatus) { + default ICreationArgument withStatus(String theStatus) { return t -> __setPrimitiveChild(getFhirContext(), t, "status", "code", theStatus); } /** * Set Observation.effectiveDate */ - default Consumer withEffectiveDate(String theDate) { + default ICreationArgument withEffectiveDate(String theDate) { return t -> __setPrimitiveChild(getFhirContext(), t, "effectiveDateTime", "dateTime", theDate); } /** * Set Observation.effectiveDate */ - default Consumer withDateTimeAt(String thePath, String theDate) { + default ICreationArgument withDateTimeAt(String thePath, String theDate) { return t -> __setPrimitiveChild(getFhirContext(), t, thePath, "dateTime", theDate); } /** * Set [Resource].identifier.system and [Resource].identifier.value */ - default Consumer withIdentifier(String theSystem, String theValue) { + default ICreationArgument withIdentifier(String theSystem, String theValue) { return t -> { IPrimitiveType system = (IPrimitiveType) getFhirContext().getElementDefinition("uri").newInstance(); system.setValueAsString(theSystem); @@ -133,7 +140,7 @@ public interface ITestDataBuilder { identifierDef.getChildByName("system").getMutator().addValue(identifier, system); identifierDef.getChildByName("value").getMutator().addValue(identifier, value); - RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition(t.getClass()); + RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition((Class) t.getClass()); resourceDef.getChildByName("identifier").getMutator().addValue(t, identifier); }; } @@ -141,73 +148,74 @@ public interface ITestDataBuilder { /** * Set Organization.name */ - default Consumer withName(String theStatus) { + default ICreationArgument withName(String theStatus) { return t -> __setPrimitiveChild(getFhirContext(), t, "name", "string", theStatus); } - default Consumer withId(String theId) { + default ICreationArgument withId(String theId) { return t -> { assertThat(theId, matchesPattern("[a-zA-Z0-9-]+")); - t.setId(theId); + ((IBaseResource)t).setId(theId); }; } - default Consumer withId(IIdType theId) { - return t -> t.setId(theId.toUnqualifiedVersionless()); + default ICreationArgument withId(IIdType theId) { + return t -> ((IBaseResource)t).setId(theId.toUnqualifiedVersionless()); } - default Consumer withTag(String theSystem, String theCode) { - return t -> t.getMeta().addTag().setSystem(theSystem).setCode(theCode); + default ICreationArgument withTag(String theSystem, String theCode) { + return t -> ((IBaseResource)t).getMeta().addTag().setSystem(theSystem).setCode(theCode); } - default Consumer withSecurity(String theSystem, String theCode) { - return t -> t.getMeta().addSecurity().setSystem(theSystem).setCode(theCode); + default ICreationArgument withSecurity(String theSystem, String theCode) { + return t -> ((IBaseResource)t).getMeta().addSecurity().setSystem(theSystem).setCode(theCode); } - default Consumer withProfile(String theProfile) { - return t -> t.getMeta().addProfile(theProfile); + default ICreationArgument withProfile(String theProfile) { + return t -> ((IBaseResource)t).getMeta().addProfile(theProfile); } - default Consumer withSource(FhirContext theContext, String theSource) { - return t -> MetaUtil.setSource(theContext, t.getMeta(), theSource); + default ICreationArgument withSource(FhirContext theContext, String theSource) { + return t -> MetaUtil.setSource(theContext, ((IBaseResource)t).getMeta(), theSource); } - default Consumer withLastUpdated(Date theLastUpdated) { - return t -> t.getMeta().setLastUpdated(theLastUpdated); + default ICreationArgument withLastUpdated(Date theLastUpdated) { + return t -> ((IBaseResource)t).getMeta().setLastUpdated(theLastUpdated); } - default Consumer withLastUpdated(String theIsoDate) { - return t -> t.getMeta().setLastUpdated(new InstantType(theIsoDate).getValue()); + default ICreationArgument withLastUpdated(String theIsoDate) { + return t -> ((IBaseResource)t).getMeta().setLastUpdated(new InstantType(theIsoDate).getValue()); } - default IIdType createEncounter(Consumer... theModifiers) { + default IIdType createEncounter(ICreationArgument... theModifiers) { return createResource("Encounter", theModifiers); } - default IIdType createGroup(Consumer... theModifiers) { + default IIdType createGroup(ICreationArgument... theModifiers) { return createResource("Group", theModifiers); } - default IIdType createObservation(Consumer... theModifiers) { + default IIdType createObservation(ICreationArgument... theModifiers) { return createResource("Observation", theModifiers); } - default IIdType createObservation(Collection> theModifiers) { - return createResource("Observation", theModifiers.toArray(new Consumer[0])); + default IIdType createObservation(Collection theModifiers) { + return createResource("Observation", theModifiers.toArray(new ICreationArgument[0])); } - default IBaseResource buildPatient(Consumer... theModifiers) { + default IBaseResource buildPatient(ICreationArgument... theModifiers) { return buildResource("Patient", theModifiers); } - default IIdType createPatient(Consumer... theModifiers) { + + default IIdType createPatient(ICreationArgument... theModifiers) { return createResource("Patient", theModifiers); } - default IIdType createOrganization(Consumer... theModifiers) { + default IIdType createOrganization(ICreationArgument... theModifiers) { return createResource("Organization", theModifiers); } - default IIdType createResource(String theResourceType, Consumer... theModifiers) { + default IIdType createResource(String theResourceType, ICreationArgument... theModifiers) { IBaseResource resource = buildResource(theResourceType, theModifiers); if (ourLog.isDebugEnabled()) { @@ -221,7 +229,7 @@ public interface ITestDataBuilder { } } - default IIdType createResourceFromJson(String theJson, Consumer... theModifiers) { + default IIdType createResourceFromJson(String theJson, ICreationArgument... theModifiers) { IBaseResource resource = getFhirContext().newJsonParser().parseResource(theJson); applyElementModifiers(resource, theModifiers); @@ -236,70 +244,76 @@ public interface ITestDataBuilder { } } - default T buildResource(String theResourceType, Consumer... theModifiers) { + default T buildResource(String theResourceType, ICreationArgument... theModifiers) { IBaseResource resource = getFhirContext().getResourceDefinition(theResourceType).newInstance(); applyElementModifiers(resource, theModifiers); return (T) resource; } - - default Consumer withSubject(@Nullable IIdType theSubject) { + default ICreationArgument withSubject(@Nullable IIdType theSubject) { return withReference("subject", theSubject); } - default Consumer withSubject(@Nullable String theSubject) { + default ICreationArgument withSubject(@Nullable String theSubject) { return withSubject(new IdType(theSubject)); } - default Consumer withPatient(@Nullable IIdType theSubject) { + default ICreationArgument withPatient(@Nullable IIdType theSubject) { return withReference("patient", theSubject); } - default Consumer withPatient(@Nullable String theSubject) { + default ICreationArgument withPatient(@Nullable String theSubject) { return withSubject(new IdType(theSubject)); } - default Consumer withGroupMember(@Nullable IIdType theMember) { - return withPrimitiveAttribute("member.entity.reference", theMember); + default ICreationArgument withGroupMember(@Nullable IIdType theMember) { + return withResourcePrimitiveAttribute("member.entity.reference", theMember); } - default Consumer withGroupMember(@Nullable String theMember) { + default ICreationArgument withGroupMember(@Nullable String theMember) { return withGroupMember(new IdType(theMember)); } - default Consumer withEncounter(@Nullable String theEncounter) { + default ICreationArgument withEncounter(@Nullable String theEncounter) { return withReference("encounter", new IdType(theEncounter)); } @Nonnull - private Consumer withReference(String theReferenceName, @Nullable IIdType theReferenceValue) { + private ICreationArgument withReference(String theReferenceName, @Nullable IIdType theReferenceValue) { return t -> { if (theReferenceValue != null && theReferenceValue.getValue() != null) { IBaseReference reference = (IBaseReference) getFhirContext().getElementDefinition("Reference").newInstance(); reference.setReference(theReferenceValue.getValue()); - RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition(t); + RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition((IBaseResource) t); resourceDef.getChildByName(theReferenceName).getMutator().addValue(t, reference); } }; } - default Consumer withPrimitiveAttribute(String thePath, Object theValue) { - return t->{ + default Consumer withPrimitiveAttribute(String thePath, Object theValue) { + return t -> { FhirTerser terser = getFhirContext().newTerser(); - terser.addElement(t, thePath, ""+theValue); + terser.addElement(t, thePath, "" + theValue); }; } - default Consumer withElementAt(String thePath, Consumer... theModifiers) { - return t->{ + default ICreationArgument withResourcePrimitiveAttribute(String thePath, Object theValue) { + return t -> { + FhirTerser terser = getFhirContext().newTerser(); + terser.addElement(t, thePath, "" + theValue); + }; + } + + default ICreationArgument withElementAt(String thePath, Consumer... theModifiers) { + return t -> { FhirTerser terser = getFhirContext().newTerser(); E element = terser.addElement(t, thePath); applyElementModifiers(element, theModifiers); }; } - default Consumer withQuantityAtPath(String thePath, Number theValue, String theSystem, String theCode) { + default ICreationArgument withQuantityAtPath(String thePath, Number theValue, String theSystem, String theCode) { return withElementAt(thePath, withPrimitiveAttribute("value", theValue), withPrimitiveAttribute("system", theSystem), @@ -307,11 +321,11 @@ public interface ITestDataBuilder { ); } - /** * Create an Element and apply modifiers + * * @param theElementType the FHIR Element type to create - * @param theModifiers modifiers to apply after construction + * @param theModifiers modifiers to apply after construction * @return the Element */ default IBase withElementOfType(String theElementType, Consumer... theModifiers) { @@ -326,19 +340,19 @@ public interface ITestDataBuilder { } } - default Consumer withObservationCode(@Nullable String theSystem, @Nullable String theCode) { + default ICreationArgument withObservationCode(@Nullable String theSystem, @Nullable String theCode) { return withObservationCode(theSystem, theCode, null); } - default Consumer withObservationCode(@Nullable String theSystem, @Nullable String theCode, @Nullable String theDisplay) { + default ICreationArgument withObservationCode(@Nullable String theSystem, @Nullable String theCode, @Nullable String theDisplay) { return withCodingAt("code.coding", theSystem, theCode, theDisplay); } - default Consumer withCodingAt(String thePath, @Nullable String theSystem, @Nullable String theValue) { + default ICreationArgument withCodingAt(String thePath, @Nullable String theSystem, @Nullable String theValue) { return withCodingAt(thePath, theSystem, theValue, null); } - default Consumer withCodingAt(String thePath, @Nullable String theSystem, @Nullable String theValue, @Nullable String theDisplay) { + default ICreationArgument withCodingAt(String thePath, @Nullable String theSystem, @Nullable String theValue, @Nullable String theDisplay) { return withElementAt(thePath, withPrimitiveAttribute("system", theSystem), withPrimitiveAttribute("code", theValue), @@ -346,15 +360,15 @@ public interface ITestDataBuilder { ); } - default Consumer withObservationComponent(Consumer... theModifiers) { + default ICreationArgument withObservationComponent(ICreationArgument... theModifiers) { return withElementAt("component", theModifiers); } - default Consumer withObservationHasMember(@Nullable IIdType theHasMember) { + default ICreationArgument withObservationHasMember(@Nullable IIdType theHasMember) { return withReference("hasMember", theHasMember); } - default Consumer withOrganization(@Nullable IIdType theHasMember) { + default ICreationArgument withOrganization(@Nullable IIdType theHasMember) { return withReference("managingOrganization", theHasMember); } @@ -373,21 +387,15 @@ public interface ITestDataBuilder { */ FhirContext getFhirContext(); - /** - * Name chosen to avoid potential for conflict. This is an internal API to this interface. - */ - static void __setPrimitiveChild(FhirContext theFhirContext, IBaseResource theTarget, String theElementName, String theElementType, String theValue) { - RuntimeResourceDefinition def = theFhirContext.getResourceDefinition(theTarget.getClass()); - BaseRuntimeChildDefinition activeChild = def.getChildByName(theElementName); - - IPrimitiveType booleanType = (IPrimitiveType) activeChild.getChildByName(theElementName).newInstance(); - booleanType.setValueAsString(theValue); - activeChild.getMutator().addValue(theTarget, booleanType); + default ICreationArgument[] asArray(ICreationArgument theIBaseResourceConsumer) { + return new ICreationArgument[]{theIBaseResourceConsumer}; } interface Support { FhirContext getFhirContext(); + IIdType doCreateResource(IBaseResource theResource); + IIdType doUpdateResource(IBaseResource theResource); } @@ -411,6 +419,10 @@ public interface ITestDataBuilder { } } + interface ICreationArgument extends Consumer { + // nothing + } + /** * Dummy support to use ITestDataBuilder as just a builder, not a DAO */ @@ -438,4 +450,6 @@ public interface ITestDataBuilder { return null; } } + + }