From 5d5b3d7639910e009a77cc8abdc6c2063e674d7c Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Fri, 31 Jan 2020 11:16:01 -0500 Subject: [PATCH 01/30] Changes to schema to loosen dependencies between Forced ID table and the Resource and Resource History tables. --- .../dao/expunge/ExpungeEverythingService.java | 2 - .../FhirResourceDaoR4SearchOptimizedTest.java | 5 +-- .../tasks/HapiFhirJpaMigrationTasks.java | 5 +++ .../jpa/model/entity/BaseHasResource.java | 25 +----------- .../model/entity/ResourceHistoryTable.java | 39 ++++++++++++++++++- .../fhir/jpa/model/entity/ResourceTable.java | 38 +++++++++++++++++- 6 files changed, 83 insertions(+), 31 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java index 31da472aef5..bcdddb572fc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java @@ -78,8 +78,6 @@ public class ExpungeEverythingService { ourLog.info("BEGINNING GLOBAL $expunge"); myTxTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); myTxTemplate.execute(t -> { - counter.addAndGet(doExpungeEverythingQuery("UPDATE " + ResourceHistoryTable.class.getSimpleName() + " d SET d.myForcedId = null")); - counter.addAndGet(doExpungeEverythingQuery("UPDATE " + ResourceTable.class.getSimpleName() + " d SET d.myForcedId = null")); counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermCodeSystem.class.getSimpleName() + " d SET d.myCurrentVersion = null")); return null; }); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java index a446d5e9440..590e86b89a3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java @@ -30,7 +30,6 @@ import org.junit.Test; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; -import org.springframework.test.context.TestPropertySource; import java.util.ArrayList; import java.util.Arrays; @@ -163,7 +162,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { } @Test - public void testFetchTotalAccurateForSlowLoading() throws InterruptedException { + public void testFetchTotalAccurateForSlowLoading() { create200Patients(); mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(25); @@ -836,8 +835,6 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); - // Because of the forced ID's bidirectional link HFJ_RESOURCE <-> HFJ_FORCED_ID - assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); runInTransaction(() -> { assertEquals(1, myResourceTableDao.count()); assertEquals(1, myResourceHistoryTableDao.count()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 53489f0c607..51667c1374f 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -61,6 +61,11 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { protected void init420() { // 20191015 - present Builder version = forVersion(VersionEnum.V4_2_0); + + // Eliminate circular dependency. + version.onTable("HFJ_RESOURCE").dropColumn("20200130.1", "FORCED_ID_PID"); + version.onTable("HFJ_RES_VER").dropColumn("20200130.2", "FORCED_ID_PID"); + version.onTable("HFJ_RES_VER").addForeignKey("20200130.3", "FK_RESOURCE_HISTORY_RESOURCE").toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID"); } protected void init410() { // 20190815 - 20191014 diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java index 6970b64dc68..0118382b324 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java @@ -45,11 +45,6 @@ public abstract class BaseHasResource implements IBaseResourceEntity, IBasePersi @OptimisticLock(excluded = true) private FhirVersionEnum myFhirVersion; - @OneToOne(optional = true, fetch = FetchType.LAZY, cascade = {}, orphanRemoval = false) - @JoinColumn(name = "FORCED_ID_PID") - @OptimisticLock(excluded = true) - private ForcedId myForcedId; - @Column(name = "HAS_TAGS", nullable = false) @OptimisticLock(excluded = true) private boolean myHasTags; @@ -96,29 +91,13 @@ public abstract class BaseHasResource implements IBaseResourceEntity, IBasePersi myFhirVersion = theFhirVersion; } - public ForcedId getForcedId() { - return myForcedId; - } + abstract public ForcedId getForcedId(); - public void setForcedId(ForcedId theForcedId) { - myForcedId = theForcedId; - } + abstract public void setForcedId(ForcedId theForcedId); @Override public abstract Long getId(); - @Override - public IdDt getIdDt() { - if (getForcedId() == null) { - Long id = getResourceId(); - return new IdDt(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); - } else { - // Avoid a join query if possible - String forcedId = getTransientForcedId() != null ? getTransientForcedId() : getForcedId().getForcedId(); - return new IdDt(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); - } - } - @Override public boolean isDeleted() { return myDeleted != null; 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 1e8c84c6c26..fea5496300e 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 @@ -21,6 +21,8 @@ package ca.uhn.fhir.jpa.model.entity; */ import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.Constants; import org.hibernate.annotations.OptimisticLock; import javax.persistence.*; @@ -54,7 +56,11 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl @Column(name = "PID") private Long myId; - @Column(name = "RES_ID") + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_RESOURCE_HISTORY_RESOURCE")) + private ResourceTable myResourceTable; + + @Column(name = "RES_ID", nullable = false, updatable = false, insertable = false) private Long myResourceId; @Column(name = "RES_TYPE", length = ResourceTable.RESTYPE_LEN, nullable = false) @@ -165,4 +171,35 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl public ResourcePersistentId getPersistentId() { return new ResourcePersistentId(myResourceId); } + + public ResourceTable getResourceTable() { + return myResourceTable; + } + + public void setResourceTable(ResourceTable theResourceTable) { + myResourceTable = theResourceTable; + } + + @Override + public IdDt getIdDt() { + if (getResourceTable().getForcedId() == null) { + Long id = getResourceId(); + return new IdDt(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + } else { + // Avoid a join query if possible + String forcedId = getTransientForcedId() != null ? getTransientForcedId() : getResourceTable().getForcedId().getForcedId(); + return new IdDt(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + } + } + + @Override + public ForcedId getForcedId() { + return getResourceTable().getForcedId(); + } + + @Override + public void setForcedId(ForcedId theForcedId) { + getResourceTable().setForcedId(theForcedId); + } + } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java index b46c9d9cd6c..a7570dcf71e 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java @@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; import ca.uhn.fhir.jpa.model.search.IndexNonDeletedInterceptor; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -199,26 +201,36 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @OneToMany(mappedBy = "myTargetResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) @OptimisticLock(excluded = true) private Collection myResourceLinksAsTarget; + @Column(name = "RES_TYPE", length = RESTYPE_LEN, nullable = false) @Field @OptimisticLock(excluded = true) private String myResourceType; + @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) @OptimisticLock(excluded = true) private Collection mySearchParamPresents; + @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) @OptimisticLock(excluded = true) private Set myTags; + @Transient private transient boolean myUnchangedInCurrentOperation; + @Version @Column(name = "RES_VER") private long myVersion; + @OneToMany(mappedBy = "myResourceTable", fetch = FetchType.LAZY) private Collection myProvenance; + @Transient private transient ResourceHistoryTable myCurrentVersionEntity; + @OneToOne(optional = true, fetch = FetchType.EAGER, cascade = {}, orphanRemoval = false, mappedBy = "myResource") + private ForcedId myForcedId; + @Override public ResourceTag addTag(TagDefinition theTag) { for (ResourceTag next : getTags()) { @@ -549,7 +561,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas retVal.setUpdated(getUpdated()); retVal.setFhirVersion(getFhirVersion()); retVal.setDeleted(getDeleted()); - retVal.setForcedId(getForcedId()); + retVal.setResourceTable(this); retVal.getTags().clear(); @@ -606,4 +618,28 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas public ResourcePersistentId getPersistentId() { return new ResourcePersistentId(getId()); } + + @Override + public ForcedId getForcedId() { + return myForcedId; + } + + @Override + public void setForcedId(ForcedId theForcedId) { + myForcedId = theForcedId; + } + + @Override + public IdDt getIdDt() { + if (getForcedId() == null) { + Long id = getResourceId(); + return new IdDt(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + } else { + // Avoid a join query if possible + String forcedId = getTransientForcedId() != null ? getTransientForcedId() : getForcedId().getForcedId(); + return new IdDt(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + } + } + + } From 5f30bdb27b42c93f0d048d62a765ce012fef6b96 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Tue, 4 Feb 2020 17:00:56 -0500 Subject: [PATCH 02/30] not sure this is going to work yet --- .../main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java | 7 ++++++- .../java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java index a743ad281e7..4e051d5b2c3 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate; */ import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; +import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask; import com.google.common.annotations.VisibleForTesting; import org.flywaydb.core.Flyway; import org.flywaydb.core.api.MigrationInfoService; @@ -85,7 +86,11 @@ public class FlywayMigrator extends BaseMigrator { @Override public void addTasks(List theTasks) { - theTasks.forEach(this::addTask); + if ("true".equals(System.getProperty("unit_test_mode"))) { + theTasks.stream().filter(task -> task instanceof InitializeSchemaTask).forEach(this::addTask); + } else { + theTasks.forEach(this::addTask); + } } @Override diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java index e68d1609158..696f70be553 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java @@ -108,7 +108,9 @@ public abstract class BaseTask { JdbcTemplate jdbcTemplate = getConnectionProperties().newJdbcTemplate(); try { int changesCount = jdbcTemplate.update(theSql, theArguments); - logInfo(ourLog, "SQL \"{}\" returned {}", theSql, changesCount); + if (!"true".equals(System.getProperty("unit_test_mode"))) { + logInfo(ourLog, "SQL \"{}\" returned {}", theSql, changesCount); + } return changesCount; } catch (DataAccessException e) { if (myFailureAllowed) { From 5e8eec8bf66b4e460dce28eec6ee4698f2f6f13f Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Sun, 9 Feb 2020 18:29:55 -0500 Subject: [PATCH 03/30] it works now --- .../uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java | 5 +++++ .../fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java | 2 +- .../jpa/migrate/tasks/SchemaInitializationProvider.java | 7 ++++++- .../uhn/fhir/jpa/migrate/tasks/api/BaseMigrationTasks.java | 7 +++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java index 2c856eac168..781b0f406b5 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java @@ -34,6 +34,7 @@ import java.util.Set; public class InitializeSchemaTask extends BaseTask { private static final Logger ourLog = LoggerFactory.getLogger(InitializeSchemaTask.class); + private final ISchemaInitializationProvider mySchemaInitializationProvider; public InitializeSchemaTask(String theProductVersion, String theSchemaVersion, ISchemaInitializationProvider theSchemaInitializationProvider) { @@ -77,4 +78,8 @@ public class InitializeSchemaTask extends BaseTask { protected void generateHashCode(HashCodeBuilder theBuilder) { theBuilder.append(mySchemaInitializationProvider); } + + public ISchemaInitializationProvider getSchemaInitializationProvider() { + return mySchemaInitializationProvider; + } } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 90e920e1234..ffcae0b4c2a 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -893,7 +893,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { } - private void init330() { // 20180114 - 20180329 + protected void init330() { // 20180114 - 20180329 Builder version = forVersion(VersionEnum.V3_3_0); version.initializeSchema("20180115.0", new SchemaInitializationProvider("/ca/uhn/hapi/fhir/jpa/docs/database", "HFJ_RESOURCE")); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java index 6e8a0c25f7e..c15e330b6ee 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java @@ -37,7 +37,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank; public class SchemaInitializationProvider implements ISchemaInitializationProvider { - private final String mySchemaFileClassPath; + private String mySchemaFileClassPath; private final String mySchemaExistsIndicatorTable; /** @@ -110,5 +110,10 @@ public class SchemaInitializationProvider implements ISchemaInitializationProvid public String getSchemaExistsIndicatorTable() { return mySchemaExistsIndicatorTable; } + + public SchemaInitializationProvider setSchemaFileClassPath(String theSchemaFileClassPath) { + mySchemaFileClassPath = theSchemaFileClassPath; + return this; + } } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/BaseMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/BaseMigrationTasks.java index 5d7c819744c..7198767ee0e 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/BaseMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/BaseMigrationTasks.java @@ -81,6 +81,13 @@ public class BaseMigrationTasks { return retval; } + protected BaseTask getTaskWithVersion(String theFlywayVersion) { + return myTasks.values().stream() + .filter(task -> theFlywayVersion.equals(task.getFlywayVersion())) + .findFirst() + .get(); + } + void validate(Collection theTasks) { for (BaseTask task: theTasks) { task.validateVersion(); From da11d3d89ed655516e57e520fa09578491707942 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Sat, 15 Feb 2020 12:45:59 -0500 Subject: [PATCH 04/30] remove distinct from search (#1712) * it works now * oops * add successful migration message * add successful migration message * Add fix for #1300 * Remove FIXME * Test fix * Test fix Co-authored-by: James Agnew --- .../ca/uhn/fhir/jpa/dao/IResultIterator.java | 2 + .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 53 +++++++++++++++---- .../jpa/search/SearchCoordinatorSvcImpl.java | 3 +- .../search/SearchCoordinatorSvcImplTest.java | 39 ++++++-------- .../uhn/fhir/jpa/migrate/FlywayMigrator.java | 2 + .../fhir/jpa/migrate/TaskOnlyMigrator.java | 8 ++- 6 files changed, 74 insertions(+), 33 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java index ce4795e9630..1b84f581849 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java @@ -29,4 +29,6 @@ public interface IResultIterator extends Iterator, Closeab int getSkippedCount(); + int getNonSkippedCount(); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 07739e125ed..99b349c698d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -254,7 +254,8 @@ public class SearchBuilder implements ISearchBuilder { outerQuery.multiselect(myBuilder.countDistinct(myQueryRoot.getRoot())); } else { outerQuery.multiselect(myQueryRoot.get("myId").as(Long.class)); - outerQuery.distinct(true); + // KHS This distinct call is causing performance issues in large installations +// outerQuery.distinct(true); } } @@ -965,6 +966,7 @@ public class SearchBuilder implements ISearchBuilder { private SortSpec mySort; private boolean myStillNeedToFetchIncludes; private int mySkipCount = 0; + private int myNonSkipCount = 0; private QueryIterator(SearchRuntimeDetails theSearchRuntimeDetails, RequestDetails theRequest) { mySearchRuntimeDetails = theSearchRuntimeDetails; @@ -994,14 +996,7 @@ public class SearchBuilder implements ISearchBuilder { myMaxResultsToFetch = myDaoConfig.getFetchSizeDefaultMaximum(); } - final TypedQuery query = createQuery(mySort, myMaxResultsToFetch, false, myRequest); - - mySearchRuntimeDetails.setQueryStopwatch(new StopWatch()); - - Query hibernateQuery = (Query) query; - hibernateQuery.setFetchSize(myFetchSize); - ScrollableResults scroll = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); - myResultsIterator = new ScrollableResultsIterator<>(scroll); + initializeIteratorQuery(myMaxResultsToFetch); // If the query resulted in extra results being requested if (myAlsoIncludePids != null) { @@ -1036,11 +1031,32 @@ public class SearchBuilder implements ISearchBuilder { ResourcePersistentId next = new ResourcePersistentId(nextLong); if (myPidSet.add(next)) { myNext = next; + myNonSkipCount++; break; } else { mySkipCount++; } } + + if (!myResultsIterator.hasNext()) { + if (myMaxResultsToFetch != null && (mySkipCount + myNonSkipCount == myMaxResultsToFetch)) { + if (mySkipCount > 0 && myNonSkipCount == 0) { + myMaxResultsToFetch += 1000; + + StorageProcessingMessage message = new StorageProcessingMessage(); + String msg = "Pass completed with no matching results. This indicates an inefficient query! Retrying with new max count of " + myMaxResultsToFetch; + ourLog.warn(msg); + message.setMessage(msg); + HookParams params = new HookParams() + .add(RequestDetails.class, myRequest) + .addIfMatchesType(ServletRequestDetails.class, myRequest) + .add(StorageProcessingMessage.class, message); + JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_WARNING, params); + + initializeIteratorQuery(myMaxResultsToFetch); + } + } + } } } @@ -1100,6 +1116,20 @@ public class SearchBuilder implements ISearchBuilder { } + private void initializeIteratorQuery(Integer theMaxResultsToFetch) { + final TypedQuery query = createQuery(mySort, theMaxResultsToFetch, false, myRequest); + + mySearchRuntimeDetails.setQueryStopwatch(new StopWatch()); + + Query hibernateQuery = (Query) query; + hibernateQuery.setFetchSize(myFetchSize); + ScrollableResults scroll = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); + myResultsIterator = new ScrollableResultsIterator<>(scroll); + + mySkipCount = 0; + myNonSkipCount = 0; + } + @Override public boolean hasNext() { if (myNext == null) { @@ -1122,6 +1152,11 @@ public class SearchBuilder implements ISearchBuilder { return mySkipCount; } + @Override + public int getNonSkippedCount() { + return myNonSkipCount; + } + @Override public void close() { if (myResultsIterator != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index 731f4019148..49ef70e13b6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -774,10 +774,11 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { if (theResultIter.hasNext() == false) { int skippedCount = theResultIter.getSkippedCount(); + int nonSkippedCount = theResultIter.getNonSkippedCount(); int totalFetched = skippedCount + myCountSavedThisPass + myCountBlockedThisPass; ourLog.trace("MaxToFetch[{}] SkippedCount[{}] CountSavedThisPass[{}] CountSavedThisTotal[{}] AdditionalPrefetchRemaining[{}]", myMaxResultsToFetch, skippedCount, myCountSavedThisPass, myCountSavedTotal, myAdditionalPrefetchThresholdsRemaining); - if (myMaxResultsToFetch != null && totalFetched < myMaxResultsToFetch) { + if (nonSkippedCount == 0 || (myMaxResultsToFetch != null && totalFetched < myMaxResultsToFetch)) { ourLog.trace("Setting search status to FINISHED"); mySearch.setStatus(SearchStatusEnum.FINISHED); mySearch.setTotalCount(myCountSavedTotal); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java index c3efb07cdc8..a622a7a4933 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java @@ -182,33 +182,11 @@ public class SearchCoordinatorSvcImplTest { when(mySearchBuilder.createQuery(any(), any(), any())).thenReturn(iter); doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any()); - when(mySearchResultCacheSvc.fetchResultPids(any(), anyInt(), anyInt())).thenAnswer(t -> { - List returnedValues = iter.getReturnedValues(); - int offset = t.getArgument(1, Integer.class); - int end = t.getArgument(2, Integer.class); - end = Math.min(end, returnedValues.size()); - offset = Math.min(offset, returnedValues.size()); - ourLog.info("findWithSearchUuid {} - {} out of {} values", offset, end, returnedValues.size()); - return returnedValues.subList(offset, end); - }); - - when(mySearchResultCacheSvc.fetchAllResultPids(any())).thenReturn(allResults); - - when(mySearchCacheSvc.tryToMarkSearchAsInProgress(any())).thenAnswer(t->{ - Search search = t.getArgument(0, Search.class); - assertEquals(SearchStatusEnum.PASSCMPLET, search.getStatus()); - search.setStatus(SearchStatusEnum.LOADING); - return Optional.of(search); - }); - when(mySearchCacheSvc.save(any())).thenAnswer(t -> { Search search = t.getArgument(0, Search.class); myCurrentSearch = search; return search; }); - when(mySearchCacheSvc.fetchByUuid(any())).thenAnswer(t -> Optional.ofNullable(myCurrentSearch)); - IFhirResourceDao dao = myCallingDao; - when(myDaoRegistry.getResourceDao(any(String.class))).thenReturn(dao); IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient", new CacheControlDirective(), null); assertNotNull(result.getUuid()); @@ -602,6 +580,11 @@ public class SearchCoordinatorSvcImplTest { return myWrap.getSkippedCount(); } + @Override + public int getNonSkippedCount() { + return myCount; + } + @Override public void close() { // nothing @@ -611,6 +594,7 @@ public class SearchCoordinatorSvcImplTest { public static class ResultIterator extends BaseIterator implements IResultIterator { private final Iterator myWrap; + private int myCount; ResultIterator(Iterator theWrap) { myWrap = theWrap; @@ -623,6 +607,7 @@ public class SearchCoordinatorSvcImplTest { @Override public ResourcePersistentId next() { + myCount++; return myWrap.next(); } @@ -631,6 +616,11 @@ public class SearchCoordinatorSvcImplTest { return 0; } + @Override + public int getNonSkippedCount() { + return myCount; + } + @Override public void close() { // nothing @@ -697,6 +687,11 @@ public class SearchCoordinatorSvcImplTest { } } + @Override + public int getNonSkippedCount() { + return 0; + } + @Override public void close() { // nothing diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java index a743ad281e7..8b60927345e 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java @@ -63,6 +63,8 @@ public class FlywayMigrator extends BaseMigrator { if (isDryRun()) { StringBuilder statementBuilder = buildExecutedStatementsString(); ourLog.info("SQL that would be executed:\n\n***********************************\n{}***********************************", statementBuilder); + } else { + ourLog.info("Schema migrated successfully."); } } catch (Exception e) { throw e; diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/TaskOnlyMigrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/TaskOnlyMigrator.java index e27668fd0fe..86d1d92bcd6 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/TaskOnlyMigrator.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/TaskOnlyMigrator.java @@ -51,7 +51,11 @@ public class TaskOnlyMigrator extends BaseMigrator { next.setConnectionProperties(connectionProperties); try { - ourLog.info("Executing task of type: {}", next.getClass().getSimpleName()); + if (isDryRun()) { + ourLog.info("Dry run {} {}", next.getFlywayVersion(), next.getDescription()); + } else { + ourLog.info("Executing {} {}", next.getFlywayVersion(), next.getDescription()); + } next.execute(); addExecutedStatements(next.getExecutedStatements()); } catch (SQLException e) { @@ -61,6 +65,8 @@ public class TaskOnlyMigrator extends BaseMigrator { if (isDryRun()) { StringBuilder statementBuilder = buildExecutedStatementsString(); ourLog.info("SQL that would be executed:\n\n***********************************\n{}***********************************", statementBuilder); + } else { + ourLog.info("Schema migrated successfully."); } } From 4fd80cfca763c18bf410e87a034525392528d99d Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 16 Feb 2020 19:00:15 -0500 Subject: [PATCH 05/30] Bump to 4.2.0 --- examples/pom.xml | 2 +- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 ++-- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 8 ++++---- .../uhn/hapi/fhir/changelog/4_2_0/version.yaml | 3 ++- hapi-fhir-igpacks/pom.xml | 2 +- hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jaxrsserver-example/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- hapi-fhir-jpaserver-migrate/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 4 ++-- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-spring-boot-autoconfigure/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 8 ++++---- hapi-fhir-testpage-overlay/pom.xml | 2 +- hapi-fhir-validation-resources-dstu2.1/pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 16 ++++++++-------- hapi-tinder-test/pom.xml | 2 +- pom.xml | 4 ++-- restful-server-example/pom.xml | 2 +- .../pom.xml | 2 +- tests/hapi-fhir-base-test-mindeps-client/pom.xml | 2 +- tests/hapi-fhir-base-test-mindeps-server/pom.xml | 2 +- 55 files changed, 72 insertions(+), 71 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 83ae563ebbc..8deec409c9e 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index d40f599762d..312da0bc08e 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 0e7ed584440..cd409164f54 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 001073563cb..77b2068590f 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 7911327e69d..119526fd20f 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -3,14 +3,14 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 4.2.0-SNAPSHOT + 4.2.0 pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index 53f125ac491..89c44f9235d 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index c1d6985409a..6d61f35c608 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index 45108f849f7..aaf86bfbfcc 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 792990a296b..b4a22255dad 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 615e60bbac8..dc9dab20b18 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 69e09a3a601..e7b863eef89 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index cfebc8a9f15..0b3a66e4849 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 04bb687721d..d194812d833 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 83a317dfd90..ede2ede7546 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml @@ -73,13 +73,13 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 4.2.0-SNAPSHOT + 4.2.0 compile ca.uhn.hapi.fhir hapi-fhir-jpaserver-subscription - 4.2.0-SNAPSHOT + 4.2.0 compile @@ -96,7 +96,7 @@ ca.uhn.hapi.fhir hapi-fhir-testpage-overlay - 4.2.0-SNAPSHOT + 4.2.0 classes diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/version.yaml index a01f7a1402d..f4ba414d4ac 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/version.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/version.yaml @@ -1,2 +1,3 @@ --- -release-date: "TBD" +release-date: "2020-02-15" +codename: "Koala" diff --git a/hapi-fhir-igpacks/pom.xml b/hapi-fhir-igpacks/pom.xml index 4e369e28cab..7f0ae44bf82 100644 --- a/hapi-fhir-igpacks/pom.xml +++ b/hapi-fhir-igpacks/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 3cd53b5143d..5b3ab315467 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 47d8f3872fb..a0c9d09062a 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-example/pom.xml b/hapi-fhir-jaxrsserver-example/pom.xml index 2bb47ad450c..2b058e76f6d 100644 --- a/hapi-fhir-jaxrsserver-example/pom.xml +++ b/hapi-fhir-jaxrsserver-example/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 73c106c90e2..2da08e89ba5 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-migrate/pom.xml b/hapi-fhir-jpaserver-migrate/pom.xml index 345bf13d5ee..7a4ce81a94f 100644 --- a/hapi-fhir-jpaserver-migrate/pom.xml +++ b/hapi-fhir-jpaserver-migrate/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 123f22b7a3a..b4ba139c480 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 06f462936c4..0482d3b8734 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 19a6c3caf27..47ef5be9117 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 32f3752d80e..a188778891d 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml @@ -157,7 +157,7 @@ ca.uhn.hapi.fhir hapi-fhir-converter - 4.2.0-SNAPSHOT + 4.2.0 diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 56d24a8322d..05c8c51bfb2 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index e6f6a6546cd..7a9a6c16c8d 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 6460fdaa090..62463da05d4 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0-SNAPSHOT + 4.2.0 hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index ec9c4c7b647..4b14bbc6b81 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0-SNAPSHOT + 4.2.0 hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index 45d11f2b0a0..72a9b132e73 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0-SNAPSHOT + 4.2.0 hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml index 2f99de20e6b..d0b37429584 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0-SNAPSHOT + 4.2.0 hapi-fhir-spring-boot-sample-server-jpa diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index bdeeb2a13a3..28138cc22c0 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 4.2.0-SNAPSHOT + 4.2.0 hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 95bbb629d7f..2d9cf5264ac 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 83dcdd267d7..2c55ca21a5d 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index c12b498af04..4fc8c86b22b 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index cb64e29ef8d..9d793dd802f 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index e8955655dd3..f1ecef5cd18 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index f92fd6871e2..bac4c00ba05 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index e4e51da7cfa..c06d046234d 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 92dabe9671b..b8560a1c797 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index d010e06b048..b4dcd23e4cc 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml @@ -19,17 +19,17 @@ ca.uhn.hapi.fhir hapi-fhir-base - 4.2.0-SNAPSHOT + 4.2.0 ca.uhn.hapi.fhir hapi-fhir-server - 4.2.0-SNAPSHOT + 4.2.0 ca.uhn.hapi.fhir hapi-fhir-client - 4.2.0-SNAPSHOT + 4.2.0 diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index b8bca0e0dad..568697bba98 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index ad05fd25d1a..2902f10bf17 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 45aa002c077..935c071a037 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index 566d901943a..d757ad88585 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index e0284321e23..40bcf0386e4 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index afa97d77f1c..8295035c627 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 751a3b721ee..ce6b854d971 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.2.0 ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index ec1362ebd4c..a88f846151d 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 4.2.0-SNAPSHOT + 4.2.0 ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 4.2.0-SNAPSHOT + 4.2.0 ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 4.2.0-SNAPSHOT + 4.2.0 ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 4.2.0-SNAPSHOT + 4.2.0 ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 4.2.0-SNAPSHOT + 4.2.0 ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 4.2.0-SNAPSHOT + 4.2.0 ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 4.2.0-SNAPSHOT + 4.2.0 org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 9e07aca1de0..c60935fa29f 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/pom.xml b/pom.xml index 588faf587f2..99a1e59c3bb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 4.2.0-SNAPSHOT + 4.2.0 HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io @@ -606,7 +606,7 @@ - 4.1.42-SNAPSHOT + 4.2.0 1.0.2 -Dfile.encoding=UTF-8 -Xmx2048m diff --git a/restful-server-example/pom.xml b/restful-server-example/pom.xml index 69440e2adf4..12e14cbbbea 100644 --- a/restful-server-example/pom.xml +++ b/restful-server-example/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../pom.xml diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index acc1aa4da1b..4af0340f874 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index e2c2c0c28dc..b7ad20f751b 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 2b0fddd66ed..2615d3e4a4c 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.2.0 ../../pom.xml From ddc7b601ee6c4cfb611714bb57f07767998160cc Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 16 Feb 2020 19:23:22 -0500 Subject: [PATCH 06/30] Version bump to 4.3.0-SNAPSHOT --- examples/pom.xml | 2 +- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 ++-- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 8 ++++---- hapi-fhir-igpacks/pom.xml | 2 +- hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jaxrsserver-example/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- hapi-fhir-jpaserver-migrate/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 4 ++-- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-spring-boot-autoconfigure/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 8 ++++---- hapi-fhir-testpage-overlay/pom.xml | 2 +- hapi-fhir-validation-resources-dstu2.1/pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 16 ++++++++-------- hapi-tinder-test/pom.xml | 2 +- pom.xml | 2 +- restful-server-example/pom.xml | 2 +- .../pom.xml | 2 +- tests/hapi-fhir-base-test-mindeps-client/pom.xml | 2 +- tests/hapi-fhir-base-test-mindeps-server/pom.xml | 2 +- 54 files changed, 69 insertions(+), 69 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 8deec409c9e..9adf692ef09 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 312da0bc08e..cfbbacddefa 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index cd409164f54..129477c4a80 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 77b2068590f..eb562662816 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 119526fd20f..450e56c6aa0 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -3,14 +3,14 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 4.2.0 + 4.3.0-SNAPSHOT pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index 89c44f9235d..6d56d743525 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 6d61f35c608..e8b4258a8e5 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index aaf86bfbfcc..649f92bc336 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index b4a22255dad..c3152ce67f4 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index dc9dab20b18..1d01a8cb58e 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index e7b863eef89..f085e293764 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 0b3a66e4849..c346272f57d 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index d194812d833..ec6e0a219ca 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index ede2ede7546..31d5fdd86e2 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -73,13 +73,13 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 4.2.0 + 4.3.0-SNAPSHOT compile ca.uhn.hapi.fhir hapi-fhir-jpaserver-subscription - 4.2.0 + 4.3.0-SNAPSHOT compile @@ -96,7 +96,7 @@ ca.uhn.hapi.fhir hapi-fhir-testpage-overlay - 4.2.0 + 4.3.0-SNAPSHOT classes diff --git a/hapi-fhir-igpacks/pom.xml b/hapi-fhir-igpacks/pom.xml index 7f0ae44bf82..0a0fba57081 100644 --- a/hapi-fhir-igpacks/pom.xml +++ b/hapi-fhir-igpacks/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 5b3ab315467..34d8a852672 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index a0c9d09062a..d8d18016d9e 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-example/pom.xml b/hapi-fhir-jaxrsserver-example/pom.xml index 2b058e76f6d..1f342519db5 100644 --- a/hapi-fhir-jaxrsserver-example/pom.xml +++ b/hapi-fhir-jaxrsserver-example/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 2da08e89ba5..00fd60b7ff5 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-migrate/pom.xml b/hapi-fhir-jpaserver-migrate/pom.xml index 7a4ce81a94f..2820928227b 100644 --- a/hapi-fhir-jpaserver-migrate/pom.xml +++ b/hapi-fhir-jpaserver-migrate/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index b4ba139c480..83b438d3efc 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 0482d3b8734..709f74dfc10 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 47ef5be9117..a7e80f6ec98 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index a188778891d..e6234a4fec8 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml @@ -157,7 +157,7 @@ ca.uhn.hapi.fhir hapi-fhir-converter - 4.2.0 + 4.3.0-SNAPSHOT diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 05c8c51bfb2..7e17a896660 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index 7a9a6c16c8d..834ad669a7d 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 62463da05d4..31805755c96 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0 + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index 4b14bbc6b81..1d4b77cb9f0 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0 + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index 72a9b132e73..09556aa2e9d 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0 + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml index d0b37429584..a087c508c21 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0 + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-sample-server-jpa diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index 28138cc22c0..5b9868d5d9c 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 4.2.0 + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 2d9cf5264ac..4869dd92e80 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 2c55ca21a5d..1b15296e40b 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 4fc8c86b22b..2e2a8777742 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 9d793dd802f..48444b154a0 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index f1ecef5cd18..f8d1d20457a 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index bac4c00ba05..ab9eb4e3214 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index c06d046234d..cdea8e99b14 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index b8560a1c797..ffd8da62120 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index b4dcd23e4cc..6caefd3117b 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -19,17 +19,17 @@ ca.uhn.hapi.fhir hapi-fhir-base - 4.2.0 + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-server - 4.2.0 + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-client - 4.2.0 + 4.3.0-SNAPSHOT diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 568697bba98..107e75d8313 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 2902f10bf17..c4bae7d6848 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 935c071a037..d8b4b070e80 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index d757ad88585..152a124fc68 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index 40bcf0386e4..901d583f414 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index 8295035c627..708cbe421a7 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index ce6b854d971..9a7e1c13971 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0 + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index a88f846151d..4caaad9f81f 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 4.2.0 + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 4.2.0 + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 4.2.0 + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 4.2.0 + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 4.2.0 + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 4.2.0 + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 4.2.0 + 4.3.0-SNAPSHOT org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index c60935fa29f..55d2ca62a4f 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 99a1e59c3bb..495bc8afe38 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 4.2.0 + 4.3.0-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io diff --git a/restful-server-example/pom.xml b/restful-server-example/pom.xml index 12e14cbbbea..4813953ee4a 100644 --- a/restful-server-example/pom.xml +++ b/restful-server-example/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../pom.xml diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index 4af0340f874..5c69d2bfd64 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index b7ad20f751b..f3a5689802a 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 2615d3e4a4c..56809aad658 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0 + 4.3.0-SNAPSHOT ../../pom.xml From 60f16dd91d757131dbdef83359e0b8f02c323248 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 17 Feb 2020 14:28:29 -0500 Subject: [PATCH 07/30] Update to 4.3.x core JAR (#1715) * Added optionality for adding resource fetcher * Added coverage that proves invocations * Bumped version of fhir.core to latest version. Added wrapping of AssumeValidRestReferences * Added optionality for adding resource fetcher * Added coverage that proves invocations * Bumped version of fhir.core to latest version. Added wrapping of AssumeValidRestReferences * Start working on converter * Account for compile errors * Test fix * Work no validator * Work on getting core working * Add some test coverage * Many test fixes Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> --- .../main/java/example/ConverterExamples.java | 12 +-- .../java/ca/uhn/fhir/util/VersionEnum.java | 3 +- .../cli/ExportConceptMapToCsvCommand.java | 4 +- .../cli/ImportCsvToConceptMapCommand.java | 10 +- .../VersionedApiConverterInterceptor.java | 34 ++++--- .../NullVersionConverterAdvisor50.java | 8 +- .../converter/VersionConvertor_10_30Test.java | 10 +- .../converter/VersionConvertor_14_30Test.java | 3 +- .../uhn/hapi/fhir/docs/ConverterExamples.java | 13 +-- .../4_3_0/1715-update-converters.yaml | 6 ++ .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 8 +- .../dstu3/FhirResourceDaoConceptMapDstu3.java | 17 ++-- .../dstu3/FhirResourceDaoValueSetDstu3.java | 11 ++- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 2 +- .../dao/r5/FhirResourceDaoConceptMapR5.java | 2 +- .../jpa/dao/r5/FhirResourceDaoValueSetR5.java | 2 +- .../provider/TerminologyUploaderProvider.java | 6 +- ...aseJpaResourceProviderConceptMapDstu3.java | 16 ++- .../dstu3/JpaResourceProviderDstu3.java | 24 +++-- .../dstu3/JpaSystemProviderDstu3.java | 22 +++-- .../BaseJpaResourceProviderConceptMapR5.java | 2 +- .../provider/r5/JpaResourceProviderR5.java | 4 +- .../jpa/provider/r5/JpaSystemProviderR5.java | 4 +- .../uhn/fhir/jpa/term/TermReadSvcDstu3.java | 24 +++-- .../ca/uhn/fhir/jpa/term/TermReadSvcR5.java | 16 +-- .../jpa/term/TermVersionAdapterSvcDstu3.java | 9 +- .../jpa/term/TermVersionAdapterSvcR5.java | 6 +- .../FhirResourceDaoDstu2ValidateTest.java | 21 ++-- .../fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java | 4 +- .../FhirResourceDaoDstu3ValidateTest.java | 17 ++-- .../dao/r4/FhirResourceDaoR4ValidateTest.java | 14 +-- .../ValidatorAcrossVersionsTest.java | 4 +- .../extractor/SearchParamExtractorR5.java | 12 ++- .../dstu3/hapi/ctx/HapiWorkerContext.java | 18 +++- .../fhir/r5/hapi/ctx/HapiWorkerContext.java | 5 + .../hapi/validation/ValidatorWrapper.java | 57 +++++++---- .../validation/FhirInstanceValidator.java | 74 ++++++++------ .../validation/FhirInstanceValidator.java | 58 ++++++++--- .../validation/FhirInstanceValidator.java | 86 ++++++++++++---- .../hapi/validation/HapiWorkerContext.java | 10 +- .../validation/FhirInstanceValidator.java | 49 +++++++--- .../validation/FhirInstanceValidator.java | 27 +++++- .../FhirInstanceValidatorDstu3Test.java | 21 +++- ...estionnaireResponseValidatorDstu3Test.java | 2 +- .../QuestionnaireValidatorDstu3Test.java | 3 +- .../FhirInstanceValidatorR4Test.java | 36 +++++-- .../QuestionnaireValidatorR4Test.java | 4 +- .../FhirInstanceValidatorR5Test.java | 26 ++++- .../src/test/resources/r4/myconsent-ext.xml | 34 +++++++ .../test/resources/r4/myconsent-profile.xml | 42 ++++++++ .../src/test/resources/vitals.json | 97 +++++++++++++++++++ pom.xml | 2 +- 52 files changed, 735 insertions(+), 266 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1715-update-converters.yaml create mode 100644 hapi-fhir-validation/src/test/resources/r4/myconsent-ext.xml create mode 100644 hapi-fhir-validation/src/test/resources/r4/myconsent-profile.xml create mode 100644 hapi-fhir-validation/src/test/resources/vitals.json diff --git a/examples/src/main/java/example/ConverterExamples.java b/examples/src/main/java/example/ConverterExamples.java index 219b59f7a22..710e8338485 100644 --- a/examples/src/main/java/example/ConverterExamples.java +++ b/examples/src/main/java/example/ConverterExamples.java @@ -1,7 +1,7 @@ package example; -import org.hl7.fhir.converter.NullVersionConverterAdvisor30; -import org.hl7.fhir.convertors.*; +import org.hl7.fhir.convertors.conv10_30.Observation10_30; +import org.hl7.fhir.convertors.conv14_30.Questionnaire14_30; import org.hl7.fhir.exceptions.FHIRException; public class ConverterExamples { @@ -9,16 +9,12 @@ public class ConverterExamples { @SuppressWarnings("unused") public void c1020() throws FHIRException { //START SNIPPET: 1020 - // Create a converter - NullVersionConverterAdvisor30 advisor = new NullVersionConverterAdvisor30(); - VersionConvertor_10_30 converter = new VersionConvertor_10_30(advisor); - // Create an input resource to convert org.hl7.fhir.dstu2.model.Observation input = new org.hl7.fhir.dstu2.model.Observation(); input.setEncounter(new org.hl7.fhir.dstu2.model.Reference("Encounter/123")); // Convert the resource - org.hl7.fhir.dstu3.model.Observation output = converter.convertObservation(input); + org.hl7.fhir.dstu3.model.Observation output = Observation10_30.convertObservation(input); String context = output.getContext().getReference(); //END SNIPPET: 1020 } @@ -31,7 +27,7 @@ public class ConverterExamples { input.setTitle("My title"); // Convert the resource - org.hl7.fhir.dstu3.model.Questionnaire output = VersionConvertor_14_30.convertQuestionnaire(input); + org.hl7.fhir.dstu3.model.Questionnaire output = Questionnaire14_30.convertQuestionnaire(input); String context = output.getTitle(); //END SNIPPET: 1420 } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index acc92dcb2a6..99d8ccd7939 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -58,7 +58,8 @@ public enum VersionEnum { V4_0_0, V4_0_3, V4_1_0, - V4_2_0; + V4_2_0, + V4_3_0; public static VersionEnum latestVersion() { VersionEnum[] values = VersionEnum.values(); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommand.java index 2a92f8df72a..1fc92fc6595 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommand.java @@ -26,7 +26,6 @@ import org.apache.commons.cli.Options; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.apache.commons.csv.QuoteMode; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.ConceptMap; @@ -43,6 +42,7 @@ import java.util.List; import java.util.concurrent.ExecutionException; import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.hl7.fhir.convertors.conv30_40.ConceptMap30_40.convertConceptMap; public class ExportConceptMapToCsvCommand extends AbstractImportExportCsvConceptMapCommand { // TODO: Don't use qualified names for loggers in HAPI CLI. @@ -114,7 +114,7 @@ public class ExportConceptMapToCsvCommand extends AbstractImportExportCsvConcept private void convertConceptMapToCsv(org.hl7.fhir.dstu3.model.ConceptMap theConceptMap) throws ExecutionException { try { - convertConceptMapToCsv(VersionConvertor_30_40.convertConceptMap(theConceptMap)); + convertConceptMapToCsv(convertConceptMap(theConceptMap)); } catch (FHIRException fe) { throw new ExecutionException(fe); } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommand.java index d44570b5d91..a795af5510a 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommand.java @@ -28,7 +28,6 @@ import org.apache.commons.cli.Options; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent; @@ -45,7 +44,10 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; -import static org.apache.commons.lang3.StringUtils.*; +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.convertors.conv30_40.ConceptMap30_40.convertConceptMap; public class ImportCsvToConceptMapCommand extends AbstractImportExportCsvConceptMapCommand { // TODO: Don't use qualified names for loggers in HAPI CLI. @@ -154,7 +156,7 @@ public class ImportCsvToConceptMapCommand extends AbstractImportExportCsvConcept private org.hl7.fhir.dstu3.model.ConceptMap convertCsvToConceptMapDstu3() throws ExecutionException { try { - return VersionConvertor_30_40.convertConceptMap(convertCsvToConceptMapR4()); + return convertConceptMap(convertCsvToConceptMapR4()); } catch (FHIRException fe) { throw new ExecutionException(fe); } @@ -174,7 +176,7 @@ public class ImportCsvToConceptMapCommand extends AbstractImportExportCsvConcept .withFirstRecordAsHeader() .withIgnoreHeaderCase() .withIgnoreEmptyLines() - .withTrim()); + .withTrim()) ) { retVal.setUrl(conceptMapUrl); diff --git a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java index 28df8b376a1..7e2bb24d80c 100644 --- a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java +++ b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java @@ -31,7 +31,11 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import org.hl7.fhir.converter.NullVersionConverterAdvisor30; import org.hl7.fhir.converter.NullVersionConverterAdvisor40; -import org.hl7.fhir.convertors.*; +import org.hl7.fhir.convertors.VersionConvertorAdvisor30; +import org.hl7.fhir.convertors.VersionConvertorAdvisor40; +import org.hl7.fhir.convertors.VersionConvertor_10_30; +import org.hl7.fhir.convertors.VersionConvertor_10_40; +import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.model.Resource; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -40,7 +44,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.StringTokenizer; -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * This is an experimental interceptor! Use with caution as @@ -54,16 +60,12 @@ import static org.apache.commons.lang3.StringUtils.*; public class VersionedApiConverterInterceptor extends InterceptorAdapter { private final FhirContext myCtxDstu2; private final FhirContext myCtxDstu2Hl7Org; - private VersionConvertor_30_40 myVersionConvertor_30_40; - private VersionConvertor_10_40 myVersionConvertor_10_40; - private VersionConvertor_10_30 myVersionConvertor_10_30; + private final NullVersionConverterAdvisor40 advisor40; + private final NullVersionConverterAdvisor30 advisor30; public VersionedApiConverterInterceptor() { - myVersionConvertor_30_40 = new VersionConvertor_30_40(); - VersionConvertorAdvisor40 advisor40 = new NullVersionConverterAdvisor40(); - myVersionConvertor_10_40 = new VersionConvertor_10_40(advisor40); - VersionConvertorAdvisor30 advisor30 = new NullVersionConverterAdvisor30(); - myVersionConvertor_10_30 = new VersionConvertor_10_30(advisor30); + advisor40 = new NullVersionConverterAdvisor40(); + advisor30 = new NullVersionConverterAdvisor30(); myCtxDstu2 = FhirContext.forDstu2(); myCtxDstu2Hl7Org = FhirContext.forDstu2Hl7Org(); @@ -104,17 +106,17 @@ public class VersionedApiConverterInterceptor extends InterceptorAdapter { IBaseResource converted = null; try { if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU3) { - converted = myVersionConvertor_30_40.convertResource(toDstu3(responseResource), true); + converted = VersionConvertor_30_40.convertResource(toDstu3(responseResource), true); } else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.R4) { - converted = myVersionConvertor_30_40.convertResource(toR4(responseResource), true); + converted = VersionConvertor_30_40.convertResource(toR4(responseResource), true); } else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.R4) { - converted = myVersionConvertor_10_40.convertResource(toR4(responseResource)); + converted = VersionConvertor_10_40.convertResource(toR4(responseResource), advisor40); } else if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU2) { - converted = myVersionConvertor_10_40.convertResource(toDstu2(responseResource)); + converted = VersionConvertor_10_40.convertResource(toDstu2(responseResource), advisor40); } else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.DSTU3) { - converted = myVersionConvertor_10_30.convertResource(toDstu3(responseResource)); + converted = VersionConvertor_10_30.convertResource(toDstu3(responseResource), advisor30); } else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.DSTU2) { - converted = myVersionConvertor_10_30.convertResource(toDstu2(responseResource)); + converted = VersionConvertor_10_30.convertResource(toDstu2(responseResource), advisor30); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor50.java b/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor50.java index 8b00abdffad..33d703f7a88 100644 --- a/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor50.java +++ b/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor50.java @@ -27,8 +27,12 @@ import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.ValueSet; +import java.util.IdentityHashMap; + public class NullVersionConverterAdvisor50 implements VersionConvertorAdvisor50 { + private IdentityHashMap myCodeSystems = new IdentityHashMap<>(); + @Override public boolean ignoreEntry(Bundle.BundleEntryComponent src) { return false; @@ -56,11 +60,11 @@ public class NullVersionConverterAdvisor50 implements VersionConvertorAdvisor50 @Override public void handleCodeSystem(CodeSystem tgtcs, ValueSet source) throws FHIRException { - + myCodeSystems.put(source, tgtcs); } @Override public CodeSystem getCodeSystem(ValueSet src) throws FHIRException { - return null; + return myCodeSystems.get(src); } } diff --git a/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_10_30Test.java b/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_10_30Test.java index be77cdb8515..a63a0d86344 100644 --- a/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_10_30Test.java +++ b/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_10_30Test.java @@ -16,13 +16,10 @@ public class VersionConvertor_10_30Test { @Test public void testConvert() throws FHIRException { - NullVersionConverterAdvisor30 advisor = new NullVersionConverterAdvisor30(); - VersionConvertor_10_30 converter = new VersionConvertor_10_30(advisor); - org.hl7.fhir.dstu2.model.Observation input = new org.hl7.fhir.dstu2.model.Observation(); input.setEncounter(new org.hl7.fhir.dstu2.model.Reference("Encounter/123")); - org.hl7.fhir.dstu3.model.Observation output = converter.convertObservation(input); + org.hl7.fhir.dstu3.model.Observation output = (Observation) VersionConvertor_10_30.convertResource(input); String context = output.getContext().getReference(); assertEquals("Encounter/123", context); @@ -31,9 +28,6 @@ public class VersionConvertor_10_30Test { @Test public void testConvertSpecimen() throws FHIRException { - NullVersionConverterAdvisor30 advisor = new NullVersionConverterAdvisor30(); - VersionConvertor_10_30 converter = new VersionConvertor_10_30(advisor); - Specimen spec = new Specimen(); CodeableConcept cc = new CodeableConcept(); Coding coding = new Coding(); @@ -58,7 +52,7 @@ public class VersionConvertor_10_30Test { Specimen.SpecimenContainerComponent specimenContainerComponent = new Specimen.SpecimenContainerComponent(); specimenContainerComponent.getExtension().add(new Extension().setUrl("some_url").setValue(new StringType("some_value"))); spec.setContainer(Collections.singletonList(specimenContainerComponent)); - Resource resource = converter.convertResource(spec); + Resource resource = VersionConvertor_10_30.convertResource(spec); } diff --git a/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_14_30Test.java b/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_14_30Test.java index 14fa9db116a..8914d474e01 100644 --- a/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_14_30Test.java +++ b/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_14_30Test.java @@ -3,6 +3,7 @@ package org.hl7.fhir.converter; import static org.junit.Assert.assertEquals; import org.hl7.fhir.convertors.VersionConvertor_14_30; +import org.hl7.fhir.dstu3.model.Questionnaire; import org.hl7.fhir.exceptions.FHIRException; import org.junit.Test; @@ -14,7 +15,7 @@ public class VersionConvertor_14_30Test { org.hl7.fhir.dstu2016may.model.Questionnaire input = new org.hl7.fhir.dstu2016may.model.Questionnaire(); input.setTitle("My title"); - org.hl7.fhir.dstu3.model.Questionnaire output = VersionConvertor_14_30.convertQuestionnaire(input); + org.hl7.fhir.dstu3.model.Questionnaire output = (Questionnaire) VersionConvertor_14_30.convertResource(input); String context = output.getTitle(); assertEquals("My title", context); diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConverterExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConverterExamples.java index 5da62480bdc..0da2042b969 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConverterExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConverterExamples.java @@ -20,9 +20,8 @@ package ca.uhn.hapi.fhir.docs; * #L% */ -import org.hl7.fhir.converter.NullVersionConverterAdvisor30; -import org.hl7.fhir.convertors.VersionConvertor_10_30; -import org.hl7.fhir.convertors.VersionConvertor_14_30; +import org.hl7.fhir.convertors.conv10_30.Observation10_30; +import org.hl7.fhir.convertors.conv14_30.Questionnaire14_30; import org.hl7.fhir.exceptions.FHIRException; public class ConverterExamples { @@ -30,16 +29,12 @@ public class ConverterExamples { @SuppressWarnings("unused") public void c1020() throws FHIRException { //START SNIPPET: 1020 - // Create a converter - NullVersionConverterAdvisor30 advisor = new NullVersionConverterAdvisor30(); - VersionConvertor_10_30 converter = new VersionConvertor_10_30(advisor); - // Create an input resource to convert org.hl7.fhir.dstu2.model.Observation input = new org.hl7.fhir.dstu2.model.Observation(); input.setEncounter(new org.hl7.fhir.dstu2.model.Reference("Encounter/123")); // Convert the resource - org.hl7.fhir.dstu3.model.Observation output = converter.convertObservation(input); + org.hl7.fhir.dstu3.model.Observation output = Observation10_30.convertObservation(input); String context = output.getContext().getReference(); //END SNIPPET: 1020 } @@ -52,7 +47,7 @@ public class ConverterExamples { input.setTitle("My title"); // Convert the resource - org.hl7.fhir.dstu3.model.Questionnaire output = VersionConvertor_14_30.convertQuestionnaire(input); + org.hl7.fhir.dstu3.model.Questionnaire output = Questionnaire14_30.convertQuestionnaire(input); String context = output.getTitle(); //END SNIPPET: 1420 } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1715-update-converters.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1715-update-converters.yaml new file mode 100644 index 00000000000..874c6fe3520 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1715-update-converters.yaml @@ -0,0 +1,6 @@ +--- +type: change +issue: 1715 +title: The version converters for all versions except R4/R5 have been reworked to be split into individual + classes per resource type (the R4/R5 converters were already organized this way). Thanks to Mark Iantorno + for a huge effort to write a Java source parser/serializer to acomplish this task. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 1377a5287cd..0947af92164 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -24,10 +24,10 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; -import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; +import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; @@ -35,7 +35,6 @@ import ca.uhn.fhir.jpa.util.LogicUtil; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeableConcept; @@ -53,6 +52,7 @@ import java.util.List; import java.util.Set; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; @Transactional public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoCodeSystem { @@ -147,7 +147,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoConceptMap { @Autowired private ITermReadSvc myHapiTerminologySvc; @@ -166,7 +171,7 @@ public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoValueSet { private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoValueSetDstu3.class); @@ -420,7 +423,7 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) { if (retVal.getDeleted() == null) { ValueSet valueSet = (ValueSet) theResource; - myHapiTerminologySvc.storeTermValueSet(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSet)); + myHapiTerminologySvc.storeTermValueSet(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSet)); } else { myHapiTerminologySvc.deleteValueSetAndChildren(retVal); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java index d6ee3364a47..90a27e56c48 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java @@ -39,7 +39,6 @@ import ca.uhn.fhir.util.ValidateUtil; import com.google.common.base.Charsets; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; @@ -61,6 +60,7 @@ import java.util.Map; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; +import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; public class TerminologyUploaderProvider extends BaseJpaProvider { @@ -272,10 +272,10 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { CodeSystem nextCodeSystem; switch (getContext().getVersion().getVersion()) { case DSTU3: - nextCodeSystem = VersionConvertor_30_40.convertCodeSystem((org.hl7.fhir.dstu3.model.CodeSystem) theCodeSystem); + nextCodeSystem = convertCodeSystem((org.hl7.fhir.dstu3.model.CodeSystem) theCodeSystem); break; case R5: - nextCodeSystem = org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem); + nextCodeSystem = org.hl7.fhir.convertors.conv40_50.CodeSystem40_50.convertCodeSystem((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem); break; default: nextCodeSystem = (CodeSystem) theCodeSystem; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java index 7bcfee951a4..0f42082bbef 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java @@ -21,9 +21,9 @@ package ca.uhn.fhir.jpa.provider.dstu3; */ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoConceptMap; +import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.term.TranslationRequest; import ca.uhn.fhir.jpa.term.TranslationResult; -import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -31,11 +31,21 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import org.hl7.fhir.convertors.VersionConvertor_30_40; -import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.CodeType; +import org.hl7.fhir.dstu3.model.CodeableConcept; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.ConceptMap; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.exceptions.FHIRException; import javax.servlet.http.HttpServletRequest; +import static org.hl7.fhir.convertors.conv30_40.Parameters30_40.convertParameters; + public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderDstu3 { @Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1), @@ -129,7 +139,7 @@ public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderD TranslationResult result = dao.translate(translationRequest, theRequestDetails); // Convert from R4 to DSTU3 - return VersionConvertor_30_40.convertParameters(result.toParameters()); + return convertParameters(result.toParameters()); } catch (FHIRException fe) { throw new InternalErrorException(fe); } finally { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java index e6376d2ddf1..42aec253031 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java @@ -21,17 +21,28 @@ package ca.uhn.fhir.jpa.provider.dstu3; */ import ca.uhn.fhir.jpa.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import org.hl7.fhir.convertors.VersionConvertor_30_40; -import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.IntegerType; +import org.hl7.fhir.dstu3.model.Meta; +import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -41,6 +52,7 @@ import javax.servlet.http.HttpServletRequest; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_ADD; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_DELETE; +import static org.hl7.fhir.convertors.conv30_40.Parameters30_40.convertParameters; public class JpaResourceProviderDstu3 extends BaseJpaResourceProvider { @@ -91,7 +103,7 @@ public class JpaResourceProviderDstu3 extends BaseJpaRes RequestDetails theRequest) { org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest); try { - return VersionConvertor_30_40.convertParameters(retVal); + return convertParameters(retVal); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -107,7 +119,7 @@ public class JpaResourceProviderDstu3 extends BaseJpaRes RequestDetails theRequest) { org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest); try { - return VersionConvertor_30_40.convertParameters(retVal); + return convertParameters(retVal); } catch (FHIRException e) { throw new InternalErrorException(e); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java index f345c6f7361..c1f1970a0c1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java @@ -3,17 +3,26 @@ package ca.uhn.fhir.jpa.provider.dstu3; import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus; import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus; import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Transaction; +import ca.uhn.fhir.rest.annotation.TransactionParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import org.hl7.fhir.convertors.VersionConvertor_30_40; -import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.DecimalType; +import org.hl7.fhir.dstu3.model.IntegerType; +import org.hl7.fhir.dstu3.model.Meta; +import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IIdType; @@ -29,6 +38,7 @@ import java.util.TreeMap; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.hl7.fhir.convertors.conv30_40.Parameters30_40.convertParameters; /* * #%L @@ -72,7 +82,7 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus dao = (IFhirResourceDaoConceptMap) getDao(); TranslationResult result = dao.translate(translationRequest, theRequestDetails); org.hl7.fhir.r4.model.Parameters parameters = result.toParameters(); - return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters); + return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java index e3e4c6b54de..ef86e4f478b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java @@ -86,7 +86,7 @@ public class JpaResourceProviderR5 extends BaseJpaResour RequestDetails theRequest) { org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest); - return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters); + return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters); } @@ -99,7 +99,7 @@ public class JpaResourceProviderR5 extends BaseJpaResour @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions, RequestDetails theRequest) { org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest); - return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters); + return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters); } @Operation(name = OPERATION_META, idempotent = true, returnParameters = { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java index 7bacb42b6ba..4a99a931bbe 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java @@ -68,7 +68,7 @@ public class JpaSystemProviderR5 extends BaseJpaSystemProviderDstu2Plus T fetchResource(Class theClass, String theUri) { + Validate.notBlank(theUri, "theUri must not be null or blank"); if (myValidationSupport == null) { return null; } else { - @SuppressWarnings("unchecked") - T retVal = (T) myFetchedResourceCache.get(theUri, t->{ - return myValidationSupport.fetchResource(myCtx, theClass, theUri); - }); - return retVal; + try { + //noinspection unchecked + return (T) myFetchedResourceCache.get(theUri, t -> { + T resource = myValidationSupport.fetchResource(myCtx, theClass, theUri); + if (resource == null) { + throw new IllegalArgumentException(); + } + return resource; + }); + } catch (IllegalArgumentException e) { + return null; + } } } diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java index c48a81f8f5e..c88ada2d294 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java @@ -343,6 +343,11 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public StructureDefinition fetchRawProfile(String url) { + throw new UnsupportedOperationException(); + } + @Override public List getTypeNames() { throw new UnsupportedOperationException(); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java index d1c28f2b274..37323be9e1c 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java @@ -4,20 +4,24 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.util.XmlUtil; import ca.uhn.fhir.validation.IValidationContext; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.apache.commons.codec.Charsets; import org.apache.commons.io.input.ReaderInputStream; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Manager; +import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.r5.utils.ValidationProfileSet; import org.hl7.fhir.r5.validation.InstanceValidator; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.io.InputStream; @@ -33,7 +37,9 @@ public class ValidatorWrapper { private boolean myAnyExtensionsAllowed; private boolean myErrorForUnknownProfiles; private boolean myNoTerminologyChecks; + private boolean myAssumeValidRestReferences; private Collection myExtensionDomains; + private IResourceValidator.IValidatorResourceFetcher myValidatorResourceFetcher; /** * Constructor @@ -42,6 +48,15 @@ public class ValidatorWrapper { super(); } + public boolean isAssumeValidRestReferences() { + return myAssumeValidRestReferences; + } + + public ValidatorWrapper setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.myAssumeValidRestReferences = assumeValidRestReferences; + return this; + } + public ValidatorWrapper setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel theBestPracticeWarningLevel) { myBestPracticeWarningLevel = theBestPracticeWarningLevel; return this; @@ -67,6 +82,12 @@ public class ValidatorWrapper { return this; } + + public ValidatorWrapper setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.myValidatorResourceFetcher = validatorResourceFetcher; + return this; + } + public List validate(IWorkerContext theWorkerContext, IValidationContext theValidationContext) { InstanceValidator v; FHIRPathEngine.IEvaluationContext evaluationCtx = new org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator.NullEvaluationContext(); @@ -76,19 +97,21 @@ public class ValidatorWrapper { throw new ConfigurationException(e); } + v.setAssumeValidRestReferences(isAssumeValidRestReferences()); v.setBestPracticeWarningLevel(myBestPracticeWarningLevel); v.setAnyExtensionsAllowed(myAnyExtensionsAllowed); v.setResourceIdRule(IResourceValidator.IdStatus.OPTIONAL); v.setNoTerminologyChecks(myNoTerminologyChecks); v.setErrorForUnknownProfiles(myErrorForUnknownProfiles); v.getExtensionDomains().addAll(myExtensionDomains); + v.setFetcher(myValidatorResourceFetcher); v.setAllowXsiLocation(true); List messages = new ArrayList<>(); - ValidationProfileSet profileSet = new ValidationProfileSet(); + List profileUrls = new ArrayList<>(); for (String next : theValidationContext.getOptions().getProfiles()) { - profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(next, true)); + fetchAndAddProfile(theWorkerContext, profileUrls, next); } String input = theValidationContext.getResourceAsString(); @@ -109,14 +132,14 @@ public class ValidatorWrapper { // Determine if meta/profiles are present... ArrayList profiles = determineIfProfilesSpecified(document); for (String nextProfile : profiles) { - profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(nextProfile, true)); + fetchAndAddProfile(theWorkerContext, profileUrls, nextProfile); } String resourceAsString = theValidationContext.getResourceAsString(); InputStream inputStream = new ReaderInputStream(new StringReader(resourceAsString), Charsets.UTF_8); Manager.FhirFormat format = Manager.FhirFormat.XML; - v.validate(null, messages, inputStream, format, profileSet); + v.validate(null, messages, inputStream, format, profileUrls); } else if (encoding == EncodingEnum.JSON) { @@ -129,7 +152,8 @@ public class ValidatorWrapper { if (profileElement != null && profileElement.isJsonArray()) { JsonArray profiles = profileElement.getAsJsonArray(); for (JsonElement element : profiles) { - profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(element.getAsString(), true)); + String nextProfile = element.getAsString(); + fetchAndAddProfile(theWorkerContext, profileUrls, nextProfile); } } } @@ -138,7 +162,7 @@ public class ValidatorWrapper { InputStream inputStream = new ReaderInputStream(new StringReader(resourceAsString), Charsets.UTF_8); Manager.FhirFormat format = Manager.FhirFormat.JSON; - v.validate(null, messages, inputStream, format, profileSet); + v.validate(null, messages, inputStream, format, profileUrls); } else { throw new IllegalArgumentException("Unknown encoding: " + encoding); @@ -157,17 +181,16 @@ public class ValidatorWrapper { return messages; } - - private String determineResourceName(Document theDocument) { - NodeList list = theDocument.getChildNodes(); - for (int i = 0; i < list.getLength(); i++) { - if (list.item(i) instanceof Element) { - return list.item(i).getLocalName(); - } + private void fetchAndAddProfile(IWorkerContext theWorkerContext, List theProfileStructureDefinitions, String theUrl) throws org.hl7.fhir.exceptions.FHIRException { + try { + StructureDefinition structureDefinition = theWorkerContext.fetchResourceWithException(StructureDefinition.class, theUrl); + theProfileStructureDefinitions.add(structureDefinition); + } catch (FHIRException e) { + ourLog.debug("Failed to load profile: {}", theUrl); } - return theDocument.getDocumentElement().getLocalName(); } + private ArrayList determineIfProfilesSpecified(Document theDocument) { ArrayList profileNames = new ArrayList<>(); NodeList list = theDocument.getChildNodes().item(0).getChildNodes(); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/FhirInstanceValidator.java index e583381a5ee..e4ab01d7f78 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/FhirInstanceValidator.java @@ -1,14 +1,10 @@ package org.hl7.fhir.dstu2016may.hapi.validation; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.*; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.validation.IValidationContext; +import ca.uhn.fhir.validation.IValidatorModule; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import org.apache.commons.lang3.Validate; @@ -18,28 +14,45 @@ import org.apache.commons.lang3.time.DateUtils; import org.fhir.ucum.UcumService; import org.hl7.fhir.common.hapi.validation.ValidatorWrapper; import org.hl7.fhir.convertors.VersionConvertor_14_50; +import org.hl7.fhir.convertors.conv14_50.CodeSystem14_50; +import org.hl7.fhir.convertors.conv14_50.StructureDefinition14_50; +import org.hl7.fhir.convertors.conv14_50.ValueSet14_50; +import org.hl7.fhir.dstu2016may.model.CodeSystem; +import org.hl7.fhir.dstu2016may.model.CodeableConcept; +import org.hl7.fhir.dstu2016may.model.Coding; +import org.hl7.fhir.dstu2016may.model.ImplementationGuide; +import org.hl7.fhir.dstu2016may.model.Questionnaire; +import org.hl7.fhir.dstu2016may.model.StructureDefinition; +import org.hl7.fhir.dstu2016may.model.ValueSet; import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.formats.ParserType; import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.utils.INarrativeGenerator; import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.r5.model.Resource; -import org.hl7.fhir.dstu2016may.model.*; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationOptions; -import org.w3c.dom.*; +import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.NodeList; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.validation.IValidationContext; -import ca.uhn.fhir.validation.IValidatorModule; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule { @@ -382,7 +395,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid } public org.hl7.fhir.r5.model.StructureDefinition convert(StructureDefinition next) { - org.hl7.fhir.r5.model.StructureDefinition structureDefinition = VersionConvertor_14_50.convertStructureDefinition(next); + org.hl7.fhir.r5.model.StructureDefinition structureDefinition = StructureDefinition14_50.convertStructureDefinition(next); if (next.getDerivation() != org.hl7.fhir.dstu2016may.model.StructureDefinition.TypeDerivationRule.CONSTRAINT) { structureDefinition.setType(next.getName()); } @@ -408,7 +421,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null; if (theResult.asConceptDefinition() != null) { try { - conceptDefinition = VersionConvertor_14_50.convertConceptDefinitionComponent(theResult.asConceptDefinition()); + conceptDefinition = CodeSystem14_50.convertConceptDefinitionComponent(theResult.asConceptDefinition()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -428,7 +441,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid public org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) { ValueSet convertedSource; try { - convertedSource = VersionConvertor_14_50.convertValueSet(source); + convertedSource = ValueSet14_50.convertValueSet(source); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -437,7 +450,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid org.hl7.fhir.r5.model.ValueSet convertedResult = null; if (expanded.getValueset() != null) { try { - convertedResult = VersionConvertor_14_50.convertValueSet(expanded.getValueset()); + convertedResult = ValueSet14_50.convertValueSet(expanded.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -458,7 +471,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid ValueSet.ConceptSetComponent convertedInc = null; if (inc != null) { try { - convertedInc = VersionConvertor_14_50.convertConceptSetComponent(inc); + convertedInc = ValueSet14_50.convertConceptSetComponent(inc); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -468,7 +481,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent valueSetExpansionComponent = null; if (expansion != null) { try { - valueSetExpansionComponent = VersionConvertor_14_50.convertValueSetExpansionComponent(expansion); + valueSetExpansionComponent = ValueSet14_50.convertValueSetExpansionComponent(expansion); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -492,7 +505,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid return null; } try { - return VersionConvertor_14_50.convertCodeSystem(fetched); + return CodeSystem14_50.convertCodeSystem(fetched); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -572,6 +585,11 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public org.hl7.fhir.r5.model.StructureDefinition fetchRawProfile(String url) { + return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, url); + } + @Override public List getTypeNames() { @@ -665,7 +683,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid try { if (vs != null) { - convertedVs = VersionConvertor_14_50.convertValueSet(vs); + convertedVs = ValueSet14_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -680,7 +698,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid ValueSet convertedVs = null; try { if (vs != null) { - convertedVs = VersionConvertor_14_50.convertValueSet(vs); + convertedVs = ValueSet14_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -700,7 +718,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid convertedCode = VersionConvertor_14_50.convertCoding(code); } if (vs != null) { - convertedVs = VersionConvertor_14_50.convertValueSet(vs); + convertedVs = ValueSet14_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -720,7 +738,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid convertedCode = VersionConvertor_14_50.convertCodeableConcept(code); } if (vs != null) { - convertedVs = VersionConvertor_14_50.convertValueSet(vs); + convertedVs = ValueSet14_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java index ba3c7a8755f..609941ba6a9 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java @@ -23,6 +23,7 @@ import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.ImplementationGuide; import org.hl7.fhir.dstu3.model.Questionnaire; import org.hl7.fhir.dstu3.model.Resource; +import org.hl7.fhir.dstu3.model.SearchParameter; import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.exceptions.FHIRException; @@ -56,6 +57,13 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import static org.hl7.fhir.convertors.conv30_50.CodeSystem30_50.convertCodeSystem; +import static org.hl7.fhir.convertors.conv30_50.CodeSystem30_50.convertConceptDefinitionComponent; +import static org.hl7.fhir.convertors.conv30_50.StructureDefinition30_50.convertStructureDefinition; +import static org.hl7.fhir.convertors.conv30_50.ValueSet30_50.convertConceptSetComponent; +import static org.hl7.fhir.convertors.conv30_50.ValueSet30_50.convertValueSet; +import static org.hl7.fhir.convertors.conv30_50.ValueSet30_50.convertValueSetExpansionComponent; + @SuppressWarnings({"PackageAccessibility", "Duplicates"}) public class FhirInstanceValidator extends BaseValidatorBridge implements IInstanceValidatorModule { @@ -66,10 +74,12 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta private StructureDefinition myStructureDefintion; private IValidationSupport myValidationSupport; private boolean noTerminologyChecks = false; + private IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher; private volatile WorkerContextWrapper myWrappedWorkerContext; private boolean errorForUnknownProfiles; private List myExtensionDomains = Collections.emptyList(); + private boolean assumeValidRestReferences; /** * Constructor @@ -285,10 +295,28 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta .setErrorForUnknownProfiles(isErrorForUnknownProfiles()) .setExtensionDomains(getExtensionDomains()) .setNoTerminologyChecks(isNoTerminologyChecks()) + .setValidatorResourceFetcher(getValidatorResourceFetcher()) + .setAssumeValidRestReferences(isAssumeValidRestReferences()) .validate(wrappedWorkerContext, theValidationCtx); } + public IResourceValidator.IValidatorResourceFetcher getValidatorResourceFetcher() { + return validatorResourceFetcher; + } + + public void setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.validatorResourceFetcher = validatorResourceFetcher; + } + + public boolean isAssumeValidRestReferences() { + return assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.assumeValidRestReferences = assumeValidRestReferences; + } + private static class WorkerContextWrapper implements IWorkerContext { private final HapiWorkerContext myWrap; @@ -327,6 +355,9 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta case "ImplementationGuide": fetched = myWrap.fetchResource(ImplementationGuide.class, key.getUri()); break; + case "SearchParameter": + fetched = myWrap.fetchResource(SearchParameter.class, key.getUri()); + break; default: throw new UnsupportedOperationException("Don't know how to fetch " + key.getResourceName()); } @@ -386,7 +417,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta retVal = new ArrayList<>(); for (StructureDefinition next : myWrap.allStructures()) { try { - retVal.add(VersionConvertor_30_50.convertStructureDefinition(next)); + retVal.add(convertStructureDefinition(next)); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -416,7 +447,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null; if (theResult.asConceptDefinition() != null) { try { - conceptDefinition = VersionConvertor_30_50.convertConceptDefinitionComponent(theResult.asConceptDefinition()); + conceptDefinition = convertConceptDefinitionComponent(theResult.asConceptDefinition()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -436,7 +467,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) { ValueSet convertedSource; try { - convertedSource = VersionConvertor_30_50.convertValueSet(source); + convertedSource = convertValueSet(source); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -445,7 +476,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.ValueSet convertedResult = null; if (expanded.getValueset() != null) { try { - convertedResult = VersionConvertor_30_50.convertValueSet(expanded.getValueset()); + convertedResult = convertValueSet(expanded.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -467,7 +498,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta ValueSet.ConceptSetComponent convertedInc = null; if (inc != null) { try { - convertedInc = VersionConvertor_30_50.convertConceptSetComponent(inc); + convertedInc = convertConceptSetComponent(inc); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -477,7 +508,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent valueSetExpansionComponent = null; if (expansion != null) { try { - valueSetExpansionComponent = VersionConvertor_30_50.convertValueSetExpansionComponent(expansion); + valueSetExpansionComponent = convertValueSetExpansionComponent(expansion); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -495,7 +526,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return null; } try { - return VersionConvertor_30_50.convertCodeSystem(fetched); + return convertCodeSystem(fetched); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -584,6 +615,11 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public org.hl7.fhir.r5.model.StructureDefinition fetchRawProfile(String url) { + return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, url); + } + @Override public List getTypeNames() { return myWrap.getTypeNames(); @@ -676,7 +712,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta try { if (vs != null) { - convertedVs = VersionConvertor_30_50.convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -691,7 +727,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta ValueSet convertedVs = null; try { if (vs != null) { - convertedVs = VersionConvertor_30_50.convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -711,7 +747,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta convertedCode = VersionConvertor_30_50.convertCoding(code); } if (vs != null) { - convertedVs = VersionConvertor_30_50.convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -731,7 +767,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta convertedCode = VersionConvertor_30_50.convertCodeableConcept(code); } if (vs != null) { - convertedVs = VersionConvertor_30_50.convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java index a92e2a957af..877c9cfa880 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java @@ -27,12 +27,13 @@ import org.fhir.ucum.UcumService; import org.hl7.fhir.converter.NullVersionConverterAdvisor50; import org.hl7.fhir.convertors.VersionConvertorAdvisor50; import org.hl7.fhir.convertors.VersionConvertor_10_50; +import org.hl7.fhir.convertors.conv10_50.ValueSet10_50; +import org.hl7.fhir.convertors.conv14_50.CodeSystem14_50; import org.hl7.fhir.dstu2.model.CodeableConcept; import org.hl7.fhir.dstu2.model.Coding; import org.hl7.fhir.dstu2.model.Questionnaire; import org.hl7.fhir.dstu2.model.StructureDefinition; import org.hl7.fhir.dstu2.model.ValueSet; -import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.r5.context.IWorkerContext; @@ -60,6 +61,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -69,6 +71,11 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.hl7.fhir.convertors.VersionConvertor_10_50.convertCoding; +import static org.hl7.fhir.convertors.conv10_50.StructureDefinition10_50.convertStructureDefinition; +import static org.hl7.fhir.convertors.conv10_50.ValueSet10_50.convertConceptSetComponent; +import static org.hl7.fhir.convertors.conv10_50.ValueSet10_50.convertValueSet; +import static org.hl7.fhir.convertors.conv10_50.ValueSet10_50.convertValueSetExpansionComponent; public class FhirInstanceValidator extends BaseValidatorBridge implements IInstanceValidatorModule { @@ -81,8 +88,19 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta private StructureDefinition myStructureDefintion; private IValidationSupport myValidationSupport; private boolean noTerminologyChecks = false; + + public boolean isAssumeValidRestReferences() { + return assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.assumeValidRestReferences = assumeValidRestReferences; + } + + private boolean assumeValidRestReferences; private volatile WorkerContextWrapper myWrappedWorkerContext; private VersionConvertorAdvisor50 myAdvisor = new NullVersionConverterAdvisor50(); + private IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher; /** * Constructor @@ -287,6 +305,8 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta v.setAnyExtensionsAllowed(isAnyExtensionsAllowed()); v.setResourceIdRule(IdStatus.OPTIONAL); v.setNoTerminologyChecks(isNoTerminologyChecks()); + v.setFetcher(getValidatorResourceFetcher()); + v.setAssumeValidRestReferences(isAssumeValidRestReferences()); List messages = new ArrayList<>(); @@ -388,6 +408,14 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return messages; } + public IResourceValidator.IValidatorResourceFetcher getValidatorResourceFetcher() { + return validatorResourceFetcher; + } + + public void setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.validatorResourceFetcher = validatorResourceFetcher; + } + @Override protected List validate(IValidationContext theCtx) { return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding()); @@ -428,7 +456,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return null; } } - profileText = IOUtils.toString(inputStream, "UTF-8"); + profileText = IOUtils.toString(inputStream, StandardCharsets.UTF_8); } catch (IOException e1) { if (theMessages != null) { theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL) @@ -457,11 +485,17 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta case "StructureDefinition": fetched = myWrap.fetchResource(StructureDefinition.class, key.getUri()); break; + case "CodeSystem": case "ValueSet": fetched = myWrap.fetchResource(ValueSet.class, key.getUri()); - break; - case "CodeSystem": - fetched = myWrap.fetchResource(ValueSet.class, key.getUri()); + + ValueSet fetchedVs = (ValueSet) fetched; + if (!fetchedVs.hasCompose()) { + if (fetchedVs.hasCodeSystem()) { + fetchedVs.getCompose().addInclude().setSystem(fetchedVs.getCodeSystem().getSystem()); + } + } + break; case "Questionnaire": fetched = myWrap.fetchResource(Questionnaire.class, key.getUri()); @@ -477,7 +511,15 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta } try { - org.hl7.fhir.r5.model.Resource converted = new VersionConvertor_10_50(myAdvisor).convertResource(fetched); + org.hl7.fhir.r5.model.Resource converted; + if ("CodeSystem".equals(key.getUri())) { + NullVersionConverterAdvisor50 advisor = new NullVersionConverterAdvisor50(); + converted = ValueSet10_50.convertValueSet((ValueSet) fetched, advisor); + converted = advisor.getCodeSystem((org.hl7.fhir.r5.model.ValueSet) converted); + } else { + converted = VersionConvertor_10_50.convertResource(fetched); + } + if (fetched instanceof StructureDefinition) { StructureDefinition fetchedSd = (StructureDefinition) fetched; @@ -501,7 +543,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta public WorkerContextWrapper(HapiWorkerContext theWorkerContext) { myWrap = theWorkerContext; - myConverter = new VersionConvertor_10_50(myAdvisor); + myConverter = new VersionConvertor_10_50(); } @Override @@ -510,7 +552,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta } @Override - public void generateSnapshot(org.hl7.fhir.r5.model.StructureDefinition p) throws DefinitionException, FHIRException { + public void generateSnapshot(org.hl7.fhir.r5.model.StructureDefinition p) throws FHIRException { } @@ -547,7 +589,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta retVal = new ArrayList<>(); for (StructureDefinition next : myWrap.allStructures()) { try { - org.hl7.fhir.r5.model.StructureDefinition converted = new VersionConvertor_10_50(myAdvisor).convertStructureDefinition(next); + org.hl7.fhir.r5.model.StructureDefinition converted = convertStructureDefinition(next); if (converted != null) { retVal.add(converted); } @@ -587,7 +629,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) { ValueSet convertedSource = null; try { - convertedSource = new VersionConvertor_10_50(myAdvisor).convertValueSet(source); + convertedSource = convertValueSet(source); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -596,7 +638,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.ValueSet convertedResult = null; if (expanded.getValueset() != null) { try { - convertedResult = new VersionConvertor_10_50(myAdvisor).convertValueSet(expanded.getValueset()); + convertedResult = convertValueSet(expanded.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -618,7 +660,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta ValueSet.ConceptSetComponent convertedInc = null; if (inc != null) { try { - convertedInc = new VersionConvertor_10_50(myAdvisor).convertConceptSetComponent(inc); + convertedInc = convertConceptSetComponent(inc); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -628,7 +670,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent valueSetExpansionComponent = null; if (expansion != null) { try { - valueSetExpansionComponent = new VersionConvertor_10_50(myAdvisor).convertValueSetExpansionComponent(expansion); + valueSetExpansionComponent = convertValueSetExpansionComponent(expansion); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -733,6 +775,11 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+typeName); } + @Override + public org.hl7.fhir.r5.model.StructureDefinition fetchRawProfile(String url) { + return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, url); + } + @Override public void setUcumService(UcumService ucumService) { throw new UnsupportedOperationException(); @@ -825,7 +872,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta try { if (vs != null) { - convertedVs = new VersionConvertor_10_50(myAdvisor).convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -840,8 +887,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta ValueSet convertedVs = null; try { if (vs != null) { - VersionConvertorAdvisor50 advisor50 = new NullVersionConverterAdvisor50(); - convertedVs = new VersionConvertor_10_50(advisor50).convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -858,10 +904,10 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta try { if (code != null) { - convertedCode = new VersionConvertor_10_50(myAdvisor).convertCoding(code); + convertedCode = convertCoding(code); } if (vs != null) { - convertedVs = new VersionConvertor_10_50(myAdvisor).convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -878,10 +924,10 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta try { if (code != null) { - convertedCode = new VersionConvertor_10_50(myAdvisor).convertCodeableConcept(code); + convertedCode = VersionConvertor_10_50.convertCodeableConcept(code); } if (vs != null) { - convertedVs = new VersionConvertor_10_50(myAdvisor).convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/HapiWorkerContext.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/HapiWorkerContext.java index 9cdae210cd8..1788fd582a5 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/HapiWorkerContext.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/HapiWorkerContext.java @@ -199,7 +199,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander nextSystem = nextComposeConceptSet.getSystem(); } - if (StringUtils.equals(nextSystem, nextComposeConceptSet.getSystem())) { + if (Constants.codeSystemNotNeeded(theSystem) || StringUtils.equals(nextSystem, nextComposeConceptSet.getSystem())) { for (ConceptReferenceComponent nextComposeCode : nextComposeConceptSet.getConcept()) { ConceptDefinitionComponent conceptDef = new ConceptDefinitionComponent(); conceptDef.setCode(nextComposeCode.getCode()); @@ -211,7 +211,13 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander } if (nextComposeConceptSet.getConcept().isEmpty()){ - ValidationResult result = validateCode(nextSystem, theCode, null); + + String validateSystem = nextSystem; + if (Constants.codeSystemNotNeeded(nextSystem)) { + validateSystem = nextComposeConceptSet.getSystem(); + } + + ValidationResult result = validateCode(validateSystem, theCode, null); if (result.isOk()){ return result; } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/FhirInstanceValidator.java index d7c52d229a1..d5441294501 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/FhirInstanceValidator.java @@ -59,7 +59,8 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV private IValidationSupport myValidationSupport; private boolean noTerminologyChecks = false; private volatile WorkerContextWrapper myWrappedWorkerContext; - + private IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher; + private boolean assumeValidRestReferences; private boolean errorForUnknownProfiles; private List extensionDomains = Collections.emptyList(); @@ -225,13 +226,32 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV .setErrorForUnknownProfiles(isErrorForUnknownProfiles()) .setExtensionDomains(getExtensionDomains()) .setNoTerminologyChecks(isNoTerminologyChecks()) + .setValidatorResourceFetcher(getValidatorResourceFetcher()) + .setAssumeValidRestReferences(isAssumeValidRestReferences()) .validate(wrappedWorkerContext, theValidationCtx); } + public IResourceValidator.IValidatorResourceFetcher getValidatorResourceFetcher() { + return validatorResourceFetcher; + } + + public void setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.validatorResourceFetcher = validatorResourceFetcher; + } + + private List getExtensionDomains() { return extensionDomains; } + public boolean isAssumeValidRestReferences() { + return assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.assumeValidRestReferences = assumeValidRestReferences; + } + private static class WorkerContextWrapper implements IWorkerContext { private final HapiWorkerContext myWrap; @@ -317,7 +337,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV retVal = new ArrayList<>(); for (StructureDefinition next : myWrap.allStructures()) { try { - retVal.add(org.hl7.fhir.convertors.conv40_50.StructureDefinition.convertStructureDefinition(next)); + retVal.add(org.hl7.fhir.convertors.conv40_50.StructureDefinition40_50.convertStructureDefinition(next)); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -347,7 +367,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null; if (theResult.asConceptDefinition() != null) { try { - conceptDefinition = org.hl7.fhir.convertors.conv40_50.CodeSystem.convertConceptDefinitionComponent(theResult.asConceptDefinition()); + conceptDefinition = org.hl7.fhir.convertors.conv40_50.CodeSystem40_50.convertConceptDefinitionComponent(theResult.asConceptDefinition()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -367,7 +387,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) { ValueSet convertedSource; try { - convertedSource = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(source); + convertedSource = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(source); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -376,7 +396,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV org.hl7.fhir.r5.model.ValueSet convertedResult = null; if (expanded.getValueset() != null) { try { - convertedResult = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(expanded.getValueset()); + convertedResult = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(expanded.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -398,7 +418,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV ValueSet.ConceptSetComponent convertedInc = null; if (inc != null) { try { - convertedInc = org.hl7.fhir.convertors.conv40_50.ValueSet.convertConceptSetComponent(inc); + convertedInc = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertConceptSetComponent(inc); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -408,7 +428,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV org.hl7.fhir.r5.model.ValueSet valueSetExpansion = null; if (expansion != null) { try { - valueSetExpansion = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(expansion.getValueset()); + valueSetExpansion = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(expansion.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -424,7 +444,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV return null; } try { - return org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem(fetched); + return org.hl7.fhir.convertors.conv40_50.CodeSystem40_50.convertCodeSystem(fetched); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -509,6 +529,11 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public org.hl7.fhir.r5.model.StructureDefinition fetchRawProfile(String url) { + return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, url); + } + @Override public List getTypeNames() { return myWrap.getTypeNames(); @@ -605,7 +630,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV try { if (vs != null) { - convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(vs); + convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -620,7 +645,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV ValueSet convertedVs = null; try { if (vs != null) { - convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(vs); + convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -640,7 +665,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV convertedCode = VersionConvertor_40_50.convertCoding(code); } if (vs != null) { - convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(vs); + convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -660,7 +685,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV convertedCode = VersionConvertor_40_50.convertCodeableConcept(code); } if (vs != null) { - convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(vs); + convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r5/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r5/hapi/validation/FhirInstanceValidator.java index 02a3fd839d2..a6ced0b4654 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r5/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r5/hapi/validation/FhirInstanceValidator.java @@ -54,7 +54,9 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV private boolean noTerminologyChecks = false; private volatile WorkerContextWrapper myWrappedWorkerContext; private boolean errorForUnknownProfiles; + private boolean assumeValidRestReferences; private List myExtensionDomains = Collections.emptyList(); + private IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher; /** * Constructor @@ -220,9 +222,27 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV .setErrorForUnknownProfiles(isErrorForUnknownProfiles()) .setExtensionDomains(getExtensionDomains()) .setNoTerminologyChecks(isNoTerminologyChecks()) + .setValidatorResourceFetcher(getValidatorResourceFetcher()) + .setAssumeValidRestReferences(isAssumeValidRestReferences()) .validate(wrappedWorkerContext, theValidationCtx); } + public IResourceValidator.IValidatorResourceFetcher getValidatorResourceFetcher() { + return validatorResourceFetcher; + } + + public void setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.validatorResourceFetcher = validatorResourceFetcher; + } + + public boolean isAssumeValidRestReferences() { + return assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.assumeValidRestReferences = assumeValidRestReferences; + } + private static class WorkerContextWrapper implements IWorkerContext { private final HapiWorkerContext myWrap; @@ -514,6 +534,11 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public StructureDefinition fetchRawProfile(String url) { + return myWrap.fetchRawProfile(url); + } + @Override public List getTypeNames() { return myWrap.getTypeNames(); @@ -706,7 +731,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV } @Override - public Base resolveReference(Object appContext, String url) throws FHIRException { + public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { return null; } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java index bace1c7268e..29fbca55962 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java @@ -24,10 +24,12 @@ import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.dstu3.utils.FHIRPathEngine; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.utils.IResourceValidator; import org.junit.*; import org.junit.rules.TestRule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; +import org.mockito.internal.matchers.Any; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -41,8 +43,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class FhirInstanceValidatorDstu3Test { @@ -975,7 +976,7 @@ public class FhirInstanceValidatorDstu3Test { myInstanceVal.setValidationSupport(myMockSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString(), containsString("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved")); + assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); } @Test @@ -1145,6 +1146,20 @@ public class FhirInstanceValidatorDstu3Test { ourLog.info(output.getMessages().get(0).getMessage()); } + @Test + public void testInvocationOfValidatorFetcher() throws IOException { + String input = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3-rick-test.json"), Charsets.UTF_8); + + IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class); + when(resourceFetcher.validationPolicy(any(),anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); + myInstanceVal.setValidatorResourceFetcher(resourceFetcher); + myVal.validateWithResult(input); + + verify(resourceFetcher, times(3)).resolveURL(any(), anyString(), anyString()); + verify(resourceFetcher, times(4)).validationPolicy(any(), anyString(), anyString()); + verify(resourceFetcher, times(4)).fetch(any(), anyString()); + } + @Test public void testValueWithWhitespace() throws IOException { String input = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3-rick-test.json"), Charsets.UTF_8); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java index d4bbfac740e..7cbd9968f1e 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java @@ -704,7 +704,7 @@ public class QuestionnaireResponseValidatorDstu3Test { coding.setCode("1293"); QuestionnaireResponseItemAnswerComponent answer = qrItem.addAnswer(); answer.setValue(coding); - coding.addExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", new BooleanType(true)); + coding.addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-CO-value", new DecimalType("1.0")); qr.addItem().setLinkId("2B").addAnswer().setValue(new BooleanType(true)); String reference = qr.getQuestionnaire().getReference(); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java index 4a8ececb99a..9e8a25f5a42 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java @@ -20,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; +import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; @@ -71,7 +72,7 @@ public class QuestionnaireValidatorDstu3Test { ValidationResult errors = myVal.validateWithResult(q); ourLog.info(errors.toString()); assertThat(errors.isSuccessful(), Matchers.is(true)); - assertThat(errors.getMessages(), Matchers.empty()); + assertThat(errors.getMessages().stream().filter(t->t.getSeverity().ordinal() > ResultSeverityEnum.INFORMATION.ordinal()).collect(Collectors.toList()), Matchers.empty()); } } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index ce094c1a8a8..fe617e9b955 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -66,9 +66,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; public class FhirInstanceValidatorR4Test extends BaseTest { @@ -397,7 +398,11 @@ public class FhirInstanceValidatorR4Test extends BaseTest { ValidationResult output = myVal.validateWithResult(encoded); List errors = logResultsAndReturnNonInformationalOnes(output); - assertEquals(46, errors.size()); + errors = errors + .stream() + .filter(t->t.getMessage().contains("Bundle entry missing fullUrl")) + .collect(Collectors.toList()); + assertEquals(5, errors.size()); } @Test @@ -633,8 +638,8 @@ public class FhirInstanceValidatorR4Test extends BaseTest { CachingValidationSupport support = new CachingValidationSupport(new ValidationSupportChain(defaultSupport, valSupport)); // Prepopulate SDs - valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/dstu3/myconsent-profile.xml")); - valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/dstu3/myconsent-ext.xml")); + valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/r4/myconsent-profile.xml")); + valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/r4/myconsent-ext.xml")); FhirValidator val = ourCtx.newValidator(); val.registerValidatorModule(new FhirInstanceValidator(support)); @@ -652,6 +657,8 @@ public class FhirInstanceValidatorR4Test extends BaseTest { .setSystem("http://terminology.hl7.org/CodeSystem/consentcategorycodes") .setCode("acd"); + + // Should pass ValidationResult output = val.validateWithResult(input); List all = logResultsAndReturnErrorOnes(output); @@ -1031,7 +1038,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { myInstanceVal.setValidationSupport(myMockSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString(), containsString("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved")); + assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); } @Test @@ -1250,6 +1257,21 @@ public class FhirInstanceValidatorR4Test extends BaseTest { assertEquals(0, all.size()); } + @Test + public void testInvocationOfValidatorFetcher() throws IOException { + + String encoded = loadResource("/r4/r4-caredove-bundle.json"); + + IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class); + when(resourceFetcher.validationPolicy(any(),anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); + myInstanceVal.setValidatorResourceFetcher(resourceFetcher); + myVal.validateWithResult(encoded); + + verify(resourceFetcher, times(14)).resolveURL(any(), anyString(), anyString()); + verify(resourceFetcher, times(12)).validationPolicy(any(), anyString(), anyString()); + verify(resourceFetcher, times(12)).fetch(any(), anyString()); + } + @Test @Ignore public void testValidateStructureDefinition() throws IOException { diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireValidatorR4Test.java index b6b88d44c15..ff872af31f4 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireValidatorR4Test.java @@ -23,6 +23,8 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.stream.Collectors; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; @@ -72,7 +74,7 @@ public class QuestionnaireValidatorR4Test { ValidationResult errors = myVal.validateWithResult(q); ourLog.info(errors.toString()); assertThat(errors.isSuccessful(), Matchers.is(true)); - assertThat(errors.getMessages(), Matchers.empty()); + assertThat(errors.getMessages().stream().filter(t->t.getSeverity().ordinal() > ResultSeverityEnum.INFORMATION.ordinal()).collect(Collectors.toList()), Matchers.empty()); } } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java index 3a4380a3a20..05a8e54e3ff 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java @@ -53,9 +53,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; public class FhirInstanceValidatorR5Test { @@ -415,6 +416,23 @@ public class FhirInstanceValidatorR5Test { assertThat(nonInfo, empty()); } + + + @Test + public void testInvocationOfValidatorFetcher() throws IOException { + + String input = IOUtils.toString(FhirInstanceValidator.class.getResourceAsStream("/vitals.json"), Charsets.UTF_8); + + IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class); + when(resourceFetcher.validationPolicy(any(),anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); + myInstanceVal.setValidatorResourceFetcher(resourceFetcher); + myVal.validateWithResult(input); + + verify(resourceFetcher, times(12)).resolveURL(any(), anyString(), anyString()); + verify(resourceFetcher, times(3)).validationPolicy(any(), anyString(), anyString()); + verify(resourceFetcher, times(3)).fetch(any(), anyString()); + } + @Test public void testIsNoTerminologyChecks() { assertFalse(myInstanceVal.isNoTerminologyChecks()); @@ -774,7 +792,7 @@ public class FhirInstanceValidatorR5Test { myInstanceVal.setValidationSupport(myMockSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString(), containsString("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved")); + assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); } @Test diff --git a/hapi-fhir-validation/src/test/resources/r4/myconsent-ext.xml b/hapi-fhir-validation/src/test/resources/r4/myconsent-ext.xml new file mode 100644 index 00000000000..2049e440b22 --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/r4/myconsent-ext.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hapi-fhir-validation/src/test/resources/r4/myconsent-profile.xml b/hapi-fhir-validation/src/test/resources/r4/myconsent-profile.xml new file mode 100644 index 00000000000..0396797fe88 --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/r4/myconsent-profile.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hapi-fhir-validation/src/test/resources/vitals.json b/hapi-fhir-validation/src/test/resources/vitals.json new file mode 100644 index 00000000000..78b3e733c6b --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/vitals.json @@ -0,0 +1,97 @@ +{ + "resourceType": "Observation", + "id": "satO2", + "meta": { + "profile": [ + "http://hl7.org/fhir/StructureDefinition/vitalsigns" + ] + }, + "text": { + "status": "generated", + "div": "

Generated Narrative with Details

id: satO2

meta:

identifier: o1223435-10

partOf: Procedure/ob

status: final

category: Vital Signs (Details : {http://terminology.hl7.org/CodeSystem/observation-category code 'vital-signs' = 'Vital Signs', given as 'Vital Signs'})

code: Oxygen saturation in Arterial blood (Details : {LOINC code '2708-6' = 'Oxygen saturation in Arterial blood', given as 'Oxygen saturation in Arterial blood'}; {LOINC code '59408-5' = 'Oxygen saturation in Arterial blood by Pulse oximetry', given as 'Oxygen saturation in Arterial blood by Pulse oximetry'}; {urn:iso:std:iso:11073:10101 code '150456' = '150456', given as 'MDC_PULS_OXIM_SAT_O2'})

subject: Patient/example

effective: Dec 5, 2014 9:30:10 AM

value: 95 % (Details: UCUM code % = '%')

interpretation: Normal (applies to non-numeric results) (Details : {http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation code 'N' = 'Normal', given as 'Normal'})

device: DeviceMetric/example

ReferenceRanges

-LowHigh
*90 % (Details: UCUM code % = '%')99 % (Details: UCUM code % = '%')
" + }, + "identifier": [ + { + "system": "http://goodcare.org/observation/id", + "value": "o1223435-10" + } + ], + "partOf": [ + { + "reference": "Procedure/ob" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "vital-signs", + "display": "Vital Signs" + } + ], + "text": "Vital Signs" + } + ], + "code": { + "coding": [ + { + "system": "http://loinc.org", + "code": "2708-6", + "display": "Oxygen saturation in Arterial blood" + }, + { + "system": "http://loinc.org", + "code": "59408-5", + "display": "Oxygen saturation in Arterial blood by Pulse oximetry" + }, + { + "system": "urn:iso:std:iso:11073:10101", + "code": "150456", + "display": "MDC_PULS_OXIM_SAT_O2" + } + ] + }, + "subject": { + "reference": "Patient/example" + }, + "effectiveDateTime": "2014-12-05T09:30:10+01:00", + "valueQuantity": { + "value": 95, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "Normal" + } + ], + "text": "Normal (applies to non-numeric results)" + } + ], + "device": { + "reference": "DeviceMetric/example" + }, + "referenceRange": [ + { + "low": { + "value": 90, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "high": { + "value": 99, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + ] +} diff --git a/pom.xml b/pom.xml index 495bc8afe38..ef8d1092440 100644 --- a/pom.xml +++ b/pom.xml @@ -606,7 +606,7 @@ - 4.2.0 + 4.2.1-SNAPSHOT 1.0.2 -Dfile.encoding=UTF-8 -Xmx2048m From 96d00f633cdba901031f6aa5a8edf84978bd10ab Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 18 Feb 2020 14:09:17 -0500 Subject: [PATCH 08/30] Move new migration task to version 4.3.0. --- .../migrate/tasks/HapiFhirJpaMigrationTasks.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 7638ed1b601..3cea17c82b0 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -56,10 +56,20 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { init360(); // 20180918 - 20181112 init400(); // 20190401 - 20190814 init410(); // 20190815 - 20191014 - init420(); // 20191015 - present + init420(); // 20191015 - 20200217 + init430(); // 20200218 - present } - protected void init420() { // 20191015 - present + protected void init430() { // 20200218 - present + Builder version = forVersion(VersionEnum.V4_3_0); + + // Eliminate circular dependency. + version.onTable("HFJ_RESOURCE").dropColumn("20200218.1", "FORCED_ID_PID"); + version.onTable("HFJ_RES_VER").dropColumn("20200218.2", "FORCED_ID_PID"); + version.onTable("HFJ_RES_VER").addForeignKey("20200218.3", "FK_RESOURCE_HISTORY_RESOURCE").toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID"); + } + + protected void init420() { // 20191015 - 20200217 Builder version = forVersion(VersionEnum.V4_2_0); // TermValueSetConceptDesignation From 2f4f62b32eb18c9cb629b3cacb19b0426eaa9720 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 18 Feb 2020 14:09:59 -0500 Subject: [PATCH 09/30] Add change log for Resource and Resource History table changes. --- .../changelog/4_3_0/1693-forcedid-circular-dependency.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1693-forcedid-circular-dependency.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1693-forcedid-circular-dependency.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1693-forcedid-circular-dependency.yaml new file mode 100644 index 00000000000..97cd25103b9 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1693-forcedid-circular-dependency.yaml @@ -0,0 +1,5 @@ +--- +type: change +issue: 1693 +title: Adjusted schema definitions for Resource and Resource History tables to eliminate circular + dependencies with Forced ID table and to improve performance when expunging large numbers of resources. From 64f07e4dc074b11a585f78a2aaa1b36bcaa1961e Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 18 Feb 2020 14:11:48 -0500 Subject: [PATCH 10/30] Includes performance enhancement (#1702) * Includes performance enhancement * Add changelog * Test fix * Fix typo * A few coverage cleanups * Test fix * Fix changelog --- .../4_3_0/1702-includes-perf-fix.yaml | 4 + .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 89 ++++++++++++------- .../uhn/fhir/jpa/dao/SearchBuilderTest.java | 5 +- .../ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java | 3 + .../r4/FhirResourceDaoR4SearchNoFtTest.java | 39 ++++++++ .../fhir/jpa/model/entity/ResourceLink.java | 8 -- .../rest/server/method/ReadMethodBinding.java | 2 +- .../server/method/ReadMethodBindingTest.java | 5 ++ 8 files changed, 109 insertions(+), 46 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1702-includes-perf-fix.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1702-includes-perf-fix.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1702-includes-perf-fix.yaml new file mode 100644 index 00000000000..bc53ca0a098 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1702-includes-perf-fix.yaml @@ -0,0 +1,4 @@ +--- +type: perf +issue: 1702 +title: "Loading of _include and _revinclude values has been optimized to be slightly faster" diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 99b349c698d..5a21d9a7933 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -29,17 +29,29 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao; import ca.uhn.fhir.jpa.dao.data.IResourceTagDao; import ca.uhn.fhir.jpa.dao.index.IdHelperService; -import ca.uhn.fhir.jpa.dao.predicate.*; +import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilder; +import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderFactory; +import ca.uhn.fhir.jpa.dao.predicate.QueryRoot; +import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum; +import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey; import ca.uhn.fhir.jpa.entity.ResourceSearchView; import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails; import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; -import ca.uhn.fhir.jpa.model.entity.*; +import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique; +import ca.uhn.fhir.jpa.model.entity.ResourceLink; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.model.entity.ResourceTag; import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails; import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; -import ca.uhn.fhir.jpa.util.*; +import ca.uhn.fhir.jpa.util.BaseIterator; +import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener; +import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster; +import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; +import ca.uhn.fhir.jpa.util.SqlQueryList; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; @@ -80,10 +92,27 @@ import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.persistence.TypedQuery; -import javax.persistence.criteria.*; -import java.util.*; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.From; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * The SearchBuilder is responsible for actually forming the SQL query that handles @@ -103,8 +132,9 @@ public class SearchBuilder implements ISearchBuilder { private static final List EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<>()); private static final Logger ourLog = LoggerFactory.getLogger(SearchBuilder.class); private static ResourcePersistentId NO_MORE = new ResourcePersistentId(-1L); - @Autowired - private DaoConfig myDaoConfig; + private final QueryRoot myQueryRoot = new QueryRoot(); + private final String myResourceName; + private final Class myResourceType; @Autowired protected IInterceptorBroadcaster myInterceptorBroadcaster; @Autowired @@ -112,6 +142,8 @@ public class SearchBuilder implements ISearchBuilder { @PersistenceContext(type = PersistenceContextType.TRANSACTION) protected EntityManager myEntityManager; @Autowired + private DaoConfig myDaoConfig; + @Autowired private IResourceSearchViewDao myResourceSearchViewDao; @Autowired private FhirContext myContext; @@ -123,7 +155,6 @@ public class SearchBuilder implements ISearchBuilder { private ISearchParamRegistry mySearchParamRegistry; @Autowired private PredicateBuilderFactory myPredicateBuilderFactory; - private List myAlsoIncludePids; private CriteriaBuilder myBuilder; private IDao myCallingDao; @@ -133,9 +164,6 @@ public class SearchBuilder implements ISearchBuilder { private Integer myMaxResultsToFetch; private Set myPidSet; private PredicateBuilder myPredicateBuilder; - private final QueryRoot myQueryRoot = new QueryRoot(); - private final String myResourceName; - private final Class myResourceType; /** * Constructor @@ -595,6 +623,7 @@ public class SearchBuilder implements ISearchBuilder { return new HashSet<>(); } String searchFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid"; + String findFieldName = theReverseMode ? "mySourceResourcePid" : "myTargetResourcePid"; Collection nextRoundMatches = theMatches; HashSet allAdded = new HashSet<>(); @@ -619,17 +648,17 @@ public class SearchBuilder implements ISearchBuilder { boolean matchAll = "*".equals(nextInclude.getValue()); if (matchAll) { String sql; - sql = "SELECT r FROM ResourceLink r WHERE r." + searchFieldName + " IN (:target_pids) "; + sql = "SELECT r." + findFieldName + " FROM ResourceLink r WHERE r." + searchFieldName + " IN (:target_pids) "; List> partitions = partition(nextRoundMatches, MAXIMUM_PAGE_SIZE); for (Collection nextPartition : partitions) { - TypedQuery q = theEntityManager.createQuery(sql, ResourceLink.class); + TypedQuery q = theEntityManager.createQuery(sql, Long.class); q.setParameter("target_pids", ResourcePersistentId.toLongList(nextPartition)); - List results = q.getResultList(); - for (ResourceLink resourceLink : results) { + List results = q.getResultList(); + for (Long resourceLink : results) { if (theReverseMode) { - pidsToInclude.add(new ResourcePersistentId(resourceLink.getSourceResourcePid())); + pidsToInclude.add(new ResourcePersistentId(resourceLink)); } else { - pidsToInclude.add(new ResourcePersistentId(resourceLink.getTargetResourcePid())); + pidsToInclude.add(new ResourcePersistentId(resourceLink)); } } } @@ -666,16 +695,16 @@ public class SearchBuilder implements ISearchBuilder { boolean haveTargetTypesDefinedByParam = param.hasTargets(); if (targetResourceType != null) { - sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids) AND r.myTargetResourceType = :target_resource_type"; + sql = "SELECT r." + findFieldName + " FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids) AND r.myTargetResourceType = :target_resource_type"; } else if (haveTargetTypesDefinedByParam) { - sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids) AND r.myTargetResourceType in (:target_resource_types)"; + sql = "SELECT r." + findFieldName + " FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids) AND r.myTargetResourceType in (:target_resource_types)"; } else { - sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids)"; + sql = "SELECT r." + findFieldName + " FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids)"; } List> partitions = partition(nextRoundMatches, MAXIMUM_PAGE_SIZE); for (Collection nextPartition : partitions) { - TypedQuery q = theEntityManager.createQuery(sql, ResourceLink.class); + TypedQuery q = theEntityManager.createQuery(sql, Long.class); q.setParameter("src_path", nextPath); q.setParameter("target_pids", ResourcePersistentId.toLongList(nextPartition)); if (targetResourceType != null) { @@ -683,18 +712,10 @@ public class SearchBuilder implements ISearchBuilder { } else if (haveTargetTypesDefinedByParam) { q.setParameter("target_resource_types", param.getTargets()); } - List results = q.getResultList(); - for (ResourceLink resourceLink : results) { - if (theReverseMode) { - Long pid = resourceLink.getSourceResourcePid(); - if (pid != null) { - pidsToInclude.add(new ResourcePersistentId(pid)); - } - } else { - Long pid = resourceLink.getTargetResourcePid(); - if (pid != null) { - pidsToInclude.add(new ResourcePersistentId(pid)); - } + List results = q.getResultList(); + for (Long resourceLink : results) { + if (resourceLink != null) { + pidsToInclude.add(new ResourcePersistentId(resourceLink)); } } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/SearchBuilderTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/SearchBuilderTest.java index fcefe28b6cf..9915a2cddb0 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/SearchBuilderTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/SearchBuilderTest.java @@ -39,11 +39,10 @@ public class SearchBuilderTest { TypedQuery mockQuery = mock(TypedQuery.class); when(mockEntityManager.createQuery(any(), any())).thenReturn(mockQuery); - List resultList = new ArrayList<>(); - ResourceLink link = new ResourceLink(); + List resultList = new ArrayList<>(); + Long link = 1L; ResourceTable target = new ResourceTable(); target.setId(1L); - link.setTargetResource(target); resultList.add(link); when(mockQuery.getResultList()).thenReturn(resultList); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index a3f8a136228..91a5702e629 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -162,6 +162,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { @Qualifier("myConditionDaoR4") protected IFhirResourceDao myConditionDao; @Autowired + @Qualifier("myEpisodeOfCareDaoR4") + protected IFhirResourceDao myEpisodeOfCareDao; + @Autowired protected DaoConfig myDaoConfig; @Autowired protected ModelConfig myModelConfig; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index c5d952df13c..3ac22eca837 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -4228,6 +4228,45 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { } } + @Test + public void testCircularReferencesDontBreakRevIncludes() { + + Patient p = new Patient(); + p.setActive(true); + IIdType patientId = myPatientDao.create(p).getId().toUnqualifiedVersionless(); + + Encounter enc = new Encounter(); + enc.setStatus(Encounter.EncounterStatus.ARRIVED); + enc.getSubject().setReference(patientId.getValue()); + IIdType encId = myEncounterDao.create(enc).getId().toUnqualifiedVersionless(); + + Condition cond = new Condition(); + cond.addIdentifier().setSystem("http://foo").setValue("123"); + IIdType conditionId = myConditionDao.create(cond).getId().toUnqualifiedVersionless(); + + EpisodeOfCare ep = new EpisodeOfCare(); + ep.setStatus(EpisodeOfCare.EpisodeOfCareStatus.ACTIVE); + IIdType epId = myEpisodeOfCareDao.create(ep).getId().toUnqualifiedVersionless(); + + enc.getEpisodeOfCareFirstRep().setReference(ep.getId()); + myEncounterDao.update(enc); + cond.getEncounter().setReference(enc.getId()); + myConditionDao.update(cond); + ep.getDiagnosisFirstRep().getCondition().setReference(cond.getId()); + myEpisodeOfCareDao.update(ep); + + // Search time + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.addRevInclude(new Include("*").setRecurse(true)); + IBundleProvider results = myPatientDao.search(params); + List values = toUnqualifiedVersionlessIdValues(results); + assertThat(values.toString(), values, containsInAnyOrder(patientId.getValue(), encId.getValue(), conditionId.getValue(), epId.getValue())); + + } + + + private String toStringMultiline(List theResults) { StringBuilder b = new StringBuilder(); for (Object next : theResults) { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java index 24b9e690b6e..e7e627c035c 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java @@ -138,10 +138,6 @@ public class ResourceLink extends BaseResourceIndex { mySourceResourceType = theSourceResource.getResourceType(); } - public Long getSourceResourcePid() { - return mySourceResourcePid; - } - public ResourceTable getTargetResource() { return myTargetResource; } @@ -157,10 +153,6 @@ public class ResourceLink extends BaseResourceIndex { return myTargetResourcePid; } - public String getTargetResourceUrl() { - return myTargetResourceUrl; - } - public void setTargetResourceUrl(IIdType theTargetResourceUrl) { Validate.isTrue(theTargetResourceUrl.hasBaseUrl()); Validate.isTrue(theTargetResourceUrl.hasResourceType()); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ReadMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ReadMethodBinding.java index 935b0c62703..facae61707d 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ReadMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ReadMethodBinding.java @@ -160,7 +160,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding { IBundleProvider retVal = toResourceList(response); - if (retVal.size() == 1) { + if (Integer.valueOf(1).equals(retVal.size())) { List responseResources = retVal.getResources(0, 1); IBaseResource responseResource = responseResources.get(0); diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ReadMethodBindingTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ReadMethodBindingTest.java index 598dddf84aa..ba4ee6476ce 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ReadMethodBindingTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ReadMethodBindingTest.java @@ -59,6 +59,11 @@ public class ReadMethodBindingTest { assertTrue(binding.incomingServerRequestMatchesMethod(myRequestDetails)); // VRead + when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123/_history/123")); + assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails)); + + // Type history + when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123")); when(myRequestDetails.getResourceName()).thenReturn("Patient"); when(myRequestDetails.getOperation()).thenReturn("_history"); assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails)); From ab8dfa5a03fabaa2c92d289edc99c6fedc55af41 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 18 Feb 2020 16:40:44 -0500 Subject: [PATCH 11/30] Avoid multi-term for ElasticSearch (#1717) * Avoid multi-term for ElasticSearch * Add changelog * Remove redundant code --- .../4_3_0/1717-avoid-multiterm-in-es.yaml | 6 ++ .../fhir/jpa/term/BaseTermReadSvcImpl.java | 12 ++- ...ourceDaoR4SearchWithElasticSearchTest.java | 50 ++++++++++ .../test/resources/r4/expand-multi-cs.json | 86 +++++++++++++++++ .../resources/r4/expand-multi-vs-all.json | 92 +++++++++++++++++++ 5 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1717-avoid-multiterm-in-es.yaml create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/r4/expand-multi-cs.json create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/r4/expand-multi-vs-all.json diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1717-avoid-multiterm-in-es.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1717-avoid-multiterm-in-es.yaml new file mode 100644 index 00000000000..d9c363e48ee --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1717-avoid-multiterm-in-es.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 1717 +title: ValueSet expansions containing lists of terms did not correctly expand when backed by + ElasticSearch due to the use of a feature not supported in ES. Thanks to Jens Villadsen for + reporting! diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 02891e7c508..5a1ed416a51 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -66,6 +66,7 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.MultiPhraseQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.RegexpQuery; +import org.apache.lucene.search.TermQuery; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.search.jpa.FullTextEntityManager; @@ -600,11 +601,16 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo .map(t -> new Term("myCode", t)) .collect(Collectors.toList()); if (codes.size() > 0) { - MultiPhraseQuery query = new MultiPhraseQuery(); - query.add(codes.toArray(new Term[0])); + + BooleanQuery.Builder builder = new BooleanQuery.Builder(); + builder.setMinimumNumberShouldMatch(1); + for (Term nextCode : codes) { + builder.add(new TermQuery(nextCode), BooleanClause.Occur.SHOULD); + } + luceneQuery = new BooleanQuery.Builder() .add(luceneQuery, BooleanClause.Occur.MUST) - .add(query, BooleanClause.Occur.MUST) + .add(builder.build(), BooleanClause.Occur.MUST) .build(); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java index 23ee3628fc5..26fbb97b9ab 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ValidationResult; +import org.hamcrest.Matchers; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.*; import org.junit.AfterClass; @@ -37,8 +38,10 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.PlatformTransactionManager; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertEquals; @@ -146,6 +149,53 @@ public class FhirResourceDaoR4SearchWithElasticSearchTest extends BaseJpaTest { } + + @Test + public void testExpandVsWithMultiInclude_All() throws IOException { + CodeSystem cs = loadResource(myFhirCtx, CodeSystem.class, "/r4/expand-multi-cs.json"); + myCodeSystemDao.update(cs); + + ValueSet vs = loadResource(myFhirCtx, ValueSet.class, "/r4/expand-multi-vs-all.json"); + ValueSet expanded = myValueSetDao.expand(vs, null); + + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + + // All codes + List codes = expanded + .getExpansion() + .getContains() + .stream() + .map(t -> t.getCode()) + .sorted() + .collect(Collectors.toList()); + assertThat(codes.toString(), codes, Matchers.contains("advice", "message", "note", "notification")); + } + + + @Test + public void testExpandVsWithMultiInclude_Some() throws IOException { + CodeSystem cs = loadResource(myFhirCtx, CodeSystem.class, "/r4/expand-multi-cs.json"); + myCodeSystemDao.update(cs); + + ValueSet vs = loadResource(myFhirCtx, ValueSet.class, "/r4/expand-multi-vs-all.json"); + vs.getCompose().getInclude().get(0).getConcept().remove(0); + vs.getCompose().getInclude().get(0).getConcept().remove(0); + + ValueSet expanded = myValueSetDao.expand(vs, null); + + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + + // All codes + List codes = expanded + .getExpansion() + .getContains() + .stream() + .map(t -> t.getCode()) + .sorted() + .collect(Collectors.toList()); + assertThat(codes.toString(), codes, Matchers.contains("advice", "note")); + } + private CodeSystem createExternalCs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/r4/expand-multi-cs.json b/hapi-fhir-jpaserver-base/src/test/resources/r4/expand-multi-cs.json new file mode 100644 index 00000000000..ea72cd32b04 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/r4/expand-multi-cs.json @@ -0,0 +1,86 @@ +{ + "resourceType" : "CodeSystem", + "id" : "ehealth-message-category", + "text" : { + "status" : "generated", + "div" : "

MessageCategory

Message category types

\n

This code system http://ehealth.sundhed.dk/cs/message-category defines the following codes:

CodeDisplayDefinition
message Message
notification Notification
advice Advice
note Note
" + }, + "url" : "http://ehealth.sundhed.dk/cs/message-category", + "version" : "0.6.0", + "name" : "MessageCategory", + "status" : "active", + "experimental" : false, + "date" : "2019-01-29T00:00:00+00:00", + "publisher" : "ehealth.sundhed.dk", + "contact" : [ + { + "telecom" : [ + { + "system" : "url", + "value" : "http://ehealth.sundhed.dk/terminology" + } + ] + } + ], + "description" : "Message category types", + "caseSensitive" : true, + "content" : "complete", + "concept" : [ + { + "code" : "message", + "display" : "Message", + "designation" : [ + { + "language" : "en-US", + "value" : "Message" + }, + { + "language" : "da", + "value" : "Besked" + } + ] + }, + { + "code" : "notification", + "display" : "Notification", + "designation" : [ + { + "language" : "en-US", + "value" : "Notification" + }, + { + "language" : "da", + "value" : "Notifikation" + } + ] + }, + { + "code" : "advice", + "display" : "Advice", + "designation" : [ + { + "language" : "en-US", + "value" : "Advice" + }, + { + "language" : "da", + "value" : "Advisering" + } + ] + }, + { + "code" : "note", + "display" : "Note", + "designation" : [ + { + "language" : "en-US", + "value" : "Note" + }, + { + "language" : "da", + "value" : "Note" + } + ] + } + ] +} diff --git a/hapi-fhir-jpaserver-base/src/test/resources/r4/expand-multi-vs-all.json b/hapi-fhir-jpaserver-base/src/test/resources/r4/expand-multi-vs-all.json new file mode 100644 index 00000000000..817b38cbe78 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/r4/expand-multi-vs-all.json @@ -0,0 +1,92 @@ +{ + "resourceType" : "ValueSet", + "id" : "message-category", + "text" : { + "status" : "generated", + "div" : "

MessageCategory

The set of possible message types

\n

This value set includes codes from the following code systems:

Additional Language Displays

CodeDansk (Danish, da)English (United States) (English (United States), en)
messageBeskedMessage
notificationNotifikationNotification
adviceAdviseringAdvice
noteNoteNote
" + }, + "url" : "http://ehealth.sundhed.dk/vs/message-category", + "version" : "0.0.1", + "name" : "MessageCategory", + "status" : "active", + "experimental" : true, + "date" : "2019-02-08T00:00:00+00:00", + "publisher" : "ehealth.sundhed.dk", + "contact" : [ + { + "name" : "FUT", + "telecom" : [ + { + "system" : "url", + "value" : "https://digst.dk/digital-service/digital-velfaerd/telemedicin-kol/faelles-udbud-af-telemedicin-fut/" + } + ] + } + ], + "description" : "The set of possible message types", + "compose" : { + "include" : [ + { + "system" : "http://ehealth.sundhed.dk/cs/message-category", + "concept" : [ + { + "code" : "message", + "display" : "Message", + "designation" : [ + { + "language" : "en-US", + "value" : "Message" + }, + { + "language" : "da", + "value" : "Besked" + } + ] + }, + { + "code" : "notification", + "display" : "Notification", + "designation" : [ + { + "language" : "en-US", + "value" : "Notification" + }, + { + "language" : "da", + "value" : "Notifikation" + } + ] + }, + { + "code" : "advice", + "display" : "Advice", + "designation" : [ + { + "language" : "en-US", + "value" : "Advice" + }, + { + "language" : "da", + "value" : "Advisering" + } + ] + }, + { + "code" : "note", + "display" : "Note", + "designation" : [ + { + "language" : "en-US", + "value" : "Note" + }, + { + "language" : "da", + "value" : "Note" + } + ] + } + ] + } + ] + } +} From a05c4ce9ebd86828cbd4c4eafe78da8272d777c0 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 19 Feb 2020 17:46:54 -0500 Subject: [PATCH 12/30] Don't thrash delta links (#1721) * Don't thrash delta links * Add changelog * Remove commented out code --- .../4_3_0/1721-dont-thrash-delta-links.yaml | 6 + .../term/TermCodeSystemStorageSvcImpl.java | 48 +++----- .../jpa/term/TerminologySvcDeltaR4Test.java | 116 ++++++++++-------- 3 files changed, 94 insertions(+), 76 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1721-dont-thrash-delta-links.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1721-dont-thrash-delta-links.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1721-dont-thrash-delta-links.yaml new file mode 100644 index 00000000000..7099817d526 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1721-dont-thrash-delta-links.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 1721 +title: When performing a terminology delta ADD operation, existing parent-child links were often + deleted and recrreated needlessly during operations, which could result in a deadlock. This has + been resolved. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 43648e3e99c..3b36c1a97ca 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -451,48 +451,40 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { String nextCodeToAdd = conceptToAdd.getCode(); String parentDescription = "(root concept)"; - Set parentConcepts = new HashSet<>(); - - if (!theParentCodes.isEmpty()) { - parentDescription = "[" + String.join(", ", theParentCodes) + "]"; - for (String nextParentCode : theParentCodes) { - Optional nextParentOpt = myConceptDao.findByCodeSystemAndCode(theCsv, nextParentCode); - if (nextParentOpt.isPresent() == false) { - throw new InvalidRequestException("Unable to add code \"" + nextCodeToAdd + "\" to unknown parent: " + nextParentCode); - } - parentConcepts.add(nextParentOpt.get()); - } - } ourLog.info("Saving concept {} with parent {}", theStatisticsTracker.getUpdatedConceptCount(), parentDescription); Optional existingCodeOpt = myConceptDao.findByCodeSystemAndCode(theCsv, nextCodeToAdd); + List existingParentLinks; if (existingCodeOpt.isPresent()) { TermConcept existingCode = existingCodeOpt.get(); existingCode.setIndexStatus(null); existingCode.setDisplay(conceptToAdd.getDisplay()); conceptToAdd = existingCode; + existingParentLinks = conceptToAdd.getParents(); + } else { + existingParentLinks = Collections.emptyList(); + } + + Set parentConceptsWeShouldLinkTo = new HashSet<>(); + for (String nextParentCode : theParentCodes) { + + // Don't add parent links that already exist for the code + if (existingParentLinks.stream().anyMatch(t->t.getParent().getCode().equals(nextParentCode))) { + continue; + } + + Optional nextParentOpt = myConceptDao.findByCodeSystemAndCode(theCsv, nextParentCode); + if (nextParentOpt.isPresent() == false) { + throw new InvalidRequestException("Unable to add code \"" + nextCodeToAdd + "\" to unknown parent: " + nextParentCode); + } + parentConceptsWeShouldLinkTo.add(nextParentOpt.get()); } if (conceptToAdd.getSequence() == null) { conceptToAdd.setSequence(theSequence); } - // Drop any old parent-child links if they aren't explicitly specified in the - // hierarchy being added - if (!theRootConcept) { - for (Iterator iter = conceptToAdd.getParents().iterator(); iter.hasNext(); ) { - TermConceptParentChildLink nextLink = iter.next(); - String parentCode = nextLink.getParent().getCode(); - ourLog.info("Dropping existing parent/child link from {} -> {}", parentCode, nextCodeToAdd); - myConceptParentChildLinkDao.delete(nextLink); - iter.remove(); - - List parentChildrenList = nextLink.getParent().getChildren(); - parentChildrenList.remove(nextLink); - } - } - // Null out the hierarchy PIDs for this concept always. We do this because we're going to // force a reindex, and it'll be regenerated then conceptToAdd.setParentPids(null); @@ -504,7 +496,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { theStatisticsTracker.incrementUpdatedConceptCount(); // Add link to new child to the parent - for (TermConcept nextParentConcept : parentConcepts) { + for (TermConcept nextParentConcept : parentConceptsWeShouldLinkTo) { TermConceptParentChildLink parentLink = new TermConceptParentChildLink(); parentLink.setParent(nextParentConcept); parentLink.setChild(conceptToAdd); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java index 496296a12ab..0d0dd62fe54 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java @@ -156,6 +156,8 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta); assertHierarchyContains( "RootA seq=0", + " ChildAA seq=0", + " ChildAAA seq=0", "RootB seq=0", " ChildAA seq=0", " ChildAAA seq=0" @@ -164,6 +166,72 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { } + @Test + public void testReAddingConceptsDoesntRecreateExistingLinks() { + createNotPresentCodeSystem(); + assertHierarchyContains(); + + UploadStatistics outcome; + CustomTerminologySet delta; + + myCaptureQueriesListener.clear(); + + delta = new CustomTerminologySet(); + delta.addRootConcept("RootA", "Root A") + .addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAA").setDisplay("Child AA"); + myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta); + assertHierarchyContains( + "RootA seq=0", + " ChildAA seq=0" + ); + + myCaptureQueriesListener.logDeleteQueries(); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + myCaptureQueriesListener.logInsertQueries(); + // 2 concepts, 1 link + assertEquals(3, myCaptureQueriesListener.countInsertQueries()); + myCaptureQueriesListener.clear(); + + delta = new CustomTerminologySet(); + delta.addRootConcept("RootA", "Root A") + .addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAA").setDisplay("Child AA") + .addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAAA").setDisplay("Child AAA"); + myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta); + assertHierarchyContains( + "RootA seq=0", + " ChildAA seq=0", + " ChildAAA seq=0" + ); + + myCaptureQueriesListener.logDeleteQueries(); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + myCaptureQueriesListener.logInsertQueries(); + // 1 concept, 1 link + assertEquals(2, myCaptureQueriesListener.countInsertQueries()); + myCaptureQueriesListener.clear(); + + delta = new CustomTerminologySet(); + delta.addRootConcept("RootA", "Root A") + .addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAA").setDisplay("Child AA") + .addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAAA").setDisplay("Child AAA") + .addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAAAA").setDisplay("Child AAAA"); + myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta); + assertHierarchyContains( + "RootA seq=0", + " ChildAA seq=0", + " ChildAAA seq=0", + " ChildAAAA seq=0" + ); + + myCaptureQueriesListener.logDeleteQueries(); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + myCaptureQueriesListener.logInsertQueries(); + // 1 concept, 1 link + assertEquals(2, myCaptureQueriesListener.countInsertQueries()); + myCaptureQueriesListener.clear(); + + } + @Test public void testAddNotPermittedForNonExternalCodeSystem() { @@ -307,54 +375,6 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { assertEquals("CODEA1", myTermSvc.lookupCode(myFhirCtx, "http://foo", "codea").getCodeDisplay()); } - @Test - public void testAddRelocateHierarchy() { - createNotPresentCodeSystem(); - - // Add code hierarchy - CustomTerminologySet delta = new CustomTerminologySet(); - TermConcept codeA = delta.addRootConcept("CodeA", "Code A"); - TermConcept codeAA = codeA.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("CodeAA").setDisplay("Code AA"); - codeAA.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("CodeAAA").setDisplay("Code AAA"); - codeAA.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("CodeAAB").setDisplay("Code AAB"); - TermConcept codeB = delta.addRootConcept("CodeB", "Code B"); - TermConcept codeBA = codeB.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("CodeBA").setDisplay("Code BA"); - codeBA.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("CodeBAA").setDisplay("Code BAA"); - codeBA.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("CodeBAB").setDisplay("Code BAB"); - UploadStatistics outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta); - assertEquals(8, outcome.getUpdatedConceptCount()); - assertHierarchyContains( - "CodeA seq=0", - " CodeAA seq=0", - " CodeAAA seq=0", - " CodeAAB seq=1", - "CodeB seq=0", - " CodeBA seq=0", - " CodeBAA seq=0", - " CodeBAB seq=1" - ); - - // Move a single child code to a new spot and make sure the hierarchy comes along - // for the ride.. - delta = new CustomTerminologySet(); - delta - .addRootConcept("CodeB", "Code B") - .addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("CodeAA").setDisplay("Code AA"); - outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta); - assertEquals(2, outcome.getUpdatedConceptCount()); - assertHierarchyContains( - "CodeA seq=0", - "CodeB seq=0", - " CodeBA seq=0", - " CodeBAA seq=0", - " CodeBAB seq=1", - " CodeAA seq=0", // <-- CodeAA got added here so it comes second - " CodeAAA seq=0", - " CodeAAB seq=1" - ); - - } - @Test @Ignore public void testAddWithPropertiesAndDesignations() { From 79ad0aebb505fb9b25d34d04fd043808e20cab85 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 19 Feb 2020 18:01:18 -0500 Subject: [PATCH 13/30] Add a test --- .../r4/FhirResourceDaoR4QueryCountTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java index ee059080b41..2b5380886de 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java @@ -133,6 +133,38 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test { assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); } + + @Test + public void testCreateWithClientAssignedId() { + myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED); + + runInTransaction(() -> { + Patient p = new Patient(); + p.getMaritalStatus().setText("123"); + return myPatientDao.create(p).getId().toUnqualified(); + }); + + myCaptureQueriesListener.clear(); + + runInTransaction(() -> { + Patient p = new Patient(); + p.setId("AAA"); + p.getMaritalStatus().setText("123"); + return myPatientDao.update(p).getId().toUnqualified(); + }); + + myCaptureQueriesListener.logSelectQueriesForCurrentThread(); + assertEquals(1, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); + myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); + assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); + myCaptureQueriesListener.logInsertQueriesForCurrentThread(); + assertEquals(4, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); + myCaptureQueriesListener.logDeleteQueriesForCurrentThread(); + assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); + } + + + @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); From 6200cd2ddcefd3aaaa54a149534bb6cd04ead4f6 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 19 Feb 2020 21:21:22 -0500 Subject: [PATCH 14/30] Add test asserts --- .../ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java index b982fb093a8..b2999af3bde 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java @@ -227,10 +227,14 @@ public class PersistedJpaBundleProvider implements IBundleProvider { template.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus theStatus) { - ensureSearchEntityLoaded(); + boolean entityLoaded = ensureSearchEntityLoaded(); + assert entityLoaded; } }); + assert mySearchEntity != null; + assert mySearchEntity.getSearchType() != null; + switch (mySearchEntity.getSearchType()) { case HISTORY: return template.execute(theStatus -> doHistoryInTransaction(theFromIndex, theToIndex)); From d39b54e7118e9c97461b47aba864355d17b1b201 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Thu, 20 Feb 2020 05:39:39 -0500 Subject: [PATCH 15/30] Add missing migration task --- .../ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 3cea17c82b0..7731f589210 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -67,6 +67,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_RESOURCE").dropColumn("20200218.1", "FORCED_ID_PID"); version.onTable("HFJ_RES_VER").dropColumn("20200218.2", "FORCED_ID_PID"); version.onTable("HFJ_RES_VER").addForeignKey("20200218.3", "FK_RESOURCE_HISTORY_RESOURCE").toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID"); + version.onTable("HFJ_RES_VER").modifyColumn("20200220.1", "RES_ID").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.LONG); } protected void init420() { // 20191015 - 20200217 From c0811376b773d35f21e109351acd10c9c16a4193 Mon Sep 17 00:00:00 2001 From: j-chan Date: Thu, 20 Feb 2020 10:52:43 -0500 Subject: [PATCH 16/30] Moved TranslateRawParameters method from BaseHapiFhirResourceDao to BaseStorageDao (#1722) --- .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 6 +- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 37 ------------ .../ca/uhn/fhir/jpa/dao/BaseStorageDao.java | 56 ++++++++++++++++--- 3 files changed, 50 insertions(+), 49 deletions(-) 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 fa4b228bb78..1b14c098e87 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 @@ -133,8 +133,6 @@ public abstract class BaseHapiFhirDao extends BaseStora @Autowired protected ISearchCoordinatorSvc mySearchCoordinatorSvc; @Autowired - protected ISearchParamRegistry mySerarchParamRegistry; - @Autowired protected ITermReadSvc myTerminologySvc; @Autowired protected IResourceHistoryTableDao myResourceHistoryTableDao; @@ -142,8 +140,7 @@ public abstract class BaseHapiFhirDao extends BaseStora protected IResourceTableDao myResourceTableDao; @Autowired protected IResourceTagDao myResourceTagDao; - @Autowired - protected ISearchParamRegistry mySearchParamRegistry; + @Autowired protected DeleteConflictService myDeleteConflictService; @Autowired @@ -1528,4 +1525,5 @@ public abstract class BaseHapiFhirDao extends BaseStora "Resource with ID " + theEntity.getIdDt().getIdPart() + " exists but it is not of type " + theResourceName + ", found resource of type " + theEntity.getResourceType()); } } + } 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 804ecd56017..6c8407107fe 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 @@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.dao; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeResourceDefinition; -import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.delete.DeleteConflictList; @@ -39,16 +38,12 @@ import ca.uhn.fhir.jpa.util.ExpungeOutcome; import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster; import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils; import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils; -import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.api.server.*; -import ca.uhn.fhir.rest.param.ParameterUtil; -import ca.uhn.fhir.rest.param.QualifierDetails; import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; -import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.OperationOutcomeUtil; @@ -1151,38 +1146,6 @@ public abstract class BaseHapiFhirResourceDao extends B return retVal; } - @Transactional(propagation = Propagation.SUPPORTS) - @Override - public void translateRawParameters(Map> theSource, SearchParameterMap theTarget) { - if (theSource == null || theSource.isEmpty()) { - return; - } - - Map searchParams = mySerarchParamRegistry.getActiveSearchParams(getResourceName()); - - Set paramNames = theSource.keySet(); - for (String nextParamName : paramNames) { - QualifierDetails qualifiedParamName = SearchMethodBinding.extractQualifiersFromParameterName(nextParamName); - RuntimeSearchParam param = searchParams.get(qualifiedParamName.getParamName()); - if (param == null) { - String msg = getContext().getLocalizer().getMessageSanitized(BaseHapiFhirResourceDao.class, "invalidSearchParameter", qualifiedParamName.getParamName(), new TreeSet<>(searchParams.keySet())); - throw new InvalidRequestException(msg); - } - - // Should not be null since the check above would have caught it - RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(myResourceName); - RuntimeSearchParam paramDef = mySearchParamRegistry.getSearchParamByName(resourceDef, qualifiedParamName.getParamName()); - - for (String nextValue : theSource.get(nextParamName)) { - QualifiedParamList qualifiedParam = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(qualifiedParamName.getWholeQualifier(), nextValue); - List paramList = Collections.singletonList(qualifiedParam); - IQueryParameterAnd parsedParam = ParameterUtil.parseQueryParams(getContext(), paramDef, nextParamName, paramList); - theTarget.add(qualifiedParamName.getParamName(), parsedParam); - } - - } - } - @Override public DaoMethodOutcome update(T theResource) { return update(theResource, null, null); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java index b845ea07598..1fa8df75b65 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java @@ -21,19 +21,24 @@ package ca.uhn.fhir.jpa.dao; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster; -import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails; -import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.server.SimplePreResourceAccessDetails; -import ca.uhn.fhir.rest.api.server.SimplePreResourceShowDetails; +import ca.uhn.fhir.model.api.IQueryParameterAnd; +import ca.uhn.fhir.rest.api.QualifiedParamList; +import ca.uhn.fhir.rest.api.server.*; +import ca.uhn.fhir.rest.param.ParameterUtil; +import ca.uhn.fhir.rest.param.QualifierDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.FhirTerser; @@ -44,20 +49,23 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.InstantType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; -import java.util.List; -import java.util.Set; +import java.util.*; import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.OO_SEVERITY_ERROR; import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.OO_SEVERITY_INFO; import static org.apache.commons.lang3.StringUtils.defaultString; -import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; public abstract class BaseStorageDao { + @Autowired + protected ISearchParamRegistry mySearchParamRegistry; /** * May be overridden by subclasses to validate resources prior to storage @@ -203,4 +211,36 @@ public abstract class BaseStorageDao { */ protected abstract FhirContext getContext(); + + @Transactional(propagation = Propagation.SUPPORTS) + public void translateRawParameters(Map> theSource, SearchParameterMap theTarget) { + if (theSource == null || theSource.isEmpty()) { + return; + } + + Map searchParams = mySearchParamRegistry.getActiveSearchParams(getResourceName()); + + Set paramNames = theSource.keySet(); + for (String nextParamName : paramNames) { + QualifierDetails qualifiedParamName = SearchMethodBinding.extractQualifiersFromParameterName(nextParamName); + RuntimeSearchParam param = searchParams.get(qualifiedParamName.getParamName()); + if (param == null) { + String msg = getContext().getLocalizer().getMessageSanitized(BaseHapiFhirResourceDao.class, "invalidSearchParameter", qualifiedParamName.getParamName(), new TreeSet<>(searchParams.keySet())); + throw new InvalidRequestException(msg); + } + + // Should not be null since the check above would have caught it + RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(getResourceName()); + RuntimeSearchParam paramDef = mySearchParamRegistry.getSearchParamByName(resourceDef, qualifiedParamName.getParamName()); + + for (String nextValue : theSource.get(nextParamName)) { + QualifiedParamList qualifiedParam = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(qualifiedParamName.getWholeQualifier(), nextValue); + List paramList = Collections.singletonList(qualifiedParam); + IQueryParameterAnd parsedParam = ParameterUtil.parseQueryParams(getContext(), paramDef, nextParamName, paramList); + theTarget.add(qualifiedParamName.getParamName(), parsedParam); + } + + } + } + } From 41b5eb24841352617ac7626eafbeecabb0d53c96 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Sun, 23 Feb 2020 15:43:30 -0500 Subject: [PATCH 17/30] Switching cdrsubscribablefactory from @ComponentScan to @Bean --- .../main/java/ca/uhn/fhir/jpa/config/BaseConfig.java | 10 +++++++--- .../module/channel/SubscriptionChannelFactory.java | 4 ---- .../module/config/BaseSubscriptionConfig.java | 8 +++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java index e5bc3f11542..719586745c9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java @@ -33,6 +33,7 @@ import ca.uhn.fhir.jpa.subscription.dbmatcher.CompositeInMemoryDaoSubscriptionMa import ca.uhn.fhir.jpa.subscription.dbmatcher.DaoSubscriptionMatcher; import ca.uhn.fhir.jpa.subscription.module.cache.LinkedBlockingQueueSubscribableChannelFactory; import ca.uhn.fhir.jpa.subscription.module.channel.ISubscribableChannelFactory; +import ca.uhn.fhir.jpa.subscription.module.channel.SubscriptionChannelFactory; import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher; import ca.uhn.fhir.jpa.subscription.module.matcher.InMemorySubscriptionMatcher; import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices; @@ -204,10 +205,15 @@ public abstract class BaseConfig { * Create a @Primary @Bean if you need a different implementation */ @Bean - public ISubscribableChannelFactory linkedBlockingQueueSubscribableChannelFactory() { + public ISubscribableChannelFactory subscribableChannelFactory() { return new LinkedBlockingQueueSubscribableChannelFactory(); } + @Bean + public SubscriptionChannelFactory subscriptionChannelFactory() { + return new SubscriptionChannelFactory(subscribableChannelFactory()); + } + @Bean @Primary public ISubscriptionMatcher subscriptionMatcherCompositeInMemoryDatabase() { @@ -280,6 +286,4 @@ public abstract class BaseConfig { private static HapiFhirHibernateJpaDialect hibernateJpaDialect(HapiLocalizer theLocalizer) { return new HapiFhirHibernateJpaDialect(theLocalizer); } - - } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/channel/SubscriptionChannelFactory.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/channel/SubscriptionChannelFactory.java index 2be4dc4d6aa..c4b57de6f10 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/channel/SubscriptionChannelFactory.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/channel/SubscriptionChannelFactory.java @@ -22,16 +22,12 @@ package ca.uhn.fhir.jpa.subscription.module.channel; import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage; import ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.SubscribableChannel; -import org.springframework.stereotype.Component; -@Component public class SubscriptionChannelFactory { private ISubscribableChannelFactory mySubscribableChannelFactory; - @Autowired public SubscriptionChannelFactory(ISubscribableChannelFactory theSubscribableChannelFactory) { mySubscribableChannelFactory = theSubscribableChannelFactory; } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java index 8da938d805e..59e22d2f788 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.subscription.module.config; import ca.uhn.fhir.interceptor.executor.InterceptorService; import ca.uhn.fhir.jpa.subscription.module.cache.LinkedBlockingQueueSubscribableChannelFactory; import ca.uhn.fhir.jpa.subscription.module.channel.ISubscribableChannelFactory; +import ca.uhn.fhir.jpa.subscription.module.channel.SubscriptionChannelFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -33,7 +34,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; @ComponentScan(basePackages = {"ca.uhn.fhir.jpa.subscription.module"}) public abstract class BaseSubscriptionConfig { @Bean - public ISubscribableChannelFactory blockingQueueSubscriptionDeliveryChannelFactory() { + public ISubscribableChannelFactory subscribableChannelFactory() { return new LinkedBlockingQueueSubscribableChannelFactory(); } @@ -41,4 +42,9 @@ public abstract class BaseSubscriptionConfig { public InterceptorService interceptorRegistry() { return new InterceptorService("hapi-fhir-jpa-subscription"); } + + @Bean + public SubscriptionChannelFactory subscriptionChannelFactory() { + return new SubscriptionChannelFactory(subscribableChannelFactory()); + } } From b33ad73d46b10b84c101e440b0ad4c7d3d6eef86 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 24 Feb 2020 14:28:14 -0500 Subject: [PATCH 18/30] switch subscriptionchannelfactory to autowire --- .../src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java | 2 +- .../module/channel/SubscriptionChannelFactory.java | 6 ++---- .../subscription/module/config/BaseSubscriptionConfig.java | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java index 719586745c9..46be7d7b1e2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java @@ -211,7 +211,7 @@ public abstract class BaseConfig { @Bean public SubscriptionChannelFactory subscriptionChannelFactory() { - return new SubscriptionChannelFactory(subscribableChannelFactory()); + return new SubscriptionChannelFactory(); } @Bean diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/channel/SubscriptionChannelFactory.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/channel/SubscriptionChannelFactory.java index c4b57de6f10..c2a2eca6589 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/channel/SubscriptionChannelFactory.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/channel/SubscriptionChannelFactory.java @@ -22,16 +22,14 @@ package ca.uhn.fhir.jpa.subscription.module.channel; import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage; import ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.SubscribableChannel; public class SubscriptionChannelFactory { + @Autowired private ISubscribableChannelFactory mySubscribableChannelFactory; - public SubscriptionChannelFactory(ISubscribableChannelFactory theSubscribableChannelFactory) { - mySubscribableChannelFactory = theSubscribableChannelFactory; - } - public SubscribableChannel newDeliveryChannel(String theChannelName) { return mySubscribableChannelFactory.createSubscribableChannel(theChannelName, ResourceDeliveryMessage.class, mySubscribableChannelFactory.getDeliveryChannelConcurrentConsumers()); } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java index 59e22d2f788..2a45b7a99f5 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java @@ -45,6 +45,6 @@ public abstract class BaseSubscriptionConfig { @Bean public SubscriptionChannelFactory subscriptionChannelFactory() { - return new SubscriptionChannelFactory(subscribableChannelFactory()); + return new SubscriptionChannelFactory(); } } From 38bae8866b0e51c4034ed87d9f3e4d8b2f8ff8e1 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Tue, 25 Feb 2020 09:43:34 -0500 Subject: [PATCH 19/30] quiet migration tasks during tests also add description to schema initialization tasks --- .../migrate/taskdef/InitializeSchemaTask.java | 6 ++++-- .../migrate/tasks/HapiFhirJpaMigrationTasks.java | 2 +- .../tasks/SchemaInitializationProvider.java | 16 +++++++++++++++- .../tasks/api/ISchemaInitializationProvider.java | 5 +++++ .../taskdef/InitializeSchemaTaskTest.java | 5 +++++ 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java index 781b0f406b5..e7c0481f56c 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java @@ -40,7 +40,7 @@ public class InitializeSchemaTask extends BaseTask { public InitializeSchemaTask(String theProductVersion, String theSchemaVersion, ISchemaInitializationProvider theSchemaInitializationProvider) { super(theProductVersion, theSchemaVersion); mySchemaInitializationProvider = theSchemaInitializationProvider; - setDescription("Initialize schema"); + setDescription("Initialize schema for " + mySchemaInitializationProvider.getSchemaDescription()); } @Override @@ -59,13 +59,15 @@ public class InitializeSchemaTask extends BaseTask { return; } - logInfo(ourLog, "Initializing schema for {}", driverType); + logInfo(ourLog, "Initializing {} schema for {}", driverType, mySchemaInitializationProvider.getSchemaDescription()); List sqlStatements = mySchemaInitializationProvider.getSqlStatements(driverType); for (String nextSql : sqlStatements) { executeSql(null, nextSql); } + + logInfo(ourLog, "{} schema for {} initialized successfully", driverType, mySchemaInitializationProvider.getSchemaDescription()); } @Override diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 78787f41498..b6df9abad7f 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -904,7 +904,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { protected void init330() { // 20180114 - 20180329 Builder version = forVersion(VersionEnum.V3_3_0); - version.initializeSchema("20180115.0", new SchemaInitializationProvider("/ca/uhn/hapi/fhir/jpa/docs/database", "HFJ_RESOURCE")); + version.initializeSchema("20180115.0", new SchemaInitializationProvider("HAPI FHIR", "/ca/uhn/hapi/fhir/jpa/docs/database", "HFJ_RESOURCE")); Builder.BuilderWithTableName hfjResource = version.onTable("HFJ_RESOURCE"); version.startSectionWithMessage("Starting work on table: " + hfjResource.getTableName()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java index c15e330b6ee..262dbb576af 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java @@ -38,13 +38,16 @@ import static org.apache.commons.lang3.StringUtils.isBlank; public class SchemaInitializationProvider implements ISchemaInitializationProvider { private String mySchemaFileClassPath; + + private String mySchemaDescription; private final String mySchemaExistsIndicatorTable; /** * @param theSchemaFileClassPath pathname to script used to initialize schema * @param theSchemaExistsIndicatorTable a table name we can use to determine if this schema has already been initialized */ - public SchemaInitializationProvider(String theSchemaFileClassPath, String theSchemaExistsIndicatorTable) { + public SchemaInitializationProvider(String theSchemaDescription, String theSchemaFileClassPath, String theSchemaExistsIndicatorTable) { + mySchemaDescription = theSchemaDescription; mySchemaFileClassPath = theSchemaFileClassPath; mySchemaExistsIndicatorTable = theSchemaExistsIndicatorTable; } @@ -115,5 +118,16 @@ public class SchemaInitializationProvider implements ISchemaInitializationProvid mySchemaFileClassPath = theSchemaFileClassPath; return this; } + + @Override + public String getSchemaDescription() { + return mySchemaDescription; + } + + @Override + public SchemaInitializationProvider setSchemaDescription(String theSchemaDescription) { + mySchemaDescription = theSchemaDescription; + return this; + } } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/ISchemaInitializationProvider.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/ISchemaInitializationProvider.java index b645498c9bc..85d2af206bc 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/ISchemaInitializationProvider.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/ISchemaInitializationProvider.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate.tasks.api; */ import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import ca.uhn.fhir.jpa.migrate.tasks.SchemaInitializationProvider; import java.util.List; @@ -28,4 +29,8 @@ public interface ISchemaInitializationProvider { List getSqlStatements(DriverTypeEnum theDriverType); String getSchemaExistsIndicatorTable(); + + String getSchemaDescription(); + + SchemaInitializationProvider setSchemaDescription(String theSchemaDescription); } diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTaskTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTaskTest.java index a55cd045f9d..1247c332246 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTaskTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTaskTest.java @@ -40,6 +40,11 @@ public class InitializeSchemaTaskTest extends BaseTest { return "DONT_MATCH_ME"; } + @Override + public String getSchemaDescription() { + return "TEST"; + } + @Override public boolean equals(Object theO) { if (this == theO) return true; From b2b0ff22b4a8b3bccc3c322c96ed32448e85b51c Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 25 Feb 2020 17:17:13 -0500 Subject: [PATCH 20/30] Collapse date joins (#1726) * Collapse date joins * Add changelog * Address review comments * Add whitespace * fix test Co-authored-by: Ken Stevens --- .../4_3_0/1726-collapse-date-joins.yaml | 6 ++ .../dao/predicate/PredicateBuilderDate.java | 37 +++++-- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 99 +++++++++++++++++++ .../api/ISchemaInitializationProvider.java | 4 +- .../taskdef/InitializeSchemaTaskTest.java | 5 + 5 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1726-collapse-date-joins.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1726-collapse-date-joins.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1726-collapse-date-joins.yaml new file mode 100644 index 00000000000..584b5dd2b63 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1726-collapse-date-joins.yaml @@ -0,0 +1,6 @@ +--- +type: perf +issue: 1726 +title: When performing date range searches in the JPA server, the server was generating extra + unneccessary joins in the generated SQL. This has been streamlined, which should result in + faster searches when performing date ranges. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java index 6ad65712e1d..37274a1d07a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java @@ -39,13 +39,17 @@ import javax.persistence.criteria.Join; import javax.persistence.criteria.Predicate; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Component @Scope("prototype") public class PredicateBuilderDate extends BasePredicateBuilder implements IPredicateBuilder { private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderDate.class); + private Map> myJoinMap; + PredicateBuilderDate(SearchBuilder theSearchBuilder) { super(theSearchBuilder); } @@ -56,7 +60,18 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi List theList, SearchFilterParser.CompareOperation operation) { - Join join = createJoin(SearchBuilderJoinEnum.DATE, theParamName); + boolean newJoin = false; + if (myJoinMap == null) { + myJoinMap = new HashMap<>(); + } + String key = theResourceName + " " + theParamName; + + Join join = myJoinMap.get(key); + if (join == null) { + join = createJoin(SearchBuilderJoinEnum.DATE, theParamName); + myJoinMap.put(key, join); + newJoin = true; + } if (theList.get(0).getMissing() != null) { Boolean missing = theList.get(0).getMissing(); @@ -77,7 +92,14 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi } Predicate orPredicates = myBuilder.or(toArray(codePredicates)); - myQueryRoot.addPredicate(orPredicates); + + if (newJoin) { + Predicate identityAndValuePredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, orPredicates); + myQueryRoot.addPredicate(identityAndValuePredicate); + } else { + myQueryRoot.addPredicate(orPredicates); + } + return orPredicates; } @@ -86,12 +108,13 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi String theParamName, CriteriaBuilder theBuilder, From theFrom) { - return createPredicateDate(theParam, + Predicate predicateDate = createPredicateDate(theParam, theResourceName, theParamName, theBuilder, theFrom, null); + return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, predicateDate); } private Predicate createPredicateDate(IQueryParameterType theParam, @@ -99,7 +122,7 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi String theParamName, CriteriaBuilder theBuilder, From theFrom, - SearchFilterParser.CompareOperation operation) { + SearchFilterParser.CompareOperation theOperation) { Predicate p; if (theParam instanceof DateParam) { @@ -109,7 +132,7 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi p = createPredicateDateFromRange(theBuilder, theFrom, range, - operation); + theOperation); } else { // TODO: handle missing date param? p = null; @@ -119,12 +142,12 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi p = createPredicateDateFromRange(theBuilder, theFrom, range, - operation); + theOperation); } else { throw new IllegalArgumentException("Invalid token type: " + theParam.getClass()); } - return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, p); + return p; } private Predicate createPredicateDateFromRange(CriteriaBuilder theBuilder, diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 3ac22eca837..b9a3a1370c5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -3258,6 +3258,105 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { } } + @Test + public void testSearchWithDateAndReusesExistingJoin() { + // Add a search parameter to Observation.issued, so that between that one + // and the existing one on Observation.effective, we have 2 date search parameters + // on the same resource + { + SearchParameter sp = new SearchParameter(); + sp.setStatus(Enumerations.PublicationStatus.ACTIVE); + sp.addBase("Observation"); + sp.setType(Enumerations.SearchParamType.DATE); + sp.setCode("issued"); + sp.setExpression("Observation.issued"); + mySearchParameterDao.create(sp); + mySearchParamRegistry.forceRefresh(); + } + + // Dates are reversed on these two observations + IIdType obsId1; + { + Observation obs = new Observation(); + obs.setIssuedElement(new InstantType("2020-06-06T12:00:00Z")); + obs.setEffective(new InstantType("2019-06-06T12:00:00Z")); + obsId1 = myObservationDao.create(obs).getId().toUnqualifiedVersionless(); + } + IIdType obsId2; + { + Observation obs = new Observation(); + obs.setIssuedElement(new InstantType("2019-06-06T12:00:00Z")); + obs.setEffective(new InstantType("2020-06-06T12:00:00Z")); + obsId2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless(); + } + + // Add two with a period + IIdType obsId3; + { + Observation obs = new Observation(); + obs.setEffective(new Period().setStartElement(new DateTimeType("2000-06-06T12:00:00Z")).setEndElement(new DateTimeType("2001-06-06T12:00:00Z"))); + obsId3 = myObservationDao.create(obs).getId().toUnqualifiedVersionless(); + } + IIdType obsId4; + { + Observation obs = new Observation(); + obs.setEffective(new Period().setStartElement(new DateTimeType("2001-01-01T12:00:00Z")).setEndElement(new DateTimeType("2002-01-01T12:00:00Z"))); + obsId4 = myObservationDao.create(obs).getId().toUnqualifiedVersionless(); + } + + // Two AND instances of 1 SP + { + myCaptureQueriesListener.clear(); + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("issued", new DateParam("ge2020-06-05")); + params.add("issued", new DateParam("lt2020-06-07")); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); + assertThat(patients.toString(), patients, contains(obsId1)); + String searchQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search query:\n{}", searchQuery); + assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toLowerCase(), "join")); + assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toLowerCase(), "hash_identity")); + assertEquals(searchQuery, 2, StringUtils.countMatches(searchQuery.toLowerCase(), "sp_value_low")); + } + + // Two AND instances of 1 SP and 1 instance of another + { + myCaptureQueriesListener.clear(); + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("issued", new DateParam("ge2020-06-05")); + params.add("issued", new DateParam("lt2020-06-07")); + params.add("date", new DateParam("gt2019-06-05")); + params.add("date", new DateParam("lt2019-06-07")); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); + assertThat(patients.toString(), patients, contains(obsId1)); + String searchQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search query:\n{}", searchQuery); + assertEquals(searchQuery, 2, StringUtils.countMatches(searchQuery.toLowerCase(), "join")); + assertEquals(searchQuery, 2, StringUtils.countMatches(searchQuery.toLowerCase(), "hash_identity")); + assertEquals(searchQuery, 4, StringUtils.countMatches(searchQuery.toLowerCase(), "sp_value_low")); + } + + // Period search + { + myCaptureQueriesListener.clear(); + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add("date", new DateParam("lt2002-01-01T12:00:00Z")); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); + assertThat(patients.toString(), patients, containsInAnyOrder(obsId3, obsId4)); + String searchQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search query:\n{}", searchQuery); + assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toLowerCase(), "join")); + assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toLowerCase(), "hash_identity")); + assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toLowerCase(), "sp_value_low")); + } + + } + + + @Test public void testSearchWithFetchSizeDefaultMaximum() { myDaoConfig.setFetchSizeDefaultMaximum(5); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/ISchemaInitializationProvider.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/ISchemaInitializationProvider.java index 85d2af206bc..1be30796e25 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/ISchemaInitializationProvider.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/ISchemaInitializationProvider.java @@ -21,16 +21,16 @@ package ca.uhn.fhir.jpa.migrate.tasks.api; */ import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; -import ca.uhn.fhir.jpa.migrate.tasks.SchemaInitializationProvider; import java.util.List; public interface ISchemaInitializationProvider { + List getSqlStatements(DriverTypeEnum theDriverType); String getSchemaExistsIndicatorTable(); String getSchemaDescription(); - SchemaInitializationProvider setSchemaDescription(String theSchemaDescription); + ISchemaInitializationProvider setSchemaDescription(String theSchemaDescription); } diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTaskTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTaskTest.java index 1247c332246..c65bcb06006 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTaskTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTaskTest.java @@ -45,6 +45,11 @@ public class InitializeSchemaTaskTest extends BaseTest { return "TEST"; } + @Override + public ISchemaInitializationProvider setSchemaDescription(String theSchemaDescription) { + return this; + } + @Override public boolean equals(Object theO) { if (this == theO) return true; From cb9906580e0572b46614fcd9113ef7c8656969b9 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 25 Feb 2020 21:45:46 -0500 Subject: [PATCH 21/30] Index canonical references (#1728) * Index canonical references * Add changelog * Fix compile error * Add whitespace --- .../changelog/4_3_0/1728-index-canonical.yaml | 5 ++ .../r4/FhirResourceDaoR4SearchNoFtTest.java | 62 ++++++++++++++++-- .../fhir/jpa/model/entity/ResourceLink.java | 63 ++++++++++++------- .../extractor/BaseSearchParamExtractor.java | 18 ++++-- .../jpa/searchparam/extractor/PathAndRef.java | 8 ++- .../extractor/ResourceLinkExtractor.java | 10 +-- .../ResourceIndexedSearchParamsTest.java | 2 +- 7 files changed, 129 insertions(+), 39 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1728-index-canonical.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1728-index-canonical.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1728-index-canonical.yaml new file mode 100644 index 00000000000..5970f650a52 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1728-index-canonical.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 1728 +title: "Fields of type `canonical` were not previously indexed by the JPA server, meaning that some + default search parameters could not be honoured (e.g. StructureDefinition:valueset). This is now corrected." diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index b9a3a1370c5..54973d1d502 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -3,7 +3,14 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.entity.Search; -import ca.uhn.fhir.jpa.model.entity.*; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri; +import ca.uhn.fhir.jpa.model.entity.ResourceLink; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; @@ -33,7 +40,11 @@ import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; import org.hl7.fhir.r4.model.Observation.ObservationStatus; import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType; import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus; -import org.junit.*; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; @@ -44,12 +55,30 @@ import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @SuppressWarnings({"unchecked", "Duplicates"}) @@ -73,6 +102,28 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { myDaoConfig.setReuseCachedSearchResultsForMillis(null); } + @Test + public void testCanonicalReference() { + StructureDefinition sd = new StructureDefinition(); + sd.getSnapshot().addElement().getBinding().setValueSet("http://foo"); + String id = myStructureDefinitionDao.create(sd).getId().toUnqualifiedVersionless().getValue(); + + { + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(StructureDefinition.SP_VALUESET, new ReferenceParam("http://foo")); + List ids = toUnqualifiedVersionlessIdValues(myStructureDefinitionDao.search(map)); + assertThat(ids, contains(id)); + } + { + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(StructureDefinition.SP_VALUESET, new ReferenceParam("http://foo2")); + List ids = toUnqualifiedVersionlessIdValues(myStructureDefinitionDao.search(map)); + assertThat(ids, empty()); + } + } + @Test public void testHasConditionAgeCompare() { Patient patient = new Patient(); @@ -4365,7 +4416,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { } - private String toStringMultiline(List theResults) { StringBuilder b = new StringBuilder(); for (Object next : theResults) { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java index e7e627c035c..f0ae3b08de3 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java @@ -84,22 +84,6 @@ public class ResourceLink extends BaseResourceIndex { super(); } - public ResourceLink(String theSourcePath, ResourceTable theSourceResource, IIdType theTargetResourceUrl, Date theUpdated) { - super(); - setSourcePath(theSourcePath); - setSourceResource(theSourceResource); - setTargetResourceUrl(theTargetResourceUrl); - setUpdated(theUpdated); - } - - public ResourceLink(String theSourcePath, ResourceTable theSourceResource, ResourceTable theTargetResource, Date theUpdated) { - super(); - setSourcePath(theSourcePath); - setSourceResource(theSourceResource); - setTargetResource(theTargetResource); - setUpdated(theUpdated); - } - @Override public boolean equals(Object theObj) { if (this == theObj) { @@ -158,18 +142,25 @@ public class ResourceLink extends BaseResourceIndex { Validate.isTrue(theTargetResourceUrl.hasResourceType()); // if (theTargetResourceUrl.hasIdPart()) { - // do nothing + // do nothing // } else { - // Must have set an url like http://example.org/something - // We treat 'something' as the resource type because of fix for #659. Prior to #659 fix, 'something' was - // treated as the id and 'example.org' was treated as the resource type - // TODO: log a warning? + // Must have set an url like http://example.org/something + // We treat 'something' as the resource type because of fix for #659. Prior to #659 fix, 'something' was + // treated as the id and 'example.org' was treated as the resource type + // TODO: log a warning? // } myTargetResourceType = theTargetResourceUrl.getResourceType(); myTargetResourceUrl = theTargetResourceUrl.getValue(); } + public void setTargetResourceUrlCanonical(String theTargetResourceUrl) { + Validate.notBlank(theTargetResourceUrl); + + myTargetResourceType = "(unknown)"; + myTargetResourceUrl = theTargetResourceUrl; + } + public Date getUpdated() { return myUpdated; } @@ -216,4 +207,34 @@ public class ResourceLink extends BaseResourceIndex { return b.toString(); } + public static ResourceLink forAbsoluteReference(String theSourcePath, ResourceTable theSourceResource, IIdType theTargetResourceUrl, Date theUpdated) { + ResourceLink retVal = new ResourceLink(); + retVal.setSourcePath(theSourcePath); + retVal.setSourceResource(theSourceResource); + retVal.setTargetResourceUrl(theTargetResourceUrl); + retVal.setUpdated(theUpdated); + return retVal; + } + + /** + * Factory for canonical URL + */ + public static ResourceLink forLogicalReference(String theSourcePath, ResourceTable theSourceResource, String theTargetResourceUrl, Date theUpdated) { + ResourceLink retVal = new ResourceLink(); + retVal.setSourcePath(theSourcePath); + retVal.setSourceResource(theSourceResource); + retVal.setTargetResourceUrlCanonical(theTargetResourceUrl); + retVal.setUpdated(theUpdated); + return retVal; + } + + public static ResourceLink forLocalReference(String theSourcePath, ResourceTable theSourceResource, ResourceTable theTargetResource, Date theUpdated) { + ResourceLink retVal = new ResourceLink(); + retVal.setSourcePath(theSourcePath); + retVal.setSourceResource(theSourceResource); + retVal.setTargetResource(theTargetResource); + retVal.setUpdated(theUpdated); + return retVal; + } + } diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java index 79bf1c41f4a..10583e7095d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java @@ -125,23 +125,29 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor case "uri": case "canonical": String typeName = toTypeName(value); + IPrimitiveType valuePrimitive = (IPrimitiveType) value; + IBaseReference fakeReference = (IBaseReference) myContext.getElementDefinition("Reference").newInstance(); + fakeReference.setReference(valuePrimitive.getValueAsString()); // Canonical has a root type of "uri" if ("canonical".equals(typeName)) { - IPrimitiveType valuePrimitive = (IPrimitiveType) value; - IBaseReference fakeReference = (IBaseReference) myContext.getElementDefinition("Reference").newInstance(); - fakeReference.setReference(valuePrimitive.getValueAsString()); /* * See #1583 * Technically canonical fields should not allow local references (e.g. * Questionnaire/123) but it seems reasonable for us to interpret a canonical - * containing a local reference for what it is, and allow people to seaerch + * containing a local reference for what it is, and allow people to search * based on that. */ IIdType parsed = fakeReference.getReferenceElement(); if (parsed.hasIdPart() && parsed.hasResourceType() && !parsed.isAbsolute()) { - PathAndRef ref = new PathAndRef(searchParam.getName(), path, fakeReference); + PathAndRef ref = new PathAndRef(searchParam.getName(), path, fakeReference, false); + params.add(ref); + break; + } + + if (parsed.isAbsolute()) { + PathAndRef ref = new PathAndRef(searchParam.getName(), path, fakeReference, true); params.add(ref); break; } @@ -165,7 +171,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor return; } - PathAndRef ref = new PathAndRef(searchParam.getName(), path, valueRef); + PathAndRef ref = new PathAndRef(searchParam.getName(), path, valueRef, false); params.add(ref); break; default: diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java index 82cb389d15f..22bc6d65b3d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java @@ -27,15 +27,21 @@ public class PathAndRef { private final String myPath; private final IBaseReference myRef; private final String mySearchParamName; + private final boolean myCanonical; /** * Constructor */ - public PathAndRef(String theSearchParamName, String thePath, IBaseReference theRef) { + public PathAndRef(String theSearchParamName, String thePath, IBaseReference theRef, boolean theCanonical) { super(); mySearchParamName = theSearchParamName; myPath = thePath; myRef = theRef; + myCanonical = theCanonical; + } + + public boolean isCanonical() { + return myCanonical; } public String getSearchParamName() { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceLinkExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceLinkExtractor.java index dbd8f79f9d9..c496f46e3f5 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceLinkExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceLinkExtractor.java @@ -87,8 +87,10 @@ public class ResourceLinkExtractor { theParams.myPopulatedResourceLinkParameters.add(thePathAndRef.getSearchParamName()); - if (LogicalReferenceHelper.isLogicalReference(myModelConfig, nextId)) { - ResourceLink resourceLink = new ResourceLink(thePathAndRef.getPath(), theEntity, nextId, theUpdateTime); + boolean canonical = thePathAndRef.isCanonical(); + if (LogicalReferenceHelper.isLogicalReference(myModelConfig, nextId) || canonical) { + String value = nextId.getValue(); + ResourceLink resourceLink = ResourceLink.forLogicalReference(thePathAndRef.getPath(), theEntity, value, theUpdateTime); if (theParams.myLinks.add(resourceLink)) { ourLog.debug("Indexing remote resource reference URL: {}", nextId); } @@ -130,7 +132,7 @@ public class ResourceLinkExtractor { String msg = myContext.getLocalizer().getMessage(BaseSearchParamExtractor.class, "externalReferenceNotAllowed", nextId.getValue()); throw new InvalidRequestException(msg); } else { - ResourceLink resourceLink = new ResourceLink(thePathAndRef.getPath(), theEntity, nextId, theUpdateTime); + ResourceLink resourceLink = ResourceLink.forAbsoluteReference(thePathAndRef.getPath(), theEntity, nextId, theUpdateTime); if (theParams.myLinks.add(resourceLink)) { ourLog.debug("Indexing remote resource reference URL: {}", nextId); } @@ -165,7 +167,7 @@ public class ResourceLinkExtractor { return null; } - return new ResourceLink(nextPathAndRef.getPath(), theEntity, targetResource, theUpdateTime); + return ResourceLink.forLocalReference(nextPathAndRef.getPath(), theEntity, targetResource, theUpdateTime); } } diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamsTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamsTest.java index 252834ab82c..34032f5c138 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamsTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamsTest.java @@ -32,7 +32,7 @@ public class ResourceIndexedSearchParamsTest { myTarget.setResourceType("Organization"); myParams = new ResourceIndexedSearchParams(source); - ResourceLink link = new ResourceLink("organization", source, myTarget, new Date()); + ResourceLink link = ResourceLink.forLocalReference("organization", source, myTarget, new Date()); myParams.getResourceLinks().add(link); } From 5bc554949b92927f7571168662f247dff0b10e2c Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 28 Feb 2020 10:57:26 -0500 Subject: [PATCH 22/30] Remove deleted resources from cached search results (#1732) * Remove deleted resources from cached search results * Add changelog * Add tests --- .../context/BaseRuntimeElementDefinition.java | 23 +++++----- .../java/ca/uhn/fhir/context/FhirContext.java | 5 +- .../1732-remove-deleted-from-results.yaml | 6 +++ .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 3 ++ .../r4/ResourceProviderR4CacheTest.java | 46 ++++++++++++++++++- .../BaseRuntimeElementDefinitionTest.java | 23 ++++++++++ 6 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1732-remove-deleted-from-results.yaml create mode 100644 hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/context/BaseRuntimeElementDefinitionTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java index 90e4a50ac91..fbe2fc502dc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java @@ -22,8 +22,11 @@ package ca.uhn.fhir.context; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.lang.reflect.Constructor; import java.util.*; @@ -33,11 +36,11 @@ public abstract class BaseRuntimeElementDefinition { private final Class myImplementingClass; private final String myName; private final boolean myStandardType; - private Map, Constructor> myConstructors = Collections.synchronizedMap(new HashMap, Constructor>()); - private List myExtensions = new ArrayList(); - private List myExtensionsModifier = new ArrayList(); - private List myExtensionsNonModifier = new ArrayList(); - private Map myUrlToExtension = new HashMap(); + private Map, Constructor> myConstructors = Collections.synchronizedMap(new HashMap<>()); + private List myExtensions = new ArrayList<>(); + private List myExtensionsModifier = new ArrayList<>(); + private List myExtensionsNonModifier = new ArrayList<>(); + private Map myUrlToExtension = new HashMap<>(); private BaseRuntimeElementDefinition myRootParentDefinition; public BaseRuntimeElementDefinition(String theName, Class theImplementingClass, boolean theStandardType) { @@ -56,19 +59,17 @@ public abstract class BaseRuntimeElementDefinition { myImplementingClass = theImplementingClass; } - public void addExtension(RuntimeChildDeclaredExtensionDefinition theExtension) { - if (theExtension == null) { - throw new NullPointerException(); - } + public void addExtension(@Nonnull RuntimeChildDeclaredExtensionDefinition theExtension) { + Validate.notNull(theExtension, "theExtension must not be null"); myExtensions.add(theExtension); } public abstract ChildTypeEnum getChildType(); @SuppressWarnings("unchecked") - private Constructor getConstructor(Object theArgument) { + private Constructor getConstructor(@Nullable Object theArgument) { - Class argumentType; + Class argumentType; if (theArgument == null) { argumentType = VOID_CLASS; } else { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index c938e30761c..c4c7dd44042 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -377,9 +377,8 @@ public class FhirContext { */ public RuntimeResourceDefinition getResourceDefinition(final Class theResourceType) { validateInitialized(); - if (theResourceType == null) { - throw new NullPointerException("theResourceType can not be null"); - } + Validate.notNull(theResourceType, "theResourceType can not be null"); + if (Modifier.isAbstract(theResourceType.getModifiers())) { throw new IllegalArgumentException("Can not scan abstract or interface class (resource definitions must be concrete classes): " + theResourceType.getName()); } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1732-remove-deleted-from-results.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1732-remove-deleted-from-results.yaml new file mode 100644 index 00000000000..070819f9846 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1732-remove-deleted-from-results.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 1732 +title: In the JPA server, quickly deleting a resource and then performing a query that had recently returned that + search result could cause a cached stub resource (containing no data but with an ID and metadata populated) to + be returned. This has been corrected. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 5a21d9a7933..e120a964a8d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -504,6 +504,9 @@ public class SearchBuilder implements ISearchBuilder { ResourcePersistentId resourceId; for (ResourceSearchView next : resourceSearchViewList) { + if (next.getDeleted() != null) { + continue; + } Class resourceType = myContext.getResourceDefinition(next.getResourceType()).getImplementingClass(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CacheTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CacheTest.java index ca08514295b..88c27f47a2e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CacheTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CacheTest.java @@ -9,20 +9,29 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; import org.junit.After; import org.junit.AfterClass; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.blankOrNullString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.core.IsNot.not; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test { + private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR4CacheTest.class); private CapturingInterceptor myCapturingInterceptor; @Autowired private ISearchDao mySearchEntityDao; @@ -184,6 +193,39 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test { assertEquals(results1.getId(), results2.getId()); } + @Test + public void testDeletedSearchResultsNotReturnedFromCache() { + Patient p = new Patient(); + p.addName().setFamily("Foo"); + String p1Id = myPatientDao.create(p).getId().toUnqualifiedVersionless().getValue(); + + p = new Patient(); + p.addName().setFamily("Foo"); + String p2Id = myPatientDao.create(p).getId().toUnqualifiedVersionless().getValue(); + + Bundle resp1 = ourClient + .search() + .forResource("Patient") + .where(Patient.NAME.matches().value("foo")) + .returnBundle(Bundle.class) + .execute(); + assertEquals(2, resp1.getEntry().size()); + + ourClient.delete().resourceById(new IdType(p1Id)).execute(); + + Bundle resp2 = ourClient + .search() + .forResource("Patient") + .where(Patient.NAME.matches().value("foo")) + .returnBundle(Bundle.class) + .execute(); + + assertEquals(resp1.getId(), resp2.getId()); + + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp2)); + assertEquals(1, resp2.getEntry().size()); + } + @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/context/BaseRuntimeElementDefinitionTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/context/BaseRuntimeElementDefinitionTest.java new file mode 100644 index 00000000000..965a327ef3a --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/context/BaseRuntimeElementDefinitionTest.java @@ -0,0 +1,23 @@ +package ca.uhn.fhir.context; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class BaseRuntimeElementDefinitionTest { + + @Test + public void testNewInstance_InvalidArgumentType() { + FhirContext ctx = FhirContext.forR4(); + + BaseRuntimeElementDefinition def = ctx.getElementDefinition("string"); + + try { + def.newInstance(123); + fail(); + } catch (ConfigurationException e) { + assertEquals("Failed to instantiate type:org.hl7.fhir.r4.model.StringType", e.getMessage()); + } + } + +} From 8c78e465b10d1315aea60c9efa6da32ce9bdd3f7 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 28 Feb 2020 14:41:15 -0500 Subject: [PATCH 23/30] Defer large term delta adds (#1736) * Defer large term delta adds * Add changelog --- ...36-defer-large-terminology-delta-adds.yaml | 7 +++ .../term/TermCodeSystemStorageSvcImpl.java | 56 ++++++++++++------- .../jpa/term/TerminologySvcDeltaR4Test.java | 54 ++++++++++++++++++ 3 files changed, 96 insertions(+), 21 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1736-defer-large-terminology-delta-adds.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1736-defer-large-terminology-delta-adds.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1736-defer-large-terminology-delta-adds.yaml new file mode 100644 index 00000000000..ba7d3a1f7df --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1736-defer-large-terminology-delta-adds.yaml @@ -0,0 +1,7 @@ +--- +type: add +issue: 1736 +title: When performing large terminology concept additions via the delta addition service, concepts will + now be added via the deferred storage service, meaning that they will be added in small incremental batches + instead of as a part of one large transaction. This helps to avoid timeouts and memory issues when uploading + large collections of concepts. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 3b36c1a97ca..94c5d7cd1a1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -45,7 +45,6 @@ import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; -import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -53,11 +52,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.ValidateUtil; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ListMultimap; import org.apache.commons.lang3.Validate; -import org.hibernate.ScrollMode; -import org.hibernate.ScrollableResults; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ConceptMap; @@ -78,11 +73,6 @@ import javax.annotation.Nonnull; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -165,11 +155,12 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { IIdType codeSystemId = cs.getResource().getIdDt(); UploadStatistics retVal = new UploadStatistics(codeSystemId); + HashMap codeToConcept = new HashMap<>(); // Add root concepts for (TermConcept nextRootConcept : theAdditions.getRootConcepts()) { List parentCodes = Collections.emptyList(); - addConcept(csv, parentCodes, nextRootConcept, retVal, true, 0); + addConceptInHierarchy(csv, parentCodes, nextRootConcept, retVal, codeToConcept, 0); } return retVal; @@ -445,7 +436,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { Validate.isTrue(myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3), "Terminology operations only supported in DSTU3+ mode"); } - private void addConcept(TermCodeSystemVersion theCsv, Collection theParentCodes, TermConcept theConceptToAdd, UploadStatistics theStatisticsTracker, boolean theRootConcept, int theSequence) { + private void addConceptInHierarchy(TermCodeSystemVersion theCsv, Collection theParentCodes, TermConcept theConceptToAdd, UploadStatistics theStatisticsTracker, Map theCodeToConcept, int theSequence) { TermConcept conceptToAdd = theConceptToAdd; List childrenToAdd = theConceptToAdd.getChildren(); @@ -470,15 +461,18 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { for (String nextParentCode : theParentCodes) { // Don't add parent links that already exist for the code - if (existingParentLinks.stream().anyMatch(t->t.getParent().getCode().equals(nextParentCode))) { + if (existingParentLinks.stream().anyMatch(t -> t.getParent().getCode().equals(nextParentCode))) { continue; } - Optional nextParentOpt = myConceptDao.findByCodeSystemAndCode(theCsv, nextParentCode); - if (nextParentOpt.isPresent() == false) { + TermConcept nextParentOpt = theCodeToConcept.get(nextParentCode); + if (nextParentOpt == null) { + nextParentOpt = myConceptDao.findByCodeSystemAndCode(theCsv, nextParentCode).orElse(null); + } + if (nextParentOpt == null) { throw new InvalidRequestException("Unable to add code \"" + nextCodeToAdd + "\" to unknown parent: " + nextParentCode); } - parentConceptsWeShouldLinkTo.add(nextParentOpt.get()); + parentConceptsWeShouldLinkTo.add(nextParentOpt); } if (conceptToAdd.getSequence() == null) { @@ -489,10 +483,17 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { // force a reindex, and it'll be regenerated then conceptToAdd.setParentPids(null); conceptToAdd.setCodeSystemVersion(theCsv); - conceptToAdd = myConceptDao.save(conceptToAdd); - Long nextConceptPid = conceptToAdd.getId(); - Validate.notNull(nextConceptPid); + if (theStatisticsTracker.getUpdatedConceptCount() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { + conceptToAdd = myConceptDao.save(conceptToAdd); + Long nextConceptPid = conceptToAdd.getId(); + Validate.notNull(nextConceptPid); + } else { + myDeferredStorageSvc.addConceptToStorageQueue(conceptToAdd); + } + + theCodeToConcept.put(conceptToAdd.getCode(), conceptToAdd); + theStatisticsTracker.incrementUpdatedConceptCount(); // Add link to new child to the parent @@ -505,7 +506,13 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { nextParentConcept.getChildren().add(parentLink); conceptToAdd.getParents().add(parentLink); ourLog.info("Saving parent/child link - Parent[{}] Child[{}]", parentLink.getParent().getCode(), parentLink.getChild().getCode()); + + if (theStatisticsTracker.getUpdatedConceptCount() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { myConceptParentChildLinkDao.save(parentLink); + } else { + myDeferredStorageSvc.addConceptLinkToStorageQueue(parentLink); + } + } ourLog.trace("About to save parent-child links"); @@ -519,13 +526,20 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { for (int i = 0; i < nextChild.getParents().size(); i++) { if (nextChild.getParents().get(i).getId() == null) { String parentCode = nextChild.getParents().get(i).getParent().getCode(); - TermConcept parentConcept = myConceptDao.findByCodeSystemAndCode(theCsv, parentCode).orElseThrow(() -> new IllegalArgumentException("Unknown parent code: " + parentCode)); + TermConcept parentConcept = theCodeToConcept.get(parentCode); + if (parentConcept == null) { + parentConcept = myConceptDao.findByCodeSystemAndCode(theCsv, parentCode).orElse(null); + } + if (parentConcept == null) { + throw new IllegalArgumentException("Unknown parent code: " + parentCode); + } + nextChild.getParents().get(i).setParent(parentConcept); } } Collection parentCodes = nextChild.getParents().stream().map(t -> t.getParent().getCode()).collect(Collectors.toList()); - addConcept(theCsv, parentCodes, nextChild, theStatisticsTracker, false, childIndex); + addConceptInHierarchy(theCsv, parentCodes, nextChild, theStatisticsTracker, theCodeToConcept, childIndex); childIndex++; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java index 0d0dd62fe54..074f0d39ad6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java @@ -1,10 +1,12 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.context.support.IContextValidationSupport; +import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.UriParam; @@ -16,12 +18,15 @@ import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.ValueSet; +import org.junit.After; import org.junit.AfterClass; import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -29,12 +34,18 @@ import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.leftPad; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcDeltaR4Test.class); + @After + public void after() { + myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize()); + } + @Test public void testAddRootConcepts() { @@ -349,6 +360,49 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { } + @Test + public void testAddLargeHierarchy() { + myDaoConfig.setDeferIndexingForCodesystemsOfSize(5); + + createNotPresentCodeSystem(); + ValueSet vs; + vs = expandNotPresentCodeSystem(); + assertEquals(0, vs.getExpansion().getContains().size()); + + CustomTerminologySet delta = new CustomTerminologySet(); + + // Create a nice deep hierarchy + TermConcept concept = delta.addRootConcept("Root", "Root"); + int nestedDepth = 10; + for (int i = 0; i < nestedDepth; i++) { + String name = concept.getCode(); + concept = concept.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode(name + "0").setDisplay(name + "0"); + } + + myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta); + + assertFalse(myTermDeferredStorageSvc.isStorageQueueEmpty()); + while (!myTermDeferredStorageSvc.isStorageQueueEmpty()) { + myTermDeferredStorageSvc.saveDeferred(); + } + + List expectedHierarchy = new ArrayList<>(); + for (int i = 0; i < nestedDepth + 1; i++) { + String expected = leftPad("", i, " ") + + "Root" + + leftPad("", i, "0") + + " seq=0"; + expectedHierarchy.add(expected); + } + + assertHierarchyContains(expectedHierarchy.toArray(new String[0])); + + } + + @Autowired + private ITermDeferredStorageSvc myTermDeferredStorageSvc; + + @Test public void testAddModifiesExistingCodesInPlace() { From ed7b9fc6b6af93e673de7f0e887ee6d99f1ae7cb Mon Sep 17 00:00:00 2001 From: Gary Graham Date: Fri, 28 Feb 2020 16:09:16 -0500 Subject: [PATCH 24/30] Initial attempt at integrating elastic APM for search performance --- hapi-fhir-jpaserver-base/pom.xml | 5 +++++ .../fhir/jpa/search/SearchCoordinatorSvcImpl.java | 13 +++++++++---- pom.xml | 6 ++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 00fd60b7ff5..f085f2e4d45 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -48,6 +48,11 @@ commons-csv
+ + co.elastic.apm + apm-agent-api + + ca.uhn.hapi.fhir hapi-fhir-base diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index 49ef70e13b6..8965e732cbd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -56,6 +56,9 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.ICachedSearchDetails; import ca.uhn.fhir.util.AsyncUtil; import ca.uhn.fhir.util.StopWatch; +import co.elastic.apm.api.ElasticApm; +import co.elastic.apm.api.Span; +import co.elastic.apm.api.Transaction; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -601,7 +604,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { private List myPreviouslyAddedResourcePids; private Integer myMaxResultsToFetch; private SearchRuntimeDetails mySearchRuntimeDetails; - + private Transaction myParentTransaction; /** * Constructor */ @@ -614,6 +617,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { mySearchRuntimeDetails = new SearchRuntimeDetails(theRequest, mySearch.getUuid()); mySearchRuntimeDetails.setQueryString(theParams.toNormalizedQueryString(theCallingDao.getContext())); myRequest = theRequest; + myParentTransaction = ElasticApm.currentTransaction(); } /** @@ -841,7 +845,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { @Override public Void call() { StopWatch sw = new StopWatch(); - + Span span = myParentTransaction.startSpan("db", "H2", "search"); + span.setName("search2!"); try { // Create an initial search in the DB and give it an ID saveSearch(); @@ -897,7 +902,6 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { ourLog.error("Failed during search loading after {}ms", sw.getMillis(), t); } myUnsyncedPids.clear(); - Throwable rootCause = ExceptionUtils.getRootCause(t); rootCause = defaultIfNull(rootCause, t); @@ -924,12 +928,13 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FAILED, params); saveSearch(); - + span.captureException(t); } finally { myIdToSearchTask.remove(mySearch.getUuid()); myInitialCollectionLatch.countDown(); markComplete(); + span.end(); } return null; diff --git a/pom.xml b/pom.xml index ef8d1092440..8522fbdeed9 100644 --- a/pom.xml +++ b/pom.xml @@ -679,6 +679,7 @@ UTF-8 1.0.1 + 1.13.0 5.4.1
@@ -1036,6 +1037,11 @@ httpcore ${httpcore_version}
+ + co.elastic.apm + apm-agent-api + ${elastic_apm_version} + org.apache.jena apache-jena-libs From b2f9dd457876cf5408b68ed15c3e6707bcea9d5b Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Fri, 28 Feb 2020 16:38:13 -0500 Subject: [PATCH 25/30] extend pointcutlatch and simplify FhirClientResourceRetriever (#1737) * extend pointcutlatch and simplify FhirClientResourceRetriever * extend pointcutlatch and simplify FhirClientResourceRetriever --- .../FhirClientResourceRetriever.java | 2 +- .../uhn/test/concurrency/PointcutLatch.java | 38 ++++++++++++++----- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/standalone/FhirClientResourceRetriever.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/standalone/FhirClientResourceRetriever.java index 2a4f5ba24ab..d112209ae2d 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/standalone/FhirClientResourceRetriever.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/standalone/FhirClientResourceRetriever.java @@ -45,6 +45,6 @@ public class FhirClientResourceRetriever implements IResourceRetriever { public IBaseResource getResource(IIdType payloadId) throws ResourceGoneException { RuntimeResourceDefinition resourceDef = myFhirContext.getResourceDefinition(payloadId.getResourceType()); - return myClient.search().forResource(resourceDef.getName()).withIdAndCompartment(payloadId.getIdPart(), payloadId.getResourceType()).execute(); + return myClient.read().resource(resourceDef.getName()).withId(payloadId).execute(); } } diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/concurrency/PointcutLatch.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/concurrency/PointcutLatch.java index 0c95bca015b..7d9b94b7ed8 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/concurrency/PointcutLatch.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/concurrency/PointcutLatch.java @@ -51,6 +51,7 @@ public class PointcutLatch implements IAnonymousInterceptor, IPointcutLatch { private int myDefaultTimeoutSeconds = DEFAULT_TIMEOUT_SECONDS; private final Pointcut myPointcut; private int myInitialCount; + private boolean myExactMatch; public PointcutLatch(Pointcut thePointcut) { @@ -69,19 +70,32 @@ public class PointcutLatch implements IAnonymousInterceptor, IPointcutLatch { } @Override - public void setExpectedCount(int count) { + public void setExpectedCount(int theCount) { + this.setExpectedCount(theCount, true); + } + + public void setExpectedCount(int theCount, boolean theExactMatch) { if (myCountdownLatch.get() != null) { throw new PointcutLatchException("setExpectedCount() called before previous awaitExpected() completed."); } - createLatch(count); - ourLog.info("Expecting {} calls to {} latch", count, name); + myExactMatch = theExactMatch; + createLatch(theCount); + if (theExactMatch) { + ourLog.info("Expecting exactly {} calls to {} latch", theCount, name); + } else { + ourLog.info("Expecting at least {} calls to {} latch", theCount, name); + } } - private void createLatch(int count) { + public void setExpectAtLeast(int theCount) { + setExpectedCount(theCount, false); + } + + private void createLatch(int theCount) { myFailures.set(Collections.synchronizedList(new ArrayList<>())); myCalledWith.set(Collections.synchronizedList(new ArrayList<>())); - myCountdownLatch.set(new CountDownLatch(count)); - myInitialCount = count; + myCountdownLatch.set(new CountDownLatch(theCount)); + myInitialCount = theCount; } private void addFailure(String failure) { @@ -153,10 +167,14 @@ public class PointcutLatch implements IAnonymousInterceptor, IPointcutLatch { @Override public void invoke(Pointcut thePointcut, HookParams theArgs) { CountDownLatch latch = myCountdownLatch.get(); - if (latch == null) { - throw new PointcutLatchException("invoke() called outside of setExpectedCount() .. awaitExpected(). Probably got more invocations than expected or clear() was called before invoke() arrived.", theArgs); - } else if (latch.getCount() <= 0) { - addFailure("invoke() called when countdown was zero."); + if (myExactMatch) { + if (latch == null) { + throw new PointcutLatchException("invoke() called outside of setExpectedCount() .. awaitExpected(). Probably got more invocations than expected or clear() was called before invoke() arrived.", theArgs); + } else if (latch.getCount() <= 0) { + addFailure("invoke() called when countdown was zero."); + } + } else if (latch == null || latch.getCount() <= 0) { + return; } if (myCalledWith.get() != null) { From 8d9119faef6e3cef7ad5828ad34f66ad15121f88 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Mon, 2 Mar 2020 14:37:09 -0800 Subject: [PATCH 26/30] Rework span name --- .../java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index 8965e732cbd..7125a415277 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -845,8 +845,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { @Override public Void call() { StopWatch sw = new StopWatch(); - Span span = myParentTransaction.startSpan("db", "H2", "search"); - span.setName("search2!"); + Span span = myParentTransaction.startSpan("db", "query", "search"); + span.setName("FHIR Database Search"); try { // Create an initial search in the DB and give it an ID saveSearch(); From 13fbdf418f0ec56d14d2ca38e03fedb75c6abaaa Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 3 Mar 2020 05:21:43 -0500 Subject: [PATCH 27/30] Add ignored test for #1740 --- .../FhirInstanceValidatorR4Test.java | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index fe617e9b955..f2322d13bc9 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -66,10 +66,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class FhirInstanceValidatorR4Test extends BaseTest { @@ -280,6 +283,33 @@ public class FhirInstanceValidatorR4Test extends BaseTest { assertEquals("Primitive types must have a value that is not empty", all.get(0).getMessage()); } + /** + * See #1740 + */ + @Ignore + @Test + public void testValidateScalarInRepeatableField() { + String operationDefinition = "{\n" + + " \"resourceType\": \"OperationDefinition\",\n" + + " \"name\": \"Questionnaire\",\n" + + " \"status\": \"draft\",\n" + + " \"kind\" : \"operation\",\n" + + " \"code\": \"populate\",\n" + + " \"resource\": \"Patient\",\n" + // should be array + " \"system\": false,\n" + " " + + " \"type\": false,\n" + + " \"instance\": true\n" + + "}"; + + FhirValidator val = ourCtx.newValidator(); + val.registerValidatorModule(new FhirInstanceValidator(myDefaultValidationSupport)); + + ValidationResult result = val.validateWithResult(operationDefinition); + List all = logResultsAndReturnAll(result); + assertFalse(result.isSuccessful()); + assertEquals("Primitive types must have a value that is not empty", all.get(0).getMessage()); + } + /** * See #1676 - We should ignore schema location */ @@ -400,7 +430,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { List errors = logResultsAndReturnNonInformationalOnes(output); errors = errors .stream() - .filter(t->t.getMessage().contains("Bundle entry missing fullUrl")) + .filter(t -> t.getMessage().contains("Bundle entry missing fullUrl")) .collect(Collectors.toList()); assertEquals(5, errors.size()); } @@ -658,7 +688,6 @@ public class FhirInstanceValidatorR4Test extends BaseTest { .setCode("acd"); - // Should pass ValidationResult output = val.validateWithResult(input); List all = logResultsAndReturnErrorOnes(output); @@ -1094,7 +1123,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { @Ignore public void testValidateDecimalWithTrailingDot() { String input = "{" + - " \"resourceType\": \"Observation\"," + + " \"resourceType\": \"Observation\"," + " \"status\": \"final\"," + " \"subject\": {\"reference\":\"Patient/123\"}," + " \"code\": { \"coding\": [{ \"system\":\"http://foo\", \"code\":\"123\" }] }," + @@ -1110,8 +1139,8 @@ public class FhirInstanceValidatorR4Test extends BaseTest { " },\n" + " \"text\": \"210.0-925.\"\n" + " }\n" + - " ]"+ - "}"; + " ]" + + "}"; ourLog.info(input); ValidationResult output = myVal.validateWithResult(input); logResultsAndReturnAll(output); @@ -1263,7 +1292,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { String encoded = loadResource("/r4/r4-caredove-bundle.json"); IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class); - when(resourceFetcher.validationPolicy(any(),anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); + when(resourceFetcher.validationPolicy(any(), anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); myInstanceVal.setValidatorResourceFetcher(resourceFetcher); myVal.validateWithResult(encoded); From ac9a371f18c5df738aa7922821ad726f6e2a037c Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 3 Mar 2020 08:27:51 -0500 Subject: [PATCH 28/30] Add test --- .../fhir/rest/param/QualifierDetailsTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java new file mode 100644 index 00000000000..d63b545b74e --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java @@ -0,0 +1,21 @@ +package ca.uhn.fhir.rest.param; + +import com.google.common.collect.Sets; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class QualifierDetailsTest { + + @Test + public void testBlacklist() { + + QualifierDetails details = new QualifierDetails(); + details.setColonQualifier(":Patient"); + assertFalse(details.passes(null, Sets.newHashSet(":Patient"))); + assertTrue(details.passes(null, Sets.newHashSet(":Observation"))); + + } + + +} From 3c2c1dab2eb7c96b63625cff0c452495e1cde6e5 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 3 Mar 2020 08:28:26 -0500 Subject: [PATCH 29/30] Error on missing profile validation (#1742) * Error on missing profile validation * Add changelog * Test fixes --- ...2-error-on-missing-profile-validation.yaml | 6 +++++ .../4_3_0/1742-terminology-delta-defer.yaml | 6 +++++ .../java/ca/uhn/fhir/jpa/dao/DaoConfig.java | 6 ++--- .../fhir/jpa/dao/data/ITermConceptDao.java | 4 +++ .../data/ITermConceptParentChildLinkDao.java | 6 +++++ .../uhn/fhir/jpa/entity/TermCodeSystem.java | 4 +-- .../jpa/entity/TermCodeSystemVersion.java | 6 ++--- .../ca/uhn/fhir/jpa/entity/TermConcept.java | 6 ++--- .../jpa/entity/TermConceptDesignation.java | 4 +-- .../entity/TermConceptParentChildLink.java | 6 ++--- .../fhir/jpa/entity/TermConceptProperty.java | 4 +-- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 2 ++ .../term/TermCodeSystemStorageSvcImpl.java | 21 +++++++++++---- .../jpa/term/TermDeferredStorageSvcImpl.java | 7 +++++ .../jpa/term/api/ITermDeferredStorageSvc.java | 5 ++++ .../FhirResourceDaoDstu3TerminologyTest.java | 6 +++++ .../FhirResourceDaoDstu3ValidateTest.java | 15 ++++++----- .../r4/FhirResourceDaoR4TerminologyTest.java | 2 ++ .../dao/r4/FhirResourceDaoR4ValidateTest.java | 19 ++++++++----- .../provider/r4/ResourceProviderR4Test.java | 27 +++++++++++++++++++ .../jpa/term/TerminologySvcDeltaR4Test.java | 26 +++++++++++++++--- .../jpa/term/TerminologySvcImplDstu3Test.java | 2 ++ .../hapi/validation/ValidatorWrapper.java | 7 +++++ .../FhirInstanceValidatorR4Test.java | 5 +++- 24 files changed, 163 insertions(+), 39 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-error-on-missing-profile-validation.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-terminology-delta-defer.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-error-on-missing-profile-validation.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-error-on-missing-profile-validation.yaml new file mode 100644 index 00000000000..c468181274e --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-error-on-missing-profile-validation.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 1742 +title: "When validating a resource, the validator will now report an error if the resource declares conformance + to an unknown or invalid profile URL via the `Resource.meta.profile` declaration. Previously this was a warning + and did not block successful validation." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-terminology-delta-defer.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-terminology-delta-defer.yaml new file mode 100644 index 00000000000..bdc0dff48f5 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-terminology-delta-defer.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 1742 +title: When performing a terminology delta ADD operation, if the number of codes being added is large + the codes will be added in small batches via an asynchronous scheduled task in order to avoid overwhelming + the database with a large operation. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java index 6168d8fe5e4..57e8aa333a6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java @@ -102,7 +102,7 @@ public class DaoConfig { /** * update setter javadoc if default changes */ - private int myDeferIndexingForCodesystemsOfSize = 2000; + private int myDeferIndexingForCodesystemsOfSize = 100; private boolean myDeleteStaleSearches = true; private boolean myEnforceReferentialIntegrityOnDelete = true; private boolean myUniqueIndexesEnabled = true; @@ -392,7 +392,7 @@ public class DaoConfig { * the code system will be indexed later in an incremental process in order to * avoid overwhelming Lucene with a huge number of codes in a single operation. *

- * Defaults to 2000 + * Defaults to 100 *

*/ public int getDeferIndexingForCodesystemsOfSize() { @@ -404,7 +404,7 @@ public class DaoConfig { * the code system will be indexed later in an incremental process in order to * avoid overwhelming Lucene with a huge number of codes in a single operation. *

- * Defaults to 2000 + * Defaults to 100 *

*/ public void setDeferIndexingForCodesystemsOfSize(int theDeferIndexingForCodesystemsOfSize) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java index 6f367cefc91..5928bdc1354 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java @@ -50,4 +50,8 @@ public interface ITermConceptDao extends JpaRepository { @Query("SELECT t FROM TermConcept t WHERE t.myIndexStatus = null") Page findResourcesRequiringReindexing(Pageable thePageRequest); + @Modifying + @Query("DELETE FROM TermConcept t WHERE t.myId = :pid") + void deleteByPid(@Param("pid") Long theId); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptParentChildLinkDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptParentChildLinkDao.java index 37ba84a0aba..cf2dc290618 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptParentChildLinkDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptParentChildLinkDao.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -39,4 +40,9 @@ public interface ITermConceptParentChildLinkDao extends JpaRepository findIdsByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid); + + @Modifying + @Query("DELETE FROM TermConceptParentChildLink t WHERE t.myChildPid = :pid OR t.myParentPid = :pid") + void deleteByConceptPid(@Param("pid") Long theId); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java index 73abe091453..ce89811024b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java @@ -47,7 +47,7 @@ public class TermCodeSystem implements Serializable { @Column(name = "CODE_SYSTEM_URI", nullable = false, length = MAX_URL_LENGTH) private String myCodeSystemUri; - @OneToOne() + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CURRENT_VERSION_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_TRMCODESYSTEM_CURVER")) private TermCodeSystemVersion myCurrentVersion; @Column(name = "CURRENT_VERSION_PID", nullable = true, insertable = false, updatable = false) @@ -57,7 +57,7 @@ public class TermCodeSystem implements Serializable { @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CODESYSTEM_PID") @Column(name = "PID") private Long myPid; - @OneToOne() + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_TRMCODESYSTEM_RES")) private ResourceTable myResource; @Column(name = "RES_ID", insertable = false, updatable = false) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java index f92e421888a..c87f76e3d3e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java @@ -50,7 +50,7 @@ public class TermCodeSystemVersion implements Serializable { @Column(name = "PID") private Long myId; - @OneToOne() + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_CODESYSVER_RES_ID")) private ResourceTable myResource; @@ -64,7 +64,7 @@ public class TermCodeSystemVersion implements Serializable { * This was added in HAPI FHIR 3.3.0 and is nullable just to avoid migration * issued. It should be made non-nullable at some point. */ - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_CODESYSVER_CS_ID")) private TermCodeSystem myCodeSystem; @@ -72,7 +72,7 @@ public class TermCodeSystemVersion implements Serializable { private Long myCodeSystemPid; @SuppressWarnings("unused") - @OneToOne(mappedBy = "myCurrentVersion", optional = true) + @OneToOne(mappedBy = "myCurrentVersion", optional = true, fetch = FetchType.LAZY) private TermCodeSystem myCodeSystemHavingThisVersionAsCurrentVersionIfAny; @Column(name = "CS_DISPLAY", nullable = true, updatable = false, length = MAX_VERSION_LENGTH) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index 1b6aa71b777..ab37618e3fa 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -65,7 +65,7 @@ public class TermConcept implements Serializable { @Temporal(TemporalType.TIMESTAMP) @Column(name = "CONCEPT_UPDATED", nullable = true) private Date myUpdated; - @ManyToOne() + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPT_PID_CS_PID")) private TermCodeSystemVersion myCodeSystem; @Column(name = "CODESYSTEM_PID", insertable = false, updatable = false) @@ -79,11 +79,11 @@ public class TermConcept implements Serializable { @Field(name = "myDisplayPhonetic", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompletePhoneticAnalyzer")) }) private String myDisplay; - @OneToMany(mappedBy = "myConcept", orphanRemoval = false) + @OneToMany(mappedBy = "myConcept", orphanRemoval = false, fetch = FetchType.LAZY) @Field(name = "PROPmyProperties", analyzer = @Analyzer(definition = "termConceptPropertyAnalyzer")) @FieldBridge(impl = TermConceptPropertyFieldBridge.class) private Collection myProperties; - @OneToMany(mappedBy = "myConcept", orphanRemoval = false) + @OneToMany(mappedBy = "myConcept", orphanRemoval = false, fetch = FetchType.LAZY) private Collection myDesignations; @Id() @SequenceGenerator(name = "SEQ_CONCEPT_PID", sequenceName = "SEQ_CONCEPT_PID") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java index 58069ed8932..61cfcf80ee2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java @@ -39,7 +39,7 @@ public class TermConceptDesignation implements Serializable { public static final int MAX_LENGTH = 500; public static final int MAX_VAL_LENGTH = 2000; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTDESIG_CONCEPT")) private TermConcept myConcept; @Id() @@ -62,7 +62,7 @@ public class TermConceptDesignation implements Serializable { * * @since 3.5.0 */ - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CS_VER_PID", nullable = true, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTDESIG_CSV")) private TermCodeSystemVersion myCodeSystemVersion; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java index a75511c57ab..2ceceabe931 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java @@ -32,14 +32,14 @@ import java.io.Serializable; public class TermConceptParentChildLink implements Serializable { private static final long serialVersionUID = 1L; - @ManyToOne() + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CHILD_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CHILD")) private TermConcept myChild; @Column(name = "CHILD_PID", insertable = false, updatable = false) private Long myChildPid; - @ManyToOne() + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CODESYSTEM_PID", nullable = false, foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CS")) private TermCodeSystemVersion myCodeSystem; @@ -47,7 +47,7 @@ public class TermConceptParentChildLink implements Serializable { @Fields({@Field(name = "myCodeSystemVersionPid")}) private long myCodeSystemVersionPid; - @ManyToOne(cascade = {}) + @ManyToOne(fetch = FetchType.LAZY, cascade = {}) @JoinColumn(name = "PARENT_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_PARENT")) private TermConcept myParent; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java index 7a59b5d3c3a..8bdbd909b24 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java @@ -44,7 +44,7 @@ public class TermConceptProperty implements Serializable { private static final int MAX_LENGTH = 500; static final int MAX_PROPTYPE_ENUM_LENGTH = 6; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CONCEPT")) private TermConcept myConcept; /** @@ -52,7 +52,7 @@ public class TermConceptProperty implements Serializable { * * @since 3.5.0 */ - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CS_VER_PID", nullable = true, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CSV")) private TermCodeSystemVersion myCodeSystemVersion; @Id() diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 5a1ed416a51..f8175bf481e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -1246,6 +1246,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo return retVal; } + @Transactional @Override public List findCodesAbove(String theSystem, String theCode) { TermCodeSystem cs = getCodeSystem(theSystem); @@ -1277,6 +1278,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo return retVal; } + @Transactional @Override public List findCodesBelow(String theSystem, String theCode) { TermCodeSystem cs = getCodeSystem(theSystem); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 94c5d7cd1a1..da22a1f823b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -223,7 +223,15 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { * save parent concepts first (it's way too slow to do that) */ if (theConcept.getId() == null) { - retVal += ensureParentsSaved(theConcept.getParents()); + boolean needToSaveParents = false; + for (TermConceptParentChildLink next : theConcept.getParents()) { + if (next.getParent().getId() == null) { + needToSaveParents = true; + } + } + if (needToSaveParents) { + retVal += ensureParentsSaved(theConcept.getParents()); + } } if (theConcept.getId() == null || theConcept.getIndexStatus() == null) { @@ -485,7 +493,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { conceptToAdd.setCodeSystemVersion(theCsv); if (theStatisticsTracker.getUpdatedConceptCount() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { - conceptToAdd = myConceptDao.save(conceptToAdd); + saveConcept(conceptToAdd); Long nextConceptPid = conceptToAdd.getId(); Validate.notNull(nextConceptPid); } else { @@ -508,7 +516,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { ourLog.info("Saving parent/child link - Parent[{}] Child[{}]", parentLink.getParent().getCode(), parentLink.getChild().getCode()); if (theStatisticsTracker.getUpdatedConceptCount() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { - myConceptParentChildLinkDao.save(parentLink); + myConceptParentChildLinkDao.save(parentLink); } else { myDeferredStorageSvc.addConceptLinkToStorageQueue(parentLink); } @@ -655,12 +663,15 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { private void deleteConceptChildrenAndConcept(TermConcept theConcept, AtomicInteger theRemoveCounter) { for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) { deleteConceptChildrenAndConcept(nextChildLink.getChild(), theRemoveCounter); - myConceptParentChildLinkDao.delete(nextChildLink); } + myConceptParentChildLinkDao.deleteByConceptPid(theConcept.getId()); + myConceptDesignationDao.deleteAll(theConcept.getDesignations()); myConceptPropertyDao.deleteAll(theConcept.getProperties()); - myConceptDao.delete(theConcept); + + ourLog.info("Deleting concept {} - Code {}", theConcept.getId(), theConcept.getCode()); + myConceptDao.deleteByPid(theConcept.getId()); theRemoveCounter.incrementAndGet(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java index e262ef1cedb..09e7cc7e69b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java @@ -98,6 +98,13 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { myDeferredValueSets.addAll(theValueSets); } + @Override + public void saveAllDeferred() { + while (!isStorageQueueEmpty()) { + saveDeferred(); + } + } + @Override public void setProcessDeferred(boolean theProcessDeferred) { myProcessDeferred = theProcessDeferred; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java index 881ce14e7f6..2aff45f7fef 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java @@ -50,4 +50,9 @@ public interface ITermDeferredStorageSvc { void addConceptMapsToStorageQueue(List theConceptMaps); void addValueSetsToStorageQueue(List theValueSets); + + /** + * This is mostly here for unit tests - Saves any and all deferred concepts and links + */ + void saveAllDeferred(); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index 4ba4f35f181..b85abe96435 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParamModifier; @@ -134,8 +135,13 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION" , cs, table); + + myTermDeferredStorageSvc.saveAllDeferred(); } + @Autowired + private ITermDeferredStorageSvc myTermDeferredStorageSvc; + private void createExternalCsAndLocalVs() { CodeSystem codeSystem = createExternalCs(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java index 7430378c272..55b8c5bf988 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java @@ -15,7 +15,6 @@ import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Observation.ObservationStatus; -import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r5.utils.IResourceValidator; @@ -323,11 +322,15 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test { ValidationModeEnum mode = ValidationModeEnum.CREATE; String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input); - MethodOutcome output = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd); - OperationOutcome oo = (OperationOutcome) output.getOperationOutcome(); - String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); - ourLog.info(outputString); - assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); + try { + myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd); + fail(); + } catch (PreconditionFailedException e) { + OperationOutcome oo = (OperationOutcome) e.getOperationOutcome(); + String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); + ourLog.info(outputString); + assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); + } } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java index 1b72bfc8f7e..1279ab11d4e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java @@ -164,6 +164,8 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { } myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs, table); + + myTerminologyDeferredStorageSvc.saveAllDeferred(); } private void createLocalCsAndVs() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index 2351887144f..992eb5f3f83 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -147,6 +147,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { } myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://loinc.org", codesToAdd); + myTerminologyDeferredStorageSvc.saveAllDeferred(); + // Create a valueset ValueSet vs = new ValueSet(); vs.setUrl("http://example.com/fhir/ValueSet/observation-vitalsignresult"); @@ -395,12 +397,15 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { ValidationModeEnum mode = ValidationModeEnum.CREATE; String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input); - MethodOutcome output = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd); - org.hl7.fhir.r4.model.OperationOutcome oo = (org.hl7.fhir.r4.model.OperationOutcome) output.getOperationOutcome(); - String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); - ourLog.info(outputString); - assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); - + try { + myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd); + fail(); + } catch (PreconditionFailedException e) { + org.hl7.fhir.r4.model.OperationOutcome oo = (org.hl7.fhir.r4.model.OperationOutcome) e.getOperationOutcome(); + String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); + ourLog.info(outputString); + assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); + } } @Test @@ -614,6 +619,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { upload("/r4/uscore/ValueSet-omb-race-category.json"); upload("/r4/uscore/ValueSet-us-core-usps-state.json"); + myTerminologyDeferredStorageSvc.saveAllDeferred(); + { String resource = loadResource("/r4/uscore/patient-resource-badcode.json"); IBaseResource parsedResource = myFhirCtx.newJsonParser().parseResource(resource); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index a8f485ba05d..186a023026c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.client.apache.ResourceEntity; import ca.uhn.fhir.rest.client.api.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; @@ -26,6 +27,9 @@ import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; +import ca.uhn.fhir.validation.ResultSeverityEnum; +import ca.uhn.fhir.validation.SingleValidationMessage; +import ca.uhn.fhir.validation.ValidationResult; import com.google.common.base.Charsets; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; @@ -52,6 +56,7 @@ import org.hl7.fhir.r4.model.Observation.ObservationStatus; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType; import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.junit.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.AopTestUtils; @@ -2260,6 +2265,28 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { } } + + @Test + public void testValidateResourceContainingProfileDeclarationDoesntResolve() throws IOException { + Observation input = new Observation(); + input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + input.getMeta().addProfile("http://foo/structuredefinition/myprofile"); + + input.getCode().setText("Hello"); + input.setStatus(ObservationStatus.FINAL); + + HttpPost post = new HttpPost(ourServerBase + "/Observation/$validate?_pretty=true"); + post.setEntity(new ResourceEntity(myFhirCtx, input)); + + try (CloseableHttpResponse resp = ourHttpClient.execute(post)) { + String respString = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8); + ourLog.info(respString); + assertEquals(412, resp.getStatusLine().getStatusCode()); + assertThat(respString, containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); + } + } + + @SuppressWarnings("unused") @Test public void testFullTextSearch() throws Exception { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java index 074f0d39ad6..5f89fb013a6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java @@ -31,8 +31,10 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import static org.apache.commons.lang3.StringUtils.countMatches; import static org.apache.commons.lang3.StringUtils.leftPad; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.matchesPattern; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; @@ -126,17 +128,23 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { ourLog.info("All concepts: {}", myTermConceptDao.findAll()); }); + myCaptureQueriesListener.clear(); + delta = new CustomTerminologySet(); TermConcept root = delta.addRootConcept("RootA", "Root A"); root.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAA").setDisplay("Child AA"); root.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAB").setDisplay("Child AB"); myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta); + + myCaptureQueriesListener.logAllQueriesForCurrentThread(); + assertHierarchyContains( "RootA seq=0", " ChildAA seq=0", " ChildAB seq=1", "RootB seq=0" ); + } @Test @@ -175,6 +183,12 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { ); assertEquals(2, outcome.getUpdatedConceptCount()); + runInTransaction(() -> { + TermConcept concept = myTermSvc.findCode("http://foo/cs", "ChildAA").orElseThrow(() -> new IllegalStateException()); + assertEquals(2, concept.getParents().size()); + assertThat(concept.getParentPidsAsString(), matchesPattern("^[0-9]+ [0-9]+$")); + }); + } @Test @@ -538,9 +552,15 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { assertEquals(true, runInTransaction(() -> myTermSvc.findCode("http://foo/cs", "codeAAA").isPresent())); // Remove CodeA - delta = new CustomTerminologySet(); - delta.addRootConcept("codeA"); - myTermCodeSystemStorageSvc.applyDeltaCodeSystemsRemove("http://foo/cs", delta); + myCaptureQueriesListener.clear(); + runInTransaction(()->{ + CustomTerminologySet delta2 = new CustomTerminologySet(); + delta2.addRootConcept("codeA"); + myTermCodeSystemStorageSvc.applyDeltaCodeSystemsRemove("http://foo/cs", delta2); + }); + myCaptureQueriesListener.logAllQueriesForCurrentThread(); + + ourLog.info("*** Done removing"); assertEquals(false, runInTransaction(() -> myTermSvc.findCode("http://foo/cs", "codeB").isPresent())); assertEquals(false, runInTransaction(() -> myTermSvc.findCode("http://foo/cs", "codeA").isPresent())); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index 3f2e784734a..7c3f2efaf78 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -100,6 +100,8 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs, table); + myTerminologyDeferredStorageSvc.saveAllDeferred(); + return id; } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java index 37323be9e1c..717279b54c1 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java @@ -171,11 +171,18 @@ public class ValidatorWrapper { for (int i = 0; i < messages.size(); i++) { ValidationMessage next = messages.get(i); String message = next.getMessage(); + + // TODO: are these still needed? if ("Binding has no source, so can't be checked".equals(message) || "ValueSet http://hl7.org/fhir/ValueSet/mimetypes not found".equals(message)) { messages.remove(i); i--; } + + if (message.endsWith("' could not be resolved, so has not been checked") && next.getLevel() == ValidationMessage.IssueSeverity.WARNING) { + next.setLevel(ValidationMessage.IssueSeverity.ERROR); + } + } return messages; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index f2322d13bc9..d666335bed8 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -1067,7 +1067,10 @@ public class FhirInstanceValidatorR4Test extends BaseTest { myInstanceVal.setValidationSupport(myMockSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); + + assertEquals(1, errors.size()); + assertEquals("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked", errors.get(0).getMessage()); + assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); } @Test From 58b5bd96645671571366f0ad2284aab8ca87330e Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 3 Mar 2020 08:28:38 -0500 Subject: [PATCH 30/30] Limit type on _has query (#1743) * Limit type on _has query * Add changelog --- .../4_3_0/1742-limit-type-on-has.yaml | 6 ++++ .../predicate/PredicateBuilderReference.java | 9 ++--- .../ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java | 3 ++ .../r4/FhirResourceDaoR4SearchNoFtTest.java | 35 +++++++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-limit-type-on-has.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-limit-type-on-has.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-limit-type-on-has.yaml new file mode 100644 index 00000000000..4397032c2aa --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1742-limit-type-on-has.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 1742 +title: "When performing a search in the JPA server where the only parameter was a `_has` parameter, + the server did not respect the resource typename being searched for, causing false positive + search results. This has been corrected." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java index 6077495a1c2..ca72f0a6f56 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java @@ -430,7 +430,7 @@ class PredicateBuilderReference extends BasePredicateBuilder { break; case Constants.PARAM_HAS: - addPredicateHas(theAndOrParams, theRequest); + addPredicateHas(theResourceName, theAndOrParams, theRequest); break; case Constants.PARAM_TAG: @@ -756,7 +756,7 @@ class PredicateBuilderReference extends BasePredicateBuilder { return retVal; } - private void addPredicateHas(List> theHasParameters, RequestDetails theRequest) { + private void addPredicateHas(String theResourceType, List> theHasParameters, RequestDetails theRequest) { for (List nextOrList : theHasParameters) { @@ -811,8 +811,9 @@ class PredicateBuilderReference extends BasePredicateBuilder { Join join = myQueryRoot.join("myResourceLinksAsTarget", JoinType.LEFT); Predicate pathPredicate = myPredicateBuilder.createResourceLinkPathPredicate(targetResourceType, paramReference, join); - Predicate pidPredicate = join.get("mySourceResourcePid").in(subQ); - Predicate andPredicate = myBuilder.and(pathPredicate, pidPredicate); + Predicate sourceTypePredicate = myBuilder.equal(join.get("myTargetResourceType"), theResourceType); + Predicate sourcePidPredicate = join.get("mySourceResourcePid").in(subQ); + Predicate andPredicate = myBuilder.and(pathPredicate, sourcePidPredicate, sourceTypePredicate); myQueryRoot.addPredicate(andPredicate); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index 91a5702e629..26f5bd9b4da 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -172,6 +172,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { @Qualifier("myDeviceDaoR4") protected IFhirResourceDao myDeviceDao; @Autowired + @Qualifier("myProvenanceDaoR4") + protected IFhirResourceDao myProvenanceDao; + @Autowired @Qualifier("myDiagnosticReportDaoR4") protected IFhirResourceDao myDiagnosticReportDao; @Autowired diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 54973d1d502..86822895a5f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -620,6 +620,41 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { } + @Test + public void testHasLimitsByType() { + + Patient patient = new Patient(); + patient.setActive(true); + IIdType patientId = myPatientDao.create(patient).getId().toUnqualifiedVersionless(); + + Encounter encounter = new Encounter(); + encounter.setStatus(Encounter.EncounterStatus.ARRIVED); + IIdType encounterId = myEncounterDao.create(encounter).getId().toUnqualifiedVersionless(); + + Device device = new Device(); + device.setManufacturer("Acme"); + IIdType deviceId = myDeviceDao.create(device).getId().toUnqualifiedVersionless(); + + Provenance provenance = new Provenance(); + provenance.addTarget().setReferenceElement(patientId); + provenance.addTarget().setReferenceElement(encounterId); + provenance.addAgent().setWho(new Reference(deviceId)); + myProvenanceDao.create(provenance); + + String criteria = "_has:Provenance:target:agent=" + deviceId.getValue(); + SearchParameterMap map = myMatchUrlService.translateMatchUrl(criteria, myFhirCtx.getResourceDefinition(Encounter.class)); + + map.setLoadSynchronous(true); + + myCaptureQueriesListener.clear(); + IBundleProvider results = myEncounterDao.search(map); + myCaptureQueriesListener.logSelectQueriesForCurrentThread(0); + + List ids = toUnqualifiedVersionlessIdValues(results); + assertThat(ids, containsInAnyOrder(encounterId.getValue())); + + } + @Test public void testHasParameter() { IIdType pid0;