Improve TX scoping for validation (#1925)
* Improve TX scoping for validation * Test fix * Test fix * Some cleanup * Test fixes * Test fix
This commit is contained in:
parent
0d4e12fe58
commit
b6540064ea
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>5.1.0-SNAPSHOT</version>
|
<version>5.1.0-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-elasticsearch-6</artifactId>
|
<artifactId>hapi-fhir-elasticsearch-6</artifactId>
|
||||||
|
|
|
@ -150,6 +150,12 @@
|
||||||
<artifactId>hapi-fhir-elasticsearch-6</artifactId>
|
<artifactId>hapi-fhir-elasticsearch-6</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<classifier>shaded6</classifier>
|
<classifier>shaded6</classifier>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1395,6 +1395,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequest) {
|
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequest) {
|
||||||
if (theRequest != null) {
|
if (theRequest != null) {
|
||||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, theResource, null, theId);
|
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, theResource, null, theId);
|
||||||
|
|
|
@ -412,8 +412,4 @@ public class TermConcept implements Serializable {
|
||||||
return getChildren().stream().map(t -> t.getChild()).collect(Collectors.toList());
|
return getChildren().stream().map(t -> t.getChild()).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public VersionIndependentConcept toVersionIndependentConcept() {
|
|
||||||
return new VersionIndependentConcept(myCodeSystem.getCodeSystem().getCodeSystemUri(), myCode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -408,7 +408,7 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
|
||||||
.build()
|
.build()
|
||||||
.execute(new HttpGet(thePackageUrl))) {
|
.execute(new HttpGet(thePackageUrl))) {
|
||||||
if (request.getStatusLine().getStatusCode() != 200) {
|
if (request.getStatusLine().getStatusCode() != 200) {
|
||||||
throw new IOException("Received HTTP " + request.getStatusLine().getStatusCode());
|
throw new ResourceNotFoundException("Received HTTP " + request.getStatusLine().getStatusCode() + " from URL: " + thePackageUrl);
|
||||||
}
|
}
|
||||||
return IOUtils.toByteArray(request.getEntity().getContent());
|
return IOUtils.toByteArray(request.getEntity().getContent());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
|
@ -89,6 +89,7 @@ import com.github.benmanes.caffeine.cache.Cache;
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Stopwatch;
|
import com.google.common.base.Stopwatch;
|
||||||
|
import net.bytebuddy.implementation.bytecode.Throw;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
@ -177,6 +178,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
||||||
private static final TermCodeSystemVersion NO_CURRENT_VERSION = new TermCodeSystemVersion().setId(-1L);
|
private static final TermCodeSystemVersion NO_CURRENT_VERSION = new TermCodeSystemVersion().setId(-1L);
|
||||||
private static boolean ourLastResultsFromTranslationCache; // For testing.
|
private static boolean ourLastResultsFromTranslationCache; // For testing.
|
||||||
private static boolean ourLastResultsFromTranslationWithReverseCache; // For testing.
|
private static boolean ourLastResultsFromTranslationWithReverseCache; // For testing.
|
||||||
|
private static Runnable myInvokeOnNextCallForUnitTest;
|
||||||
private final int myFetchSize = DEFAULT_FETCH_SIZE;
|
private final int myFetchSize = DEFAULT_FETCH_SIZE;
|
||||||
private final Cache<String, TermCodeSystemVersion> myCodeSystemCurrentVersionCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
|
private final Cache<String, TermCodeSystemVersion> myCodeSystemCurrentVersionCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1299,7 +1301,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri) {
|
private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri) {
|
||||||
TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(theUri, uri -> {
|
TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(theUri, uri -> myTxTemplate.execute(tx -> {
|
||||||
TermCodeSystemVersion csv = null;
|
TermCodeSystemVersion csv = null;
|
||||||
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(uri);
|
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(uri);
|
||||||
if (cs != null && cs.getCurrentVersion() != null) {
|
if (cs != null && cs.getCurrentVersion() != null) {
|
||||||
|
@ -1310,7 +1312,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
||||||
} else {
|
} else {
|
||||||
return NO_CURRENT_VERSION;
|
return NO_CURRENT_VERSION;
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
if (retVal == NO_CURRENT_VERSION) {
|
if (retVal == NO_CURRENT_VERSION) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1935,8 +1937,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||||
|
|
||||||
|
if (myInvokeOnNextCallForUnitTest != null) {
|
||||||
|
Runnable invokeOnNextCallForUnitTest = myInvokeOnNextCallForUnitTest;
|
||||||
|
myInvokeOnNextCallForUnitTest = null;
|
||||||
|
invokeOnNextCallForUnitTest.run();
|
||||||
|
}
|
||||||
|
|
||||||
IPrimitiveType<?> urlPrimitive = myContext.newTerser().getSingleValueOrNull(theValueSet, "url", IPrimitiveType.class);
|
IPrimitiveType<?> urlPrimitive = myContext.newTerser().getSingleValueOrNull(theValueSet, "url", IPrimitiveType.class);
|
||||||
String url = urlPrimitive.getValueAsString();
|
String url = urlPrimitive.getValueAsString();
|
||||||
if (isNotBlank(url)) {
|
if (isNotBlank(url)) {
|
||||||
|
@ -2106,6 +2115,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static void setInvokeOnNextCallForUnitTest(Runnable theInvokeOnNextCallForUnitTest) {
|
||||||
|
myInvokeOnNextCallForUnitTest = theInvokeOnNextCallForUnitTest;
|
||||||
|
}
|
||||||
|
|
||||||
static List<TermConcept> toPersistedConcepts(List<CodeSystem.ConceptDefinitionComponent> theConcept, TermCodeSystemVersion theCodeSystemVersion) {
|
static List<TermConcept> toPersistedConcepts(List<CodeSystem.ConceptDefinitionComponent> theConcept, TermCodeSystemVersion theCodeSystemVersion) {
|
||||||
ArrayList<TermConcept> retVal = new ArrayList<>();
|
ArrayList<TermConcept> retVal = new ArrayList<>();
|
||||||
|
|
||||||
|
|
|
@ -447,6 +447,8 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
||||||
doDelete(descriptor, loader, counter, myConceptDao);
|
doDelete(descriptor, loader, counter, myConceptDao);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||||
|
txTemplate.executeWithoutResult(tx -> {
|
||||||
Optional<TermCodeSystem> codeSystemOpt = myCodeSystemDao.findWithCodeSystemVersionAsCurrentVersion(theCodeSystemVersionPid);
|
Optional<TermCodeSystem> codeSystemOpt = myCodeSystemDao.findWithCodeSystemVersionAsCurrentVersion(theCodeSystemVersionPid);
|
||||||
if (codeSystemOpt.isPresent()) {
|
if (codeSystemOpt.isPresent()) {
|
||||||
TermCodeSystem codeSystem = codeSystemOpt.get();
|
TermCodeSystem codeSystem = codeSystemOpt.get();
|
||||||
|
@ -457,6 +459,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
||||||
|
|
||||||
ourLog.info(" * Deleting code system version");
|
ourLog.info(" * Deleting code system version");
|
||||||
myCodeSystemVersionDao.deleteById(theCodeSystemVersionPid);
|
myCodeSystemVersionDao.deleteById(theCodeSystemVersionPid);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation
|
||||||
if (!haveValidated) {
|
if (!haveValidated) {
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||||
codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> c.toVersionIndependentConcept()));
|
codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> new VersionIndependentConcept(theCodeSystem, c.getCode())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codeOpt != null && codeOpt.isPresent()) {
|
if (codeOpt != null && codeOpt.isPresent()) {
|
||||||
|
|
|
@ -112,7 +112,7 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4
|
||||||
if (!haveValidated) {
|
if (!haveValidated) {
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||||
codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> c.toVersionIndependentConcept()));
|
codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> new VersionIndependentConcept(theCodeSystem, c.getCode())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codeOpt != null && codeOpt.isPresent()) {
|
if (codeOpt != null && codeOpt.isPresent()) {
|
||||||
|
|
|
@ -99,7 +99,7 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup
|
||||||
if (!haveValidated) {
|
if (!haveValidated) {
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||||
codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> c.toVersionIndependentConcept()));
|
codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> new VersionIndependentConcept(theCodeSystem, c.getCode())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codeOpt != null && codeOpt.isPresent()) {
|
if (codeOpt != null && codeOpt.isPresent()) {
|
||||||
|
|
|
@ -41,6 +41,11 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
|
||||||
* starvation
|
* starvation
|
||||||
*/
|
*/
|
||||||
ourMaxThreads = (int) (Math.random() * 6.0) + 1;
|
ourMaxThreads = (int) (Math.random() * 6.0) + 1;
|
||||||
|
|
||||||
|
if ("true".equals(System.getProperty("single_db_connection"))) {
|
||||||
|
ourMaxThreads = 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Exception myLastStackTrace;
|
private Exception myLastStackTrace;
|
||||||
|
|
|
@ -94,6 +94,11 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
* starvation
|
* starvation
|
||||||
*/
|
*/
|
||||||
int maxThreads = (int) (Math.random() * 6.0) + 1;
|
int maxThreads = (int) (Math.random() * 6.0) + 1;
|
||||||
|
|
||||||
|
if ("true".equals(System.getProperty("single_db_connection"))) {
|
||||||
|
maxThreads = 1;
|
||||||
|
}
|
||||||
|
|
||||||
retVal.setMaxTotal(maxThreads);
|
retVal.setMaxTotal(maxThreads);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
|
@ -43,6 +43,10 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
||||||
*/
|
*/
|
||||||
if (ourMaxThreads == null) {
|
if (ourMaxThreads == null) {
|
||||||
ourMaxThreads = (int) (Math.random() * 6.0) + 1;
|
ourMaxThreads = (int) (Math.random() * 6.0) + 1;
|
||||||
|
|
||||||
|
if ("true".equals(System.getProperty("single_db_connection"))) {
|
||||||
|
ourMaxThreads = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,11 @@ public class TestR5Config extends BaseJavaConfigR5 {
|
||||||
*/
|
*/
|
||||||
if (ourMaxThreads == null) {
|
if (ourMaxThreads == null) {
|
||||||
ourMaxThreads = (int) (Math.random() * 6.0) + 1;
|
ourMaxThreads = (int) (Math.random() * 6.0) + 1;
|
||||||
|
|
||||||
|
if ("true".equals(System.getProperty("single_db_connection"))) {
|
||||||
|
ourMaxThreads = 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
import org.hibernate.jdbc.Work;
|
import org.hibernate.jdbc.Work;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||||
import org.hl7.fhir.dstu3.model.Resource;
|
import org.hl7.fhir.dstu3.model.Resource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
@ -133,7 +134,8 @@ public abstract class BaseJpaTest extends BaseTest {
|
||||||
@Qualifier(BaseConfig.JPA_VALIDATION_SUPPORT)
|
@Qualifier(BaseConfig.JPA_VALIDATION_SUPPORT)
|
||||||
@Autowired
|
@Autowired
|
||||||
private IValidationSupport myJpaPersistedValidationSupport;
|
private IValidationSupport myJpaPersistedValidationSupport;
|
||||||
|
@Autowired
|
||||||
|
private FhirInstanceValidator myFhirInstanceValidator;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterPerformCleanup() {
|
public void afterPerformCleanup() {
|
||||||
|
@ -150,7 +152,9 @@ public abstract class BaseJpaTest extends BaseTest {
|
||||||
if (myJpaPersistedValidationSupport != null) {
|
if (myJpaPersistedValidationSupport != null) {
|
||||||
ProxyUtil.getSingletonTarget(myJpaPersistedValidationSupport, JpaPersistedResourceValidationSupport.class).clearCaches();
|
ProxyUtil.getSingletonTarget(myJpaPersistedValidationSupport, JpaPersistedResourceValidationSupport.class).clearCaches();
|
||||||
}
|
}
|
||||||
|
if (myFhirInstanceValidator != null) {
|
||||||
|
myFhirInstanceValidator.invalidateCaches();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
||||||
// Validate once
|
// Validate once
|
||||||
myCaptureQueriesListener.clear();
|
myCaptureQueriesListener.clear();
|
||||||
myObservationDao.validate(obs, null, null, null, null, null, null);
|
myObservationDao.validate(obs, null, null, null, null, null, null);
|
||||||
assertEquals(myCaptureQueriesListener.logSelectQueriesForCurrentThread(), 10, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
assertEquals(myCaptureQueriesListener.logSelectQueriesForCurrentThread(), 9, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||||
assertEquals(myCaptureQueriesListener.logUpdateQueriesForCurrentThread(), 0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size());
|
assertEquals(myCaptureQueriesListener.logUpdateQueriesForCurrentThread(), 0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size());
|
||||||
assertEquals(myCaptureQueriesListener.logInsertQueriesForCurrentThread(), 0, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size());
|
assertEquals(myCaptureQueriesListener.logInsertQueriesForCurrentThread(), 0, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size());
|
||||||
assertEquals(myCaptureQueriesListener.logDeleteQueriesForCurrentThread(), 0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size());
|
assertEquals(myCaptureQueriesListener.logDeleteQueriesForCurrentThread(), 0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size());
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||||
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
|
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
|
||||||
|
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||||
|
@ -23,9 +27,27 @@ import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.AllergyIntolerance;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
||||||
|
import org.hl7.fhir.r4.model.CanonicalType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.Condition;
|
||||||
|
import org.hl7.fhir.r4.model.DateTimeType;
|
||||||
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
import org.hl7.fhir.r4.model.Narrative;
|
||||||
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
|
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
|
||||||
|
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||||
|
import org.hl7.fhir.r4.model.Organization;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.hl7.fhir.r4.model.Questionnaire;
|
||||||
|
import org.hl7.fhir.r4.model.QuestionnaireResponse;
|
||||||
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||||
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
|
@ -33,6 +55,7 @@ import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.util.AopTestUtils;
|
import org.springframework.test.util.AopTestUtils;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -45,6 +68,9 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ValidateTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ValidateTest.class);
|
||||||
|
@ -56,6 +82,10 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
private ITermCodeSystemStorageSvc myTermCodeSystemStorageSvcc;
|
private ITermCodeSystemStorageSvc myTermCodeSystemStorageSvcc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoRegistry myDaoRegistry;
|
private DaoRegistry myDaoRegistry;
|
||||||
|
@Autowired
|
||||||
|
private JpaValidationSupportChain myJpaValidationSupportChain;
|
||||||
|
@Autowired
|
||||||
|
private PlatformTransactionManager myTransactionManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a loinc valueset that expands to more results than the expander is willing to do
|
* Create a loinc valueset that expands to more results than the expander is willing to do
|
||||||
|
@ -155,13 +185,11 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per: https://chat.fhir.org/#narrow/stream/179166-implementers/topic/Handling.20incomplete.20CodeSystems
|
* Per: https://chat.fhir.org/#narrow/stream/179166-implementers/topic/Handling.20incomplete.20CodeSystems
|
||||||
*
|
* <p>
|
||||||
* We should generate a warning if a code can't be found but the codesystem is a fragment
|
* We should generate a warning if a code can't be found but the codesystem is a fragment
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@ -219,7 +247,6 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a loinc valueset that expands to more results than the expander is willing to do
|
* Create a loinc valueset that expands to more results than the expander is willing to do
|
||||||
* in memory, and make sure we can still validate correctly, even if we're using
|
* in memory, and make sure we can still validate correctly, even if we're using
|
||||||
|
@ -303,6 +330,115 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure that we do something sane when validating throws an unexpected exception
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testValidate_ValidationSupportThrowsException() {
|
||||||
|
IValidationSupport validationSupport = mock(IValidationSupport.class);
|
||||||
|
when(validationSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenAnswer(t -> {
|
||||||
|
// This will fail with a constraint error
|
||||||
|
try {
|
||||||
|
myResourceTableDao.save(new ResourceTable());
|
||||||
|
myResourceTableDao.flush();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ourLog.info("Hit expected exception: {}", e.toString());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
when(validationSupport.getFhirContext()).thenReturn(myFhirCtx);
|
||||||
|
|
||||||
|
myJpaValidationSupportChain.addValidationSupport(0, validationSupport);
|
||||||
|
try {
|
||||||
|
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs.getText().setDivAsString("<div>Hello</div>");
|
||||||
|
obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs");
|
||||||
|
obs.setSubject(new Reference("Patient/123"));
|
||||||
|
obs.addPerformer(new Reference("Practitioner/123"));
|
||||||
|
obs.setEffective(DateTimeType.now());
|
||||||
|
obs.setStatus(ObservationStatus.FINAL);
|
||||||
|
obs.setValue(new StringType("This is the value"));
|
||||||
|
|
||||||
|
OperationOutcome oo;
|
||||||
|
|
||||||
|
// Valid code
|
||||||
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
|
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3");
|
||||||
|
oo = validateAndReturnOutcome(obs);
|
||||||
|
assertEquals(encode(oo), "No issues detected during validation", oo.getIssueFirstRep().getDiagnostics());
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
myJpaValidationSupportChain.removeValidationSupport(validationSupport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure that we do something sane when validating throws an unexpected exception
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testValidate_TermSvcHasDatabaseRollback() {
|
||||||
|
BaseTermReadSvcImpl.setInvokeOnNextCallForUnitTest(() -> {
|
||||||
|
try {
|
||||||
|
myResourceTableDao.save(new ResourceTable());
|
||||||
|
myResourceTableDao.flush();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ourLog.info("Hit expected exception: {}", e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs.getText().setDivAsString("<div>Hello</div>");
|
||||||
|
obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs");
|
||||||
|
obs.setSubject(new Reference("Patient/123"));
|
||||||
|
obs.addPerformer(new Reference("Practitioner/123"));
|
||||||
|
obs.setEffective(DateTimeType.now());
|
||||||
|
obs.setStatus(ObservationStatus.FINAL);
|
||||||
|
obs.setValue(new StringType("This is the value"));
|
||||||
|
|
||||||
|
OperationOutcome oo;
|
||||||
|
|
||||||
|
// Valid code
|
||||||
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
|
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3");
|
||||||
|
oo = validateAndReturnOutcome(obs);
|
||||||
|
assertEquals(encode(oo), "No issues detected during validation", oo.getIssueFirstRep().getDiagnostics());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure that we do something sane when validating throws an unexpected exception
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testValidate_TermSvcHasNpe() {
|
||||||
|
BaseTermReadSvcImpl.setInvokeOnNextCallForUnitTest(() -> {
|
||||||
|
throw new NullPointerException("MY ERROR");
|
||||||
|
});
|
||||||
|
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs.getText().setDivAsString("<div>Hello</div>");
|
||||||
|
obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs");
|
||||||
|
obs.setSubject(new Reference("Patient/123"));
|
||||||
|
obs.addPerformer(new Reference("Practitioner/123"));
|
||||||
|
obs.setEffective(DateTimeType.now());
|
||||||
|
obs.setStatus(ObservationStatus.FINAL);
|
||||||
|
obs.setValue(new StringType("This is the value"));
|
||||||
|
|
||||||
|
OperationOutcome oo;
|
||||||
|
|
||||||
|
// Valid code
|
||||||
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
|
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE99999").setDisplay("Display 3");
|
||||||
|
try {
|
||||||
|
validateAndReturnOutcome(obs);
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("MY ERROR", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCodeableConceptWithNoSystem() {
|
public void testValidateCodeableConceptWithNoSystem() {
|
||||||
AllergyIntolerance allergy = new AllergyIntolerance();
|
AllergyIntolerance allergy = new AllergyIntolerance();
|
||||||
|
@ -605,6 +741,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||||
myDaoConfig.setMaximumExpansionSize(DaoConfig.DEFAULT_MAX_EXPANSION_SIZE);
|
myDaoConfig.setMaximumExpansionSize(DaoConfig.DEFAULT_MAX_EXPANSION_SIZE);
|
||||||
myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets());
|
myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets());
|
||||||
|
|
||||||
|
BaseTermReadSvcImpl.setInvokeOnNextCallForUnitTest(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||||
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
import ca.uhn.fhir.test.utilities.ProxyUtil;
|
import ca.uhn.fhir.test.utilities.ProxyUtil;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
|
@ -30,9 +31,9 @@ import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class IgInstallerTestDstu3 extends BaseJpaDstu3Test {
|
public class IgInstallerDstu3Test extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(IgInstallerTestDstu3.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(IgInstallerDstu3Test.class);
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoConfig daoConfig;
|
private DaoConfig daoConfig;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -185,7 +186,7 @@ public class IgInstallerTestDstu3 extends BaseJpaDstu3Test {
|
||||||
);
|
);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (InvalidRequestException e) {
|
||||||
assertEquals("", e.getMessage());
|
assertEquals("Package ID nictiz.fhir.nl.stu3.questionnaires doesn't match expected: blah", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -199,8 +200,8 @@ public class IgInstallerTestDstu3 extends BaseJpaDstu3Test {
|
||||||
.setPackageUrl("http://localhost:" + myPort + "/foo.tgz")
|
.setPackageUrl("http://localhost:" + myPort + "/foo.tgz")
|
||||||
);
|
);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (ResourceNotFoundException e) {
|
||||||
assertEquals("", e.getMessage());
|
assertEquals("Received HTTP 404 from URL: http://localhost:" + myPort + "/foo.tgz", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -19,7 +19,7 @@ public class JpaPackageCacheTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSavePackage() throws IOException {
|
public void testSavePackage() throws IOException {
|
||||||
try (InputStream stream = IgInstallerTestDstu3.class.getResourceAsStream("/packages/basisprofil.de.tar.gz")) {
|
try (InputStream stream = IgInstallerDstu3Test.class.getResourceAsStream("/packages/basisprofil.de.tar.gz")) {
|
||||||
myPackageCacheManager.addPackageToCache("basisprofil.de", "0.2.40", stream, "basisprofil.de");
|
myPackageCacheManager.addPackageToCache("basisprofil.de", "0.2.40", stream, "basisprofil.de");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package ca.uhn.fhir.jpa.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class JpaClasspathTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure no dependencies start bringing in log4j - This makes hibernate decide to start using log4j instead of
|
||||||
|
* slf4j which is super annoying..
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testNoLog4jOnClasspath() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class.forName("org.apache.logging.log4j.status.StatusLogger");
|
||||||
|
fail("org.apache.logging.log4j.status.StatusLogger" + " found on classpath - Make sure log4j isn't being introduced");
|
||||||
|
} catch (ClassNotFoundException theE) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -122,6 +122,14 @@ public class ValidationSupportChain implements IValidationSupport {
|
||||||
myChain.add(theIndex, theValidationSupport);
|
myChain.add(theIndex, theValidationSupport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item from the chain. Note that this method is mostly intended for testing. Removing items from the chain while validation is
|
||||||
|
* actually occurring is not an expected use case for this class.
|
||||||
|
*/
|
||||||
|
public void removeValidationSupport(IValidationSupport theValidationSupport) {
|
||||||
|
myChain.remove(theValidationSupport);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand) {
|
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand) {
|
||||||
for (IValidationSupport next : myChain) {
|
for (IValidationSupport next : myChain) {
|
||||||
|
|
|
@ -309,8 +309,10 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta
|
||||||
*/
|
*/
|
||||||
public void invalidateCaches() {
|
public void invalidateCaches() {
|
||||||
myValidationSupport.invalidateCaches();
|
myValidationSupport.invalidateCaches();
|
||||||
|
if (myWrappedWorkerContext != null) {
|
||||||
myWrappedWorkerContext.invalidateCaches();
|
myWrappedWorkerContext.invalidateCaches();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class NullEvaluationContext implements FHIRPathEngine.IEvaluationContext {
|
public static class NullEvaluationContext implements FHIRPathEngine.IEvaluationContext {
|
||||||
|
|
Loading…
Reference in New Issue