Make sure the validator can get at codes defined in notpresent
codesystems
This commit is contained in:
parent
94f87e7282
commit
cc0df9850c
|
@ -108,9 +108,13 @@ public class OperationOutcomeUtil {
|
||||||
if (theOutcome == null) {
|
if (theOutcome == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return getIssueCount(theCtx, theOutcome) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getIssueCount(FhirContext theCtx, IBaseOperationOutcome theOutcome) {
|
||||||
RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome);
|
RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome);
|
||||||
BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue");
|
BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue");
|
||||||
return issueChild.getAccessor().getValues(theOutcome).size() > 0;
|
return issueChild.getAccessor().getValues(theOutcome).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IBaseOperationOutcome newInstance(FhirContext theCtx) {
|
public static IBaseOperationOutcome newInstance(FhirContext theCtx) {
|
||||||
|
@ -152,5 +156,4 @@ public class OperationOutcomeUtil {
|
||||||
locationChild.getMutator().addValue(theIssue, locationElem);
|
locationChild.getMutator().addValue(theIssue, locationElem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,4 +173,11 @@ public class TermCodeSystemVersion implements Serializable {
|
||||||
"Version ID exceeds maximum length (" + MAX_VERSION_LENGTH + "): " + length(theCodeSystemDisplayName));
|
"Version ID exceeds maximum length (" + MAX_VERSION_LENGTH + "): " + length(theCodeSystemDisplayName));
|
||||||
myCodeSystemDisplayName = theCodeSystemDisplayName;
|
myCodeSystemDisplayName = theCodeSystemDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TermConcept addConcept() {
|
||||||
|
TermConcept concept = new TermConcept();
|
||||||
|
concept.setCodeSystemVersion(this);
|
||||||
|
getConcepts().add(concept);
|
||||||
|
return concept;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,6 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
|
||||||
import ca.uhn.fhir.rest.param.UriParam;
|
|
||||||
import ca.uhn.fhir.util.CoverageIgnore;
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
|
@ -174,13 +170,6 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
|
||||||
@CoverageIgnore
|
@CoverageIgnore
|
||||||
@Override
|
@Override
|
||||||
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
|
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||||
SearchParameterMap map = new SearchParameterMap();
|
|
||||||
map.add(CodeSystem.SP_URL, new UriParam(theSystem));
|
|
||||||
map.setLoadSynchronousUpTo(1);
|
|
||||||
IBundleProvider outcome = myCodeSystemResourceDao.search(map);
|
|
||||||
if (outcome.size() > 0) {
|
|
||||||
return (CodeSystem) outcome.getResources(0, 1).get(0);
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,6 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
|
||||||
import ca.uhn.fhir.rest.param.UriParam;
|
|
||||||
import ca.uhn.fhir.util.CoverageIgnore;
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import ca.uhn.fhir.util.ValidateUtil;
|
import ca.uhn.fhir.util.ValidateUtil;
|
||||||
|
@ -185,13 +181,6 @@ public class HapiTerminologySvcR5 extends BaseHapiTerminologySvcImpl implements
|
||||||
@CoverageIgnore
|
@CoverageIgnore
|
||||||
@Override
|
@Override
|
||||||
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
|
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||||
SearchParameterMap map = new SearchParameterMap();
|
|
||||||
map.add(org.hl7.fhir.r5.model.CodeSystem.SP_URL, new UriParam(theSystem));
|
|
||||||
map.setLoadSynchronousUpTo(1);
|
|
||||||
IBundleProvider outcome = myCodeSystemResourceDao.search(map);
|
|
||||||
if (outcome.size() > 0) {
|
|
||||||
return (org.hl7.fhir.r5.model.CodeSystem) outcome.getResources(0, 1).get(0);
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +270,7 @@ public class HapiTerminologySvcR5 extends BaseHapiTerminologySvcImpl implements
|
||||||
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||||
return txTemplate.execute(t-> {
|
return txTemplate.execute(t -> {
|
||||||
Optional<TermConcept> codeOpt = findCode(theCodeSystem, theCode);
|
Optional<TermConcept> codeOpt = findCode(theCodeSystem, theCode);
|
||||||
if (codeOpt.isPresent()) {
|
if (codeOpt.isPresent()) {
|
||||||
TermConcept code = codeOpt.get();
|
TermConcept code = codeOpt.get();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||||
|
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||||
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;
|
||||||
|
@ -8,10 +10,12 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
|
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import ca.uhn.fhir.validation.IValidatorModule;
|
import ca.uhn.fhir.validation.IValidatorModule;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
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.hapi.validation.FhirInstanceValidator;
|
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
|
||||||
|
@ -28,16 +32,18 @@ import org.springframework.test.util.AopTestUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
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);
|
||||||
@Autowired
|
@Autowired
|
||||||
private IValidatorModule myValidatorModule;
|
private IValidatorModule myValidatorModule;
|
||||||
|
@Autowired
|
||||||
|
private IHapiTerminologySvc myTerminologySvc;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateStructureDefinition() throws Exception {
|
public void testValidateStructureDefinition() throws Exception {
|
||||||
|
@ -106,6 +112,43 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
assertThat(ooString, containsString("Element '/f:Observation.device': minimum required = 1, but only found 0"));
|
assertThat(ooString, containsString("Element '/f:Observation.device': minimum required = 1, but only found 0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateUsingExternallyDefinedCode() {
|
||||||
|
CodeSystem codeSystem = new CodeSystem();
|
||||||
|
codeSystem.setUrl("http://foo");
|
||||||
|
codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
||||||
|
IIdType csId = myCodeSystemDao.create(codeSystem).getId();
|
||||||
|
|
||||||
|
TermCodeSystemVersion csv = new TermCodeSystemVersion();
|
||||||
|
csv.addConcept().setCode("bar").setDisplay("Bar Code");
|
||||||
|
myTerminologySvc.storeNewCodeSystemVersion(codeSystem, csv, mySrd, Collections.emptyList(), Collections.emptyList());
|
||||||
|
|
||||||
|
// Validate a resource containing this codesystem in a field with an extendable binding
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.getText().setStatus(Narrative.NarrativeStatus.GENERATED).setDivAsString("<div>hello</div>");
|
||||||
|
patient
|
||||||
|
.addIdentifier()
|
||||||
|
.setSystem("http://example.com")
|
||||||
|
.setValue("12345")
|
||||||
|
.getType()
|
||||||
|
.addCoding()
|
||||||
|
.setSystem("http://foo")
|
||||||
|
.setCode("bar");
|
||||||
|
MethodOutcome outcome = myPatientDao.validate(patient, null, encode(patient), EncodingEnum.JSON, ValidationModeEnum.CREATE, null, mySrd);
|
||||||
|
IBaseOperationOutcome oo = outcome.getOperationOutcome();
|
||||||
|
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
|
||||||
|
|
||||||
|
// It would be ok for this to produce 0 issues, or just an information message too
|
||||||
|
assertEquals(1, OperationOutcomeUtil.getIssueCount(myFhirCtx, oo));
|
||||||
|
assertEquals("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type, and a code should come from this value set unless it has no suitable code) (codes = http://foo#bar)", OperationOutcomeUtil.getFirstIssueDetails(myFhirCtx, oo));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String encode(Patient thePatient) {
|
||||||
|
return myFhirCtx.newJsonParser().encodeResourceToString(thePatient);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private OperationOutcome doTestValidateResourceContainingProfileDeclaration(String methodName, EncodingEnum enc) throws IOException {
|
private OperationOutcome doTestValidateResourceContainingProfileDeclaration(String methodName, EncodingEnum enc) throws IOException {
|
||||||
Bundle vss = loadResourceFromClasspath(Bundle.class, "/org/hl7/fhir/r4/model/valueset/valuesets.xml");
|
Bundle vss = loadResourceFromClasspath(Bundle.class, "/org/hl7/fhir/r4/model/valueset/valuesets.xml");
|
||||||
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-status"), mySrd);
|
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-status"), mySrd);
|
||||||
|
|
|
@ -98,13 +98,11 @@ public class ValidationSupportChain implements IValidationSupport {
|
||||||
@Override
|
@Override
|
||||||
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
||||||
for (IValidationSupport next : myChain) {
|
for (IValidationSupport next : myChain) {
|
||||||
if (next.isCodeSystemSupported(theCtx, theSystem)) {
|
|
||||||
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);
|
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);
|
||||||
if (retVal != null) {
|
if (retVal != null) {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,13 +77,11 @@ public class ValidationSupportChain implements IValidationSupport {
|
||||||
@Override
|
@Override
|
||||||
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
||||||
for (IValidationSupport next : myChain) {
|
for (IValidationSupport next : myChain) {
|
||||||
if (next.isCodeSystemSupported(theCtx, theSystem)) {
|
|
||||||
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);
|
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);
|
||||||
if (retVal != null) {
|
if (retVal != null) {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,13 +75,11 @@ public class ValidationSupportChain implements IValidationSupport {
|
||||||
@Override
|
@Override
|
||||||
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
||||||
for (IValidationSupport next : myChain) {
|
for (IValidationSupport next : myChain) {
|
||||||
if (next.isCodeSystemSupported(theCtx, theSystem)) {
|
|
||||||
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);
|
CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem);
|
||||||
if (retVal != null) {
|
if (retVal != null) {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,10 @@
|
||||||
handled by method implementations that did not have any <![CDATA[<code>@IncludeParam</code>]]> defined. This
|
handled by method implementations that did not have any <![CDATA[<code>@IncludeParam</code>]]> defined. This
|
||||||
is now corrected. Thanks to Tuomo Ala-Vannesluoma for reporting and providing a test case!
|
is now corrected. Thanks to Tuomo Ala-Vannesluoma for reporting and providing a test case!
|
||||||
</action>
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
The JPA server failed to find codes defined in not-present codesystems in some cases, and reported
|
||||||
|
that the CodeSystem did not exist. This has been corrected.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
|
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
|
||||||
<action type="fix">
|
<action type="fix">
|
||||||
|
|
Loading…
Reference in New Issue