Error on missing profile validation (#1742)
* Error on missing profile validation * Add changelog * Test fixes
This commit is contained in:
parent
ac9a371f18
commit
3c2c1dab2e
|
@ -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."
|
|
@ -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.
|
|
@ -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.
|
||||
* <p>
|
||||
* Defaults to 2000
|
||||
* Defaults to 100
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* Defaults to 2000
|
||||
* Defaults to 100
|
||||
* </p>
|
||||
*/
|
||||
public void setDeferIndexingForCodesystemsOfSize(int theDeferIndexingForCodesystemsOfSize) {
|
||||
|
|
|
@ -50,4 +50,8 @@ public interface ITermConceptDao extends JpaRepository<TermConcept, Long> {
|
|||
@Query("SELECT t FROM TermConcept t WHERE t.myIndexStatus = null")
|
||||
Page<TermConcept> findResourcesRequiringReindexing(Pageable thePageRequest);
|
||||
|
||||
@Modifying
|
||||
@Query("DELETE FROM TermConcept t WHERE t.myId = :pid")
|
||||
void deleteByPid(@Param("pid") Long theId);
|
||||
|
||||
}
|
||||
|
|
|
@ -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<TermConcep
|
|||
|
||||
@Query("SELECT t.myPid FROM TermConceptParentChildLink t WHERE t.myCodeSystem.myId = :cs_pid")
|
||||
Slice<Long> 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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<TermConceptProperty> myProperties;
|
||||
@OneToMany(mappedBy = "myConcept", orphanRemoval = false)
|
||||
@OneToMany(mappedBy = "myConcept", orphanRemoval = false, fetch = FetchType.LAZY)
|
||||
private Collection<TermConceptDesignation> myDesignations;
|
||||
@Id()
|
||||
@SequenceGenerator(name = "SEQ_CONCEPT_PID", sequenceName = "SEQ_CONCEPT_PID")
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -1246,6 +1246,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public List<VersionIndependentConcept> 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<VersionIndependentConcept> findCodesBelow(String theSystem, String theCode) {
|
||||
TermCodeSystem cs = getCodeSystem(theSystem);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -50,4 +50,9 @@ public interface ITermDeferredStorageSvc {
|
|||
void addConceptMapsToStorageQueue(List<ConceptMap> theConceptMaps);
|
||||
|
||||
void addValueSetsToStorageQueue(List<ValueSet> theValueSets);
|
||||
|
||||
/**
|
||||
* This is mostly here for unit tests - Saves any and all deferred concepts and links
|
||||
*/
|
||||
void saveAllDeferred();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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("<div>AA</div>")).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 {
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1067,7 +1067,10 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
|||
myInstanceVal.setValidationSupport(myMockSupport);
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
List<SingleValidationMessage> 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
|
||||
|
|
Loading…
Reference in New Issue