Validation fixes for QuestionnaireResponse validation

This commit is contained in:
James Agnew 2017-10-18 11:59:22 -04:00
parent 90ecaef87c
commit a265de8649
9 changed files with 3964 additions and 3836 deletions

View File

@ -180,7 +180,7 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
@OperationParam(name = "searchParam", min = 1, max = 1) String theSearchParam,
@OperationParam(name = "text", min = 1, max = 1) String theText
) {
JpaSystemProviderDstu3.validateFulltextSearchEnabled(mySearchDao);
if (isBlank(theContext)) {
throw new InvalidRequestException("Parameter 'context' must be provided");

View File

@ -2105,6 +2105,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
private String resolve(String uri, String ref) {
if (isBlank(uri)) {
return ref;
}
String[] up = uri.split("\\/");
String[] rp = ref.split("\\/");
if (context.getResourceNames().contains(up[up.length-2]) && context.getResourceNames().contains(rp[0])) {

View File

@ -874,9 +874,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try {
Coding c = ObjectConverter.readAsCoding(element);
long t = System.nanoTime();
ValidationResult vr = context.validateCode(c, valueset);
ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(c, valueset);
}
txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) {
if (vr != null && !vr.isOk()) {
if (vr.IsNoService())
hint(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided could not be validated in the absence of a terminology server");
else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) {
@ -1385,7 +1388,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ValueSet vs = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl());
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null, "ValueSet {0} not found", describeReference(binding.getValueSet()))) {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(null, value, null, vs);
ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(null, value, null, vs);
}
txTime = txTime + (System.nanoTime() - t);
if (vr != null && !vr.isOk()) {
if (vr.IsNoService())
@ -2015,6 +2021,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
private String resolve(String uri, String ref) {
if (isBlank(uri)) {
return ref;
}
String[] up = uri.split("\\/");
String[] rp = ref.split("\\/");
if (context.getResourceNames().contains(up[up.length-2]) && context.getResourceNames().contains(rp[0])) {

View File

@ -884,4 +884,5 @@ public class FhirInstanceValidatorDstu3Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -1,17 +1,7 @@
package org.hl7.fhir.dstu3.hapi.validation;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
@ -20,38 +10,39 @@ import org.hl7.fhir.dstu3.context.IWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IntegerType;
import org.hl7.fhir.dstu3.model.Questionnaire;
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class QuestionnaireResponseValidatorDstu3Test {
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport();
private static FhirContext ourCtx = FhirContext.forDstu3();
public static final IdType ID_ICC_QUESTIONNAIRE_SETUP = new IdType("Questionnaire/profile");
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QuestionnaireResponseValidatorDstu3Test.class);
private static final String CODE_ICC_SCHOOLTYPE_PT = "PT";
private static final IdType ID_VS_SCHOOLTYPE = new IdType("ValueSet/schooltype");
private static final String SYSTEMURI_ICC_SCHOOLTYPE = "http://ehealthinnovation/icc/ns/schooltype";
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport();
private static FhirContext ourCtx = FhirContext.forDstu3();
private FhirInstanceValidator myInstanceVal;
private FhirValidator myVal;
private IValidationSupport myValSupport;
private IWorkerContext myWorkerCtx;
@Before
@ -70,6 +61,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
myVal.registerValidatorModule(myInstanceVal);
}
private ValidationResult stripBindingHasNoSourceMessage(ValidationResult theErrors) {
List<SingleValidationMessage> messages = new ArrayList<SingleValidationMessage>(theErrors.getMessages());
for (int i = 0; i < messages.size(); i++) {
@ -81,6 +73,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
return new ValidationResult(ourCtx, messages);
}
@Test
public void testAnswerWithWrongType() {
Questionnaire q = new Questionnaire();
@ -221,6 +214,64 @@ public class QuestionnaireResponseValidatorDstu3Test {
assertThat(errors.toString(), containsString("No response found for required item link0"));
}
@Test
public void testValidateQuestionnaireResponseWithValueSetChoiceAnswer() {
/*
* Create valueset
*/
ValueSet iccSchoolTypeVs = new ValueSet();
iccSchoolTypeVs.setId(ID_VS_SCHOOLTYPE);
iccSchoolTypeVs.getCompose().getIncludeFirstRep().setSystem(SYSTEMURI_ICC_SCHOOLTYPE);
iccSchoolTypeVs
.getCompose()
.getIncludeFirstRep()
.addConcept()
.setCode(CODE_ICC_SCHOOLTYPE_PT)
.setDisplay("Part Time");
/*
* Create Questionnaire
*/
Questionnaire questionnaire = new Questionnaire();
{
questionnaire.setId(ID_ICC_QUESTIONNAIRE_SETUP);
Questionnaire.QuestionnaireItemComponent basicGroup = questionnaire.addItem();
basicGroup.setLinkId("basic");
basicGroup.setType(Questionnaire.QuestionnaireItemType.GROUP);
basicGroup
.addItem()
.setLinkId("schoolType")
.setType(Questionnaire.QuestionnaireItemType.CHOICE)
.setOptions(new Reference(ID_VS_SCHOOLTYPE))
.setRequired(true);
}
/*
* Create response
*/
QuestionnaireResponse questionnaireResponse = new QuestionnaireResponse();
questionnaireResponse.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED);
questionnaireResponse.setQuestionnaire(new Reference(ID_ICC_QUESTIONNAIRE_SETUP));
questionnaireResponse.getSubject().setReference("Patient/123");
QuestionnaireResponse.QuestionnaireResponseItemComponent basicGroup = questionnaireResponse
.addItem();
basicGroup.setLinkId("basic");
basicGroup
.addItem()
.setLinkId("schoolType")
.addAnswer()
.setValue(new Coding(SYSTEMURI_ICC_SCHOOLTYPE, CODE_ICC_SCHOOLTYPE_PT, ""));
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(questionnaireResponse.getQuestionnaire().getReference()))).thenReturn(questionnaire);
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(ID_VS_SCHOOLTYPE.getValue()))).thenReturn(iccSchoolTypeVs);
ValidationResult errors = myVal.validateWithResult(questionnaireResponse);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No issues"));
}
@Test
public void testOpenchoiceAnswer() {
String questionnaireRef = "http://example.com/Questionnaire/q1";

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.r4.validation;
package org.hl7.fhir.r4.validation;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
@ -241,9 +241,9 @@ public class FhirInstanceValidatorR4Test {
myVal.registerValidatorModule(myInstanceVal);
mySupportedCodeSystemsForExpansion = new HashMap<String, ValueSet.ValueSetExpansionComponent>();
mySupportedCodeSystemsForExpansion = new HashMap<>();
myValidConcepts = new ArrayList<String>();
myValidConcepts = new ArrayList<>();
when(myMockSupport.expandValueSet(any(FhirContext.class), any(ConceptSetComponent.class))).thenAnswer(new Answer<ValueSetExpansionComponent>() {
@Override

View File

@ -1,57 +1,52 @@
package ca.uhn.fhir.validation;
package org.hl7.fhir.r4.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.r4.hapi.ctx.ValidationSupportChain;
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
import org.hl7.fhir.r4.model.IdType;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.context.IWorkerContext;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IntegerType;
import org.hl7.fhir.dstu3.model.Questionnaire;
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
public class QuestionnaireResponseValidatorR4Test {
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport();
private static FhirContext ourCtx = FhirContext.forDstu3();
public static final IdType ID_ICC_QUESTIONNAIRE_SETUP = new IdType("Questionnaire/profile");
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QuestionnaireResponseValidatorR4Test.class);
private static final String CODE_ICC_SCHOOLTYPE_PT = "PT";
private static final IdType ID_VS_SCHOOLTYPE = new IdType("ValueSet/schooltype");
private static final String SYSTEMURI_ICC_SCHOOLTYPE = "http://ehealthinnovation/icc/ns/schooltype";
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport();
private static FhirContext ourCtx = FhirContext.forR4();
private FhirInstanceValidator myInstanceVal;
private FhirValidator myVal;
private IValidationSupport myValSupport;
private IWorkerContext myWorkerCtx;
@Before
@ -70,6 +65,7 @@ public class QuestionnaireResponseValidatorR4Test {
myVal.registerValidatorModule(myInstanceVal);
}
private ValidationResult stripBindingHasNoSourceMessage(ValidationResult theErrors) {
List<SingleValidationMessage> messages = new ArrayList<SingleValidationMessage>(theErrors.getMessages());
for (int i = 0; i < messages.size(); i++) {
@ -81,6 +77,7 @@ public class QuestionnaireResponseValidatorR4Test {
return new ValidationResult(ourCtx, messages);
}
@Test
public void testAnswerWithWrongType() {
Questionnaire q = new Questionnaire();
@ -177,7 +174,7 @@ public class QuestionnaireResponseValidatorR4Test {
ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("minimum required = 1, but only found 0 - QuestionnaireResponse.item"));
assertThat(errors.toString(), containsString("ERROR - No LinkId, so can't be validated - QuestionnaireResponse"));
}
@Test
@ -375,6 +372,64 @@ public class QuestionnaireResponseValidatorR4Test {
assertThat(errors.toString(), containsString("LinkId \"link1\" not found in questionnaire"));
}
@Test
public void testValidateQuestionnaireResponseWithValueSetChoiceAnswer() {
/*
* Create valueset
*/
ValueSet iccSchoolTypeVs = new ValueSet();
iccSchoolTypeVs.setId(ID_VS_SCHOOLTYPE);
iccSchoolTypeVs.getCompose().getIncludeFirstRep().setSystem(SYSTEMURI_ICC_SCHOOLTYPE);
iccSchoolTypeVs
.getCompose()
.getIncludeFirstRep()
.addConcept()
.setCode(CODE_ICC_SCHOOLTYPE_PT)
.setDisplay("Part Time");
/*
* Create Questionnaire
*/
Questionnaire questionnaire = new Questionnaire();
{
questionnaire.setId(ID_ICC_QUESTIONNAIRE_SETUP);
Questionnaire.QuestionnaireItemComponent basicGroup = questionnaire.addItem();
basicGroup.setLinkId("basic");
basicGroup.setType(Questionnaire.QuestionnaireItemType.GROUP);
basicGroup
.addItem()
.setLinkId("schoolType")
.setType(Questionnaire.QuestionnaireItemType.CHOICE)
.setOptions(new Reference(ID_VS_SCHOOLTYPE))
.setRequired(true);
}
/*
* Create response
*/
QuestionnaireResponse questionnaireResponse = new QuestionnaireResponse();
questionnaireResponse.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED);
questionnaireResponse.setQuestionnaire(new Reference(ID_ICC_QUESTIONNAIRE_SETUP));
questionnaireResponse.getSubject().setReference("Patient/123");
QuestionnaireResponse.QuestionnaireResponseItemComponent basicGroup = questionnaireResponse
.addItem();
basicGroup.setLinkId("basic");
basicGroup
.addItem()
.setLinkId("schoolType")
.addAnswer()
.setValue(new Coding(SYSTEMURI_ICC_SCHOOLTYPE, CODE_ICC_SCHOOLTYPE_PT, ""));
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(questionnaireResponse.getQuestionnaire().getReference()))).thenReturn(questionnaire);
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq(ID_VS_SCHOOLTYPE.getValue()))).thenReturn(iccSchoolTypeVs);
ValidationResult errors = myVal.validateWithResult(questionnaireResponse);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No issues"));
}
// @Test
public void validateHealthConnexExample() throws Exception {
String input = IOUtils.toString(QuestionnaireResponseValidatorR4Test.class.getResourceAsStream("/questionnaireanswers-0f431c50ddbe4fff8e0dd6b7323625fc.xml"));
@ -417,4 +472,5 @@ public class QuestionnaireResponseValidatorR4Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -1,9 +1,11 @@
package ca.uhn.fhir.validation;
package org.hl7.fhir.r4.validation;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.junit.AfterClass;
import org.junit.Test;

View File

@ -114,6 +114,12 @@
These were accidentally removed in HAPI FHIR 3.0.0. Thanks to
GitHub user @CarthageKing for reporting!
</action>
<action type="fix">
The resource Profile Validator has been enhanced to not try to validate
bound fields where the binding strength is "example", and a crash was
resolved when validating QuestionnaireResponse answers with a type
of "choice" where the choice was bound to a ValueSet.
</action>
</release>
<release version="3.0.0" date="2017-09-27">
<action type="add">