Send Accept header on client requests, and allow string responses in
QuestionnaireResponse validator for questions of type OPENCHOICE
This commit is contained in:
parent
81e7d8c071
commit
072c1ece87
|
@ -31,6 +31,7 @@ import org.apache.http.Header;
|
|||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
|
||||
|
@ -85,7 +86,7 @@ public abstract class BaseHttpClientInvocation {
|
|||
}
|
||||
}
|
||||
|
||||
public void addHeadersToRequest(HttpRequestBase theHttpRequest) {
|
||||
public void addHeadersToRequest(HttpRequestBase theHttpRequest, EncodingEnum theEncoding) {
|
||||
if (myHeaders != null) {
|
||||
for (Header next : myHeaders) {
|
||||
theHttpRequest.addHeader(next);
|
||||
|
@ -96,6 +97,13 @@ public abstract class BaseHttpClientInvocation {
|
|||
theHttpRequest.addHeader("Accept-Charset", "utf-8");
|
||||
theHttpRequest.addHeader("Accept-Encoding", "gzip");
|
||||
|
||||
if (theEncoding == null) {
|
||||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_ALL);
|
||||
} else if (theEncoding == EncodingEnum.JSON) {
|
||||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
|
||||
} else if (theEncoding == EncodingEnum.XML) {
|
||||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -916,7 +916,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
public Object execute() {
|
||||
ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass(), null);
|
||||
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation();
|
||||
return invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
|
||||
return super.invoke(null, binding, invocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -280,6 +280,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
|
|||
void validateServerBase(String theServerBase, HttpClient theHttpClient, BaseClient theClient) {
|
||||
|
||||
GenericClient client = new GenericClient(myContext, theHttpClient, theServerBase, this);
|
||||
client.setEncoding(theClient.getEncoding());
|
||||
for (IClientInterceptor interceptor : theClient.getInterceptors()) {
|
||||
client.registerInterceptor(interceptor);
|
||||
}
|
||||
|
|
|
@ -298,7 +298,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
|
|||
}
|
||||
|
||||
HttpRequestBase retVal = createRequest(url, entity);
|
||||
super.addHeadersToRequest(retVal);
|
||||
super.addHeadersToRequest(retVal, encoding);
|
||||
addMatchHeaders(retVal, url);
|
||||
|
||||
if (contentType != null) {
|
||||
|
|
|
@ -62,7 +62,7 @@ public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
|
|||
appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1);
|
||||
|
||||
HttpDelete retVal = new HttpDelete(b.toString());
|
||||
super.addHeadersToRequest(retVal);
|
||||
super.addHeadersToRequest(retVal, theEncoding);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
|
|||
appendExtraParamsWithQuestionMark(theExtraParams, b, first);
|
||||
|
||||
HttpGet retVal = new HttpGet(b.toString());
|
||||
super.addHeadersToRequest(retVal);
|
||||
super.addHeadersToRequest(retVal, theEncoding);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class HttpSimpleGetClientInvocation extends BaseHttpClientInvocation {
|
|||
@Override
|
||||
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
|
||||
HttpGet retVal = new HttpGet(myUrl);
|
||||
super.addHeadersToRequest(retVal);
|
||||
super.addHeadersToRequest(retVal, theEncoding);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ public class Constants {
|
|||
public static final String FORMAT_XML = "xml";
|
||||
public static final String HEADER_ACCEPT = "Accept";
|
||||
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
|
||||
public static final String HEADER_ACCEPT_VALUE_ALL = CT_FHIR_XML + ";q=1.0, " + CT_FHIR_XML + ";q=1.0";
|
||||
public static final String HEADER_ALLOW = "Allow";
|
||||
public static final String HEADER_AUTHORIZATION = "Authorization";
|
||||
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic ";
|
||||
|
|
|
@ -625,7 +625,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
resourceId = dt.getIdPart();
|
||||
}
|
||||
Long targetPid = translateForcedIdToPid(new IdDt(resourceId));
|
||||
ourLog.info("Searching for resource link with target PID: {}", targetPid);
|
||||
ourLog.debug("Searching for resource link with target PID: {}", targetPid);
|
||||
Predicate eq = builder.equal(from.get("myTargetResourcePid"), targetPid);
|
||||
|
||||
codePredicates.add(eq);
|
||||
|
|
|
@ -77,6 +77,7 @@ public class GenericClientDstu2Test {
|
|||
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
||||
myResponseCount = 0;
|
||||
}
|
||||
|
||||
private String extractBody(ArgumentCaptor<HttpUriRequest> capt, int count) throws IOException {
|
||||
|
@ -139,6 +140,130 @@ public class GenericClientDstu2Test {
|
|||
idx++;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptHeaderFetchConformance() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
||||
Conformance conf = new Conformance();
|
||||
conf.setCopyright("COPY");
|
||||
|
||||
final String respString = p.encodeResourceToString(conf);
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
Conformance resp = (Conformance)client.fetchConformance().ofType(Conformance.class).execute();
|
||||
assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
|
||||
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
|
||||
idx++;
|
||||
|
||||
resp = (Conformance)client.fetchConformance().ofType(Conformance.class).encodedJson().execute();
|
||||
assertEquals("http://example.com/fhir/metadata?_format=json", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
|
||||
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
|
||||
idx++;
|
||||
|
||||
resp = (Conformance)client.fetchConformance().ofType(Conformance.class).encodedXml().execute();
|
||||
assertEquals("http://example.com/fhir/metadata?_format=xml", capt.getAllValues().get(idx).getURI().toASCIIString());
|
||||
assertEquals(1, capt.getAllValues().get(idx).getHeaders("Accept").length);
|
||||
assertThat(capt.getAllValues().get(idx).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_XML));
|
||||
idx++;
|
||||
}
|
||||
|
||||
private int myResponseCount = 0;
|
||||
|
||||
@Test
|
||||
public void testAcceptHeaderPreflightConformancePreferJson() throws Exception {
|
||||
String methodName = "testAcceptHeaderPreflightConformancePreferJson";
|
||||
final IParser p = ourCtx.newXmlParser();
|
||||
|
||||
final Conformance conf = new Conformance();
|
||||
conf.setCopyright("COPY");
|
||||
|
||||
final Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAMILY");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
if (myResponseCount++ == 0) {
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(conf)), Charset.forName("UTF-8"));
|
||||
} else {
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(patient)), Charset.forName("UTF-8"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://"+methodName+".example.com/fhir");
|
||||
client.setEncoding(EncodingEnum.JSON);
|
||||
|
||||
Patient resp = client.read(Patient.class, new IdDt("123"));
|
||||
assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue());
|
||||
assertEquals("http://"+methodName+".example.com/fhir/metadata?_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals(1, capt.getAllValues().get(0).getHeaders("Accept").length);
|
||||
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
|
||||
assertEquals("http://"+methodName+".example.com/fhir/Patient/123?_format=json", capt.getAllValues().get(1).getURI().toASCIIString());
|
||||
assertEquals(1, capt.getAllValues().get(1).getHeaders("Accept").length);
|
||||
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.CT_FHIR_JSON));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptHeaderPreflightConformance() throws Exception {
|
||||
String methodName = "testAcceptHeaderPreflightConformance";
|
||||
final IParser p = ourCtx.newXmlParser();
|
||||
|
||||
final Conformance conf = new Conformance();
|
||||
conf.setCopyright("COPY");
|
||||
|
||||
final Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAMILY");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
if (myResponseCount++ == 0) {
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(conf)), Charset.forName("UTF-8"));
|
||||
} else {
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(patient)), Charset.forName("UTF-8"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://"+methodName+".example.com/fhir");
|
||||
|
||||
Patient resp = client.read(Patient.class, new IdDt("123"));
|
||||
assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue());
|
||||
assertEquals("http://"+methodName+".example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals(1, capt.getAllValues().get(0).getHeaders("Accept").length);
|
||||
assertThat(capt.getAllValues().get(0).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
|
||||
assertEquals("http://"+methodName+".example.com/fhir/Patient/123", capt.getAllValues().get(1).getURI().toASCIIString());
|
||||
assertEquals(1, capt.getAllValues().get(1).getHeaders("Accept").length);
|
||||
assertThat(capt.getAllValues().get(1).getHeaders("Accept")[0].getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_ALL));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreate() throws Exception {
|
||||
|
|
|
@ -72,8 +72,15 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
return Collections.unmodifiableSet(retVal);
|
||||
}
|
||||
|
||||
private List<org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent> findAnswersByLinkId(
|
||||
List<org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent> theQuestion, String theLinkId) {
|
||||
private Set<Class<? extends Type>> allowedTypes(Class<? extends Type> theClass0, Class<? extends Type> theClass1) {
|
||||
HashSet<Class<? extends Type>> retVal = new HashSet<Class<? extends Type>>();
|
||||
retVal.add(theClass0);
|
||||
retVal.add(theClass1);
|
||||
return Collections.unmodifiableSet(retVal);
|
||||
}
|
||||
|
||||
private List<org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent> findAnswersByLinkId(List<org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent> theQuestion,
|
||||
String theLinkId) {
|
||||
Validate.notBlank(theLinkId, "theLinkId must not be blank");
|
||||
|
||||
ArrayList<org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent> retVal = new ArrayList<QuestionnaireResponse.QuestionComponent>();
|
||||
|
@ -85,8 +92,7 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> findGroupByLinkId(
|
||||
List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> theGroups, String theLinkId) {
|
||||
private List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> findGroupByLinkId(List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> theGroups, String theLinkId) {
|
||||
ArrayList<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> retVal = new ArrayList<QuestionnaireResponse.GroupComponent>();
|
||||
for (org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent next : theGroups) {
|
||||
if (theLinkId == null) {
|
||||
|
@ -100,28 +106,25 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
// protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) {
|
||||
// return test(errors, type, pathParts, thePass, msg, IssueSeverity.FATAL);
|
||||
// }
|
||||
// protected boolean fail(List<ValidationMessage> errors, IssueType type, List<String> pathParts, boolean thePass, String msg) {
|
||||
// return test(errors, type, pathParts, thePass, msg, IssueSeverity.FATAL);
|
||||
// }
|
||||
|
||||
public void validate(List<ValidationMessage> theErrors, QuestionnaireResponse theAnswers) {
|
||||
LinkedList<String> pathStack = new LinkedList<String>();
|
||||
pathStack.add("QuestionnaireResponse");
|
||||
pathStack.add(QuestionnaireResponse.SP_QUESTIONNAIRE);
|
||||
|
||||
if (!super.fail(theErrors, IssueType.INVALID, pathStack, theAnswers.hasQuestionnaire(),
|
||||
"QuestionnaireResponse does not specity which questionnaire it is providing answers to")) {
|
||||
if (!super.fail(theErrors, IssueType.INVALID, pathStack, theAnswers.hasQuestionnaire(), "QuestionnaireResponse does not specity which questionnaire it is providing answers to")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference questionnaireRef = theAnswers.getQuestionnaire();
|
||||
Questionnaire questionnaire = getQuestionnaire(theAnswers, questionnaireRef);
|
||||
if (questionnaire == null && theErrors.size() > 0
|
||||
&& theErrors.get(theErrors.size() - 1).getLevel() == IssueSeverity.FATAL) {
|
||||
if (questionnaire == null && theErrors.size() > 0 && theErrors.get(theErrors.size() - 1).getLevel() == IssueSeverity.FATAL) {
|
||||
return;
|
||||
}
|
||||
if (!fail(theErrors, IssueType.INVALID, pathStack, questionnaire != null,
|
||||
"Questionnaire {0} is not found in the WorkerContext", theAnswers.getQuestionnaire().getReference())) {
|
||||
if (!fail(theErrors, IssueType.INVALID, pathStack, questionnaire != null, "Questionnaire {0} is not found in the WorkerContext", theAnswers.getQuestionnaire().getReference())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -185,9 +188,8 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private void validateGroup(List<ValidationMessage> theErrors, GroupComponent theQuestGroup,
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent theAnsGroup, LinkedList<String> thePathStack,
|
||||
QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
private void validateGroup(List<ValidationMessage> theErrors, GroupComponent theQuestGroup, org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent theAnsGroup,
|
||||
LinkedList<String> thePathStack, QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
|
||||
for (org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent next : theAnsGroup.getQuestion()) {
|
||||
rule(theErrors, IssueType.INVALID, thePathStack, isNotBlank(next.getLinkId()), "Question found with no linkId");
|
||||
|
@ -205,11 +207,10 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
|
||||
// Check that there are no extra answers
|
||||
for (int i = 0; i < theAnsGroup.getQuestion().size(); i++) {
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent nextQuestion = theAnsGroup.getQuestion()
|
||||
.get(i);
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent nextQuestion = theAnsGroup.getQuestion().get(i);
|
||||
thePathStack.add("question[" + i + "]");
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, allowedQuestions.contains(nextQuestion.getLinkId()),
|
||||
"Found answer with linkId[{0}] but this ID is not allowed at this position", nextQuestion.getLinkId());
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, allowedQuestions.contains(nextQuestion.getLinkId()), "Found answer with linkId[{0}] but this ID is not allowed at this position",
|
||||
nextQuestion.getLinkId());
|
||||
thePathStack.remove();
|
||||
}
|
||||
|
||||
|
@ -217,21 +218,18 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
|
||||
}
|
||||
|
||||
private void validateQuestion(List<ValidationMessage> theErrors, QuestionComponent theQuestion,
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent theAnsGroup, LinkedList<String> thePathStack,
|
||||
QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
private void validateQuestion(List<ValidationMessage> theErrors, QuestionComponent theQuestion, org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent theAnsGroup,
|
||||
LinkedList<String> thePathStack, QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
QuestionComponent question = theQuestion;
|
||||
String linkId = question.getLinkId();
|
||||
if (!fail(theErrors, IssueType.INVALID, thePathStack, isNotBlank(linkId),
|
||||
"Questionnaire is invalid, question found with no link ID")) {
|
||||
if (!fail(theErrors, IssueType.INVALID, thePathStack, isNotBlank(linkId), "Questionnaire is invalid, question found with no link ID")) {
|
||||
return;
|
||||
}
|
||||
|
||||
AnswerFormat type = question.getType();
|
||||
if (type == null) {
|
||||
// Support old format/casing and new
|
||||
List<Extension> extensions = question
|
||||
.getExtensionsByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-deReference");
|
||||
List<Extension> extensions = question.getExtensionsByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-deReference");
|
||||
if (extensions.isEmpty()) {
|
||||
extensions = question.getExtensionsByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-dereference");
|
||||
}
|
||||
|
@ -256,27 +254,22 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
// question = toQuestion(element);
|
||||
} else {
|
||||
if (question.getGroup().isEmpty()) {
|
||||
rule(theErrors, IssueType.INVALID, thePathStack, false,
|
||||
"Questionnaire is invalid, no type and no groups specified for question with link ID[{0}]", linkId);
|
||||
rule(theErrors, IssueType.INVALID, thePathStack, false, "Questionnaire is invalid, no type and no groups specified for question with link ID[{0}]", linkId);
|
||||
return;
|
||||
}
|
||||
type = AnswerFormat.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
List<org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent> answers = findAnswersByLinkId(
|
||||
theAnsGroup.getQuestion(), linkId);
|
||||
List<org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent> answers = findAnswersByLinkId(theAnsGroup.getQuestion(), linkId);
|
||||
if (answers.size() > 1) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !question.getRequired(),
|
||||
"Multiple answers repetitions found with linkId[{0}]", linkId);
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !question.getRequired(), "Multiple answers repetitions found with linkId[{0}]", linkId);
|
||||
}
|
||||
if (answers.size() == 0) {
|
||||
if (theValidateRequired) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !question.getRequired(),
|
||||
"Missing answer to required question with linkId[{0}]", linkId);
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !question.getRequired(), "Missing answer to required question with linkId[{0}]", linkId);
|
||||
} else {
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack, !question.getRequired(),
|
||||
"Missing answer to required question with linkId[{0}]", linkId);
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack, !question.getRequired(), "Missing answer to required question with linkId[{0}]", linkId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -291,24 +284,19 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
}
|
||||
}
|
||||
|
||||
private void validateQuestionGroups(List<ValidationMessage> theErrors, QuestionComponent theQuestion,
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent theAnswerQuestion,
|
||||
private void validateQuestionGroups(List<ValidationMessage> theErrors, QuestionComponent theQuestion, org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent theAnswerQuestion,
|
||||
LinkedList<String> thePathSpec, QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
for (QuestionAnswerComponent nextAnswer : theAnswerQuestion.getAnswer()) {
|
||||
validateGroups(theErrors, theQuestion.getGroup(), nextAnswer.getGroup(), thePathSpec, theAnswers,
|
||||
theValidateRequired);
|
||||
validateGroups(theErrors, theQuestion.getGroup(), nextAnswer.getGroup(), thePathSpec, theAnswers, theValidateRequired);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateGroupGroups(List<ValidationMessage> theErrors, GroupComponent theQuestGroup,
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent theAnsGroup, LinkedList<String> thePathSpec,
|
||||
QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
validateGroups(theErrors, theQuestGroup.getGroup(), theAnsGroup.getGroup(), thePathSpec, theAnswers,
|
||||
theValidateRequired);
|
||||
private void validateGroupGroups(List<ValidationMessage> theErrors, GroupComponent theQuestGroup, org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent theAnsGroup,
|
||||
LinkedList<String> thePathSpec, QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
validateGroups(theErrors, theQuestGroup.getGroup(), theAnsGroup.getGroup(), thePathSpec, theAnswers, theValidateRequired);
|
||||
}
|
||||
|
||||
private void validateGroups(List<ValidationMessage> theErrors, List<GroupComponent> theQuestionGroups,
|
||||
List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> theAnswerGroups,
|
||||
private void validateGroups(List<ValidationMessage> theErrors, List<GroupComponent> theQuestionGroups, List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> theAnswerGroups,
|
||||
LinkedList<String> thePathStack, QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
Set<String> linkIds = new HashSet<String>();
|
||||
for (GroupComponent nextQuestionGroup : theQuestionGroups) {
|
||||
|
@ -316,11 +304,9 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
if (!linkIds.add(nextLinkId)) {
|
||||
if (isBlank(nextLinkId)) {
|
||||
fail(theErrors, IssueType.BUSINESSRULE, thePathStack, false,
|
||||
"Questionnaire in invalid, unable to validate QuestionnaireResponse: Multiple groups found at this position with blank/missing linkId",
|
||||
nextLinkId);
|
||||
"Questionnaire in invalid, unable to validate QuestionnaireResponse: Multiple groups found at this position with blank/missing linkId", nextLinkId);
|
||||
} else {
|
||||
fail(theErrors, IssueType.BUSINESSRULE, thePathStack, false,
|
||||
"Questionnaire in invalid, unable to validate QuestionnaireResponse: Multiple groups found at this position with linkId[{0}]",
|
||||
fail(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Questionnaire in invalid, unable to validate QuestionnaireResponse: Multiple groups found at this position with linkId[{0}]",
|
||||
nextLinkId);
|
||||
}
|
||||
}
|
||||
|
@ -331,16 +317,13 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
String linkId = nextQuestionGroup.getLinkId();
|
||||
allowedGroups.add(linkId);
|
||||
|
||||
List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> answerGroups = findGroupByLinkId(
|
||||
theAnswerGroups, linkId);
|
||||
List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> answerGroups = findGroupByLinkId(theAnswerGroups, linkId);
|
||||
if (answerGroups.isEmpty()) {
|
||||
if (nextQuestionGroup.getRequired()) {
|
||||
if (theValidateRequired) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required group with linkId[{0}]",
|
||||
linkId);
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required group with linkId[{0}]", linkId);
|
||||
} else {
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required group with linkId[{0}]",
|
||||
linkId);
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required group with linkId[{0}]", linkId);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
|
@ -349,9 +332,7 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
if (nextQuestionGroup.getRepeats() == false) {
|
||||
int index = theAnswerGroups.indexOf(answerGroups.get(1));
|
||||
thePathStack.add("group[" + index + "]");
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false,
|
||||
"Multiple repetitions of group with linkId[{0}] found at this position, but this group can not repeat",
|
||||
linkId);
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Multiple repetitions of group with linkId[{0}] found at this position, but this group can not repeat", linkId);
|
||||
thePathStack.removeLast();
|
||||
}
|
||||
}
|
||||
|
@ -369,36 +350,27 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
idx++;
|
||||
if (!allowedGroups.contains(next.getLinkId())) {
|
||||
thePathStack.add("group[" + idx + "]");
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false,
|
||||
"Group with linkId[{0}] found at this position, but this group does not exist at this position in Questionnaire",
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Group with linkId[{0}] found at this position, but this group does not exist at this position in Questionnaire",
|
||||
next.getLinkId());
|
||||
thePathStack.removeLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateQuestionAnswers(List<ValidationMessage> theErrors, QuestionComponent theQuestion,
|
||||
LinkedList<String> thePathStack, AnswerFormat type,
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent answerQuestion,
|
||||
QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
private void validateQuestionAnswers(List<ValidationMessage> theErrors, QuestionComponent theQuestion, LinkedList<String> thePathStack, AnswerFormat type,
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent answerQuestion, QuestionnaireResponse theAnswers, boolean theValidateRequired) {
|
||||
|
||||
String linkId = theQuestion.getLinkId();
|
||||
Set<Class<? extends Type>> allowedAnswerTypes = determineAllowedAnswerTypes(type);
|
||||
if (allowedAnswerTypes.isEmpty()) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, answerQuestion.isEmpty(),
|
||||
"Question with linkId[{0}] has no answer type but an answer was provided", linkId);
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, answerQuestion.isEmpty(), "Question with linkId[{0}] has no answer type but an answer was provided", linkId);
|
||||
} else {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack,
|
||||
!(answerQuestion.getAnswer().size() > 1 && !theQuestion.getRepeats()),
|
||||
"Multiple answers to non repeating question with linkId[{0}]", linkId);
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !(answerQuestion.getAnswer().size() > 1 && !theQuestion.getRepeats()), "Multiple answers to non repeating question with linkId[{0}]",
|
||||
linkId);
|
||||
if (theValidateRequired) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack,
|
||||
!(theQuestion.getRequired() && answerQuestion.getAnswer().isEmpty()),
|
||||
"Missing answer to required question with linkId[{0}]", linkId);
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, !(theQuestion.getRequired() && answerQuestion.getAnswer().isEmpty()), "Missing answer to required question with linkId[{0}]", linkId);
|
||||
} else {
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack,
|
||||
!(theQuestion.getRequired() && answerQuestion.getAnswer().isEmpty()),
|
||||
"Missing answer to required question with linkId[{0}]", linkId);
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack, !(theQuestion.getRequired() && answerQuestion.getAnswer().isEmpty()), "Missing answer to required question with linkId[{0}]", linkId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,72 +381,66 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
thePathStack.add("answer[" + answerIdx + "]");
|
||||
Type nextValue = nextAnswer.getValue();
|
||||
if (!allowedAnswerTypes.contains(nextValue.getClass())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false,
|
||||
"Answer to question with linkId[{0}] found of type [{1}] but this is invalid for question of type [{2}]",
|
||||
linkId, nextValue.getClass().getSimpleName(), type.toCode());
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] found of type [{1}] but this is invalid for question of type [{2}]", linkId,
|
||||
nextValue.getClass().getSimpleName(), type.toCode());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate choice answers
|
||||
if (type == AnswerFormat.CHOICE || type == AnswerFormat.OPENCHOICE) {
|
||||
Coding coding = (Coding) nextAnswer.getValue();
|
||||
if (isBlank(coding.getCode()) && isBlank(coding.getSystem()) && isBlank(coding.getSystem())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false,
|
||||
"Answer to question with linkId[{0}] is of type coding, but none of code, system, and display are populated",
|
||||
linkId);
|
||||
continue;
|
||||
} else if (isBlank(coding.getCode()) && isBlank(coding.getSystem())) {
|
||||
if (type != AnswerFormat.OPENCHOICE) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false,
|
||||
"Answer to question with linkId[{0}] is of type only has a display populated (no code or system) but question does not allow {1}",
|
||||
linkId, AnswerFormat.OPENCHOICE.name());
|
||||
if (nextAnswer.getValue() instanceof StringType) {
|
||||
StringType answer = (StringType) nextAnswer.getValue();
|
||||
if (answer == null || isBlank(answer.getValueAsString())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] is required but answer does not have a value", linkId);
|
||||
continue;
|
||||
}
|
||||
} else if (isBlank(coding.getCode()) || isBlank(coding.getSystem())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false,
|
||||
"Answer to question with linkId[{0}] has a coding, but this coding does not contain a code and system (both must be present, or neither is the question allows {1})",
|
||||
linkId, AnswerFormat.OPENCHOICE.name());
|
||||
continue;
|
||||
}
|
||||
|
||||
String optionsRef = theQuestion.getOptions().getReference();
|
||||
if (isNotBlank(optionsRef)) {
|
||||
ValueSet valueSet = getValueSet(theAnswers, theQuestion.getOptions());
|
||||
if (valueSet == null) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false,
|
||||
"Question with linkId[{0}] has options ValueSet[{1}] but this ValueSet can not be found", linkId,
|
||||
optionsRef);
|
||||
} else {
|
||||
Coding coding = (Coding) nextAnswer.getValue();
|
||||
if (isBlank(coding.getCode())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] is of type {1} but coding answer does not have a code", linkId, type.name());
|
||||
continue;
|
||||
}
|
||||
if (isBlank(coding.getSystem())) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Answer to question with linkId[{0}] is of type {1} but coding answer does not have a system", linkId, type.name());
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean found = false;
|
||||
if (coding.getSystem().equals(valueSet.getCodeSystem().getSystem())) {
|
||||
for (ConceptDefinitionComponent next : valueSet.getCodeSystem().getConcept()) {
|
||||
if (coding.getCode().equals(next.getCode())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
String optionsRef = theQuestion.getOptions().getReference();
|
||||
if (isNotBlank(optionsRef)) {
|
||||
ValueSet valueSet = getValueSet(theAnswers, theQuestion.getOptions());
|
||||
if (valueSet == null) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Question with linkId[{0}] has options ValueSet[{1}] but this ValueSet can not be found", linkId, optionsRef);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
for (ConceptSetComponent nextCompose : valueSet.getCompose().getInclude()) {
|
||||
if (coding.getSystem().equals(nextCompose.getSystem())) {
|
||||
for (ConceptReferenceComponent next : nextCompose.getConcept()) {
|
||||
if (coding.getCode().equals(next.getCode())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
boolean found = false;
|
||||
if (coding.getSystem().equals(valueSet.getCodeSystem().getSystem())) {
|
||||
for (ConceptDefinitionComponent next : valueSet.getCodeSystem().getConcept()) {
|
||||
if (coding.getCode().equals(next.getCode())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
for (ConceptSetComponent nextCompose : valueSet.getCompose().getInclude()) {
|
||||
if (coding.getSystem().equals(nextCompose.getSystem())) {
|
||||
for (ConceptReferenceComponent next : nextCompose.getConcept()) {
|
||||
if (coding.getCode().equals(next.getCode())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, found,
|
||||
"Question with linkId[{0}] has answer with system[{1}] and code[{2}] but this is not a valid answer for ValueSet[{3}]",
|
||||
linkId, coding.getSystem(), coding.getCode(), optionsRef);
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, found, "Question with linkId[{0}] has answer with system[{1}] and code[{2}] but this is not a valid answer for ValueSet[{3}]",
|
||||
linkId, coding.getSystem(), coding.getCode(), optionsRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,7 +479,7 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
allowedAnswerTypes = allowedTypes(IntegerType.class);
|
||||
break;
|
||||
case OPENCHOICE:
|
||||
allowedAnswerTypes = allowedTypes(Coding.class);
|
||||
allowedAnswerTypes = allowedTypes(Coding.class, StringType.class);
|
||||
break;
|
||||
case QUANTITY:
|
||||
allowedAnswerTypes = allowedTypes(Quantity.class);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.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;
|
||||
|
||||
|
@ -10,6 +11,7 @@ import java.util.List;
|
|||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.Coding;
|
||||
import org.hl7.fhir.instance.model.DataElement;
|
||||
import org.hl7.fhir.instance.model.IntegerType;
|
||||
import org.hl7.fhir.instance.model.Questionnaire;
|
||||
import org.hl7.fhir.instance.model.Questionnaire.AnswerFormat;
|
||||
import org.hl7.fhir.instance.model.Questionnaire.GroupComponent;
|
||||
|
@ -27,240 +29,347 @@ import org.junit.Test;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
||||
public class QuestionnaireResponseValidatorTest {
|
||||
private static final FhirContext ourCtx = FhirContext.forDstu2Hl7Org();
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QuestionnaireResponseValidatorTest.class);
|
||||
private QuestionnaireResponseValidator myVal;
|
||||
private static final FhirContext ourCtx = FhirContext.forDstu2Hl7Org();
|
||||
|
||||
private WorkerContext myWorkerCtx;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myWorkerCtx = new WorkerContext();
|
||||
myVal = new QuestionnaireResponseValidator(myWorkerCtx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnswerWithWrongType() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Answer to question with linkId[link0] found of type [StringType] but this is invalid for question of type [boolean]"));
|
||||
}
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QuestionnaireResponseValidatorTest.class);
|
||||
private QuestionnaireResponseValidator myVal;
|
||||
|
||||
@Test
|
||||
public void testCodedAnswer() {
|
||||
String questionnaireRef = "http://example.com/Questionnaire/q1";
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.CHOICE).setOptions(new Reference("http://somevalueset"));
|
||||
myWorkerCtx.getQuestionnaires().put(questionnaireRef, q);
|
||||
|
||||
ValueSet options = new ValueSet();
|
||||
options.getCodeSystem().setSystem("urn:system").addConcept().setCode("code0");
|
||||
options.getCompose().addInclude().setSystem("urn:system2").addConcept().setCode("code2");
|
||||
myWorkerCtx.getValueSets().put("http://somevalueset", options);
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
List<ValidationMessage> errors;
|
||||
|
||||
// Good code
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system").setCode("code0"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 0, errors.size());
|
||||
private WorkerContext myWorkerCtx;
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system2").setCode("code2"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 0, errors.size());
|
||||
@Before
|
||||
public void before() {
|
||||
myWorkerCtx = new WorkerContext();
|
||||
myVal = new QuestionnaireResponseValidator(myWorkerCtx);
|
||||
}
|
||||
|
||||
// Bad code
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system").setCode("code1"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(), containsString("message=Question with linkId[link0] has answer with system[urn:system] and code[code1] but this is not a valid answer for ValueSet[http://somevalueset]"));
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system2").setCode("code3"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(), containsString("message=Question with linkId[link0] has answer with system[urn:system2] and code[code3] but this is not a valid answer for ValueSet[http://somevalueset]"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtensionDereference() throws Exception {
|
||||
Questionnaire q = ourCtx.newJsonParser().parseResource(Questionnaire.class, IOUtils.toString(getClass().getResourceAsStream("/dereference-q.json")));
|
||||
QuestionnaireResponse qa = ourCtx.newXmlParser().parseResource(QuestionnaireResponse.class, IOUtils.toString(getClass().getResourceAsStream("/dereference-qr.xml")));
|
||||
DataElement de = ourCtx.newJsonParser().parseResource(DataElement.class, IOUtils.toString(getClass().getResourceAsStream("/dereference-de.json")));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
myWorkerCtx.getDataElements().put("DataElement/4771", de);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertEquals(errors.toString(), errors.size(), 0);
|
||||
}
|
||||
@Test
|
||||
public void testAnswerWithWrongType() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
@Test
|
||||
public void testGroupWithNoLinkIdInQuestionnaireResponse() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
GroupComponent qGroup = q.getGroup().addGroup();
|
||||
qGroup.addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent qaGroup = qa.getGroup().addGroup();
|
||||
qaGroup.addQuestion().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Answer to question with linkId[link0] found of type [StringType] but this is invalid for question of type [boolean]"));
|
||||
}
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testMissingRequiredQuestion() {
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.STRING);
|
||||
q.getGroup().addQuestion().setLinkId("link1").setRequired(true).setType(AnswerFormat.STRING);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
qa.getGroup().addQuestion().setLinkId("link1").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Missing answer to required question with linkId[link0]"));
|
||||
}
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Answer to question with linkId[link0] found of type [StringType] but this is invalid for question of type [boolean]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleGroupsWithNoLinkIdInQuestionnaire() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
GroupComponent qGroup = q.getGroup().addGroup();
|
||||
qGroup.addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.BOOLEAN);
|
||||
qGroup = q.getGroup().addGroup();
|
||||
qGroup.addQuestion().setLinkId("link1").setRequired(true).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent qaGroup = qa.getGroup().addGroup();
|
||||
qaGroup.addQuestion().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("ValidationMessage[level=FATAL,type=BUSINESSRULE,location=//QuestionnaireResponse/group[0],message=Questionnaire in invalid, unable to validate QuestionnaireResponse: Multiple groups found at this position with blank/missing linkId]"));
|
||||
assertEquals(1, errors.size());
|
||||
}
|
||||
@Test
|
||||
public void testCodedAnswer() {
|
||||
String questionnaireRef = "http://example.com/Questionnaire/q1";
|
||||
|
||||
@Test
|
||||
public void testUnexpectedAnswer() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
qa.getGroup().addQuestion().setLinkId("link1").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]"));
|
||||
assertThat(errors.toString(), containsString("message=Found answer with linkId[link1] but this ID is not allowed at this position"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnexpectedGroup() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
qa.getGroup().addGroup().setLinkId("link1");
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/group[0]"));
|
||||
assertThat(errors.toString(), containsString("Group with linkId[link1] found at this position, but this group does not exist at this position in Questionnaire"));
|
||||
}
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.CHOICE).setOptions(new Reference("http://somevalueset"));
|
||||
myWorkerCtx.getQuestionnaires().put(questionnaireRef, q);
|
||||
|
||||
// @Test
|
||||
public void validateHealthConnexExample() throws Exception {
|
||||
String input = IOUtils.toString(QuestionnaireResponseValidatorTest.class.getResourceAsStream("/questionnaireanswers-0f431c50ddbe4fff8e0dd6b7323625fc.xml"));
|
||||
ValueSet options = new ValueSet();
|
||||
options.getCodeSystem().setSystem("urn:system").addConcept().setCode("code0");
|
||||
options.getCompose().addInclude().setSystem("urn:system2").addConcept().setCode("code2");
|
||||
myWorkerCtx.getValueSets().put("http://somevalueset", options);
|
||||
|
||||
QuestionnaireResponse qa = ourCtx.newXmlParser().parseResource(QuestionnaireResponse.class, input);
|
||||
ArrayList<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 0, errors.size());
|
||||
|
||||
/*
|
||||
* Now change a coded value
|
||||
*/
|
||||
//@formatter:off
|
||||
input = input.replaceAll("<answer>\n" +
|
||||
" <valueCoding>\n" +
|
||||
" <system value=\"f69573b8-cb63-4d31-85a4-23ac784735ab\"/>\n" +
|
||||
" <code value=\"2\"/>\n" +
|
||||
" <display value=\"Once/twice\"/>\n" +
|
||||
" </valueCoding>\n" +
|
||||
" </answer>", "<answer>\n" +
|
||||
" <valueCoding>\n" +
|
||||
" <system value=\"f69573b8-cb63-4d31-85a4-23ac784735ab\"/>\n" +
|
||||
" <code value=\"GGG\"/>\n" +
|
||||
" <display value=\"Once/twice\"/>\n" +
|
||||
" </valueCoding>\n" +
|
||||
" </answer>");
|
||||
assertThat(input, containsString("GGG"));
|
||||
//@formatter:on
|
||||
|
||||
qa = ourCtx.newXmlParser().parseResource(QuestionnaireResponse.class, input);
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 10, errors.size());
|
||||
}
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
List<ValidationMessage> errors;
|
||||
|
||||
// Good code
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system").setCode("code0"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 0, errors.size());
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system2").setCode("code2"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 0, errors.size());
|
||||
|
||||
// Bad code
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system").setCode("code1"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(),
|
||||
containsString("message=Question with linkId[link0] has answer with system[urn:system] and code[code1] but this is not a valid answer for ValueSet[http://somevalueset]"));
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system2").setCode("code3"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(),
|
||||
containsString("message=Question with linkId[link0] has answer with system[urn:system2] and code[code3] but this is not a valid answer for ValueSet[http://somevalueset]"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenchoiceAnswer() {
|
||||
String questionnaireRef = "http://example.com/Questionnaire/q1";
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.OPENCHOICE).setOptions(new Reference("http://somevalueset"));
|
||||
myWorkerCtx.getQuestionnaires().put(questionnaireRef, q);
|
||||
|
||||
ValueSet options = new ValueSet();
|
||||
options.getCodeSystem().setSystem("urn:system").addConcept().setCode("code0");
|
||||
options.getCompose().addInclude().setSystem("urn:system2").addConcept().setCode("code2");
|
||||
myWorkerCtx.getValueSets().put("http://somevalueset", options);
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
List<ValidationMessage> errors;
|
||||
|
||||
// Good code
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system").setCode("code0"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 0, errors.size());
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system2").setCode("code2"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 0, errors.size());
|
||||
|
||||
// Bad code
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("urn:system").setCode("code1"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(),
|
||||
containsString("message=Question with linkId[link0] has answer with system[urn:system] and code[code1] but this is not a valid answer for ValueSet[http://somevalueset]"));
|
||||
|
||||
// Partial code
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem(null).setCode("code1"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(), containsString("message=Answer to question with linkId[link0] is of type OPENCHOICE but coding answer does not have a system"));
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("").setCode("code1"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(), containsString("message=Answer to question with linkId[link0] is of type OPENCHOICE but coding answer does not have a system"));
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("system").setCode(null));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(), containsString("message=Answer to question with linkId[link0] is of type OPENCHOICE but coding answer does not have a code"));
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("system").setCode(null));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(), containsString("message=Answer to question with linkId[link0] is of type OPENCHOICE but coding answer does not have a code"));
|
||||
|
||||
// Wrong type
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new IntegerType(123));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(), containsString("message=Answer to question with linkId[link0] found of type [IntegerType] but this is invalid for question of type [open-choice]"));
|
||||
|
||||
// String answer
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new StringType("Hello"));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors, empty());
|
||||
|
||||
// Missing String answer
|
||||
|
||||
qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference(questionnaireRef);
|
||||
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new StringType(""));
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]/answer[0]"));
|
||||
assertThat(errors.toString(), containsString("Answer to question with linkId[link0] is required but answer does not have a value"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtensionDereference() throws Exception {
|
||||
Questionnaire q = ourCtx.newJsonParser().parseResource(Questionnaire.class, IOUtils.toString(getClass().getResourceAsStream("/dereference-q.json")));
|
||||
QuestionnaireResponse qa = ourCtx.newXmlParser().parseResource(QuestionnaireResponse.class, IOUtils.toString(getClass().getResourceAsStream("/dereference-qr.xml")));
|
||||
DataElement de = ourCtx.newJsonParser().parseResource(DataElement.class, IOUtils.toString(getClass().getResourceAsStream("/dereference-de.json")));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
myWorkerCtx.getDataElements().put("DataElement/4771", de);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertEquals(errors.toString(), errors.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupWithNoLinkIdInQuestionnaireResponse() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
GroupComponent qGroup = q.getGroup().addGroup();
|
||||
qGroup.addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent qaGroup = qa.getGroup().addGroup();
|
||||
qaGroup.addQuestion().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Answer to question with linkId[link0] found of type [StringType] but this is invalid for question of type [boolean]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingRequiredQuestion() {
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.STRING);
|
||||
q.getGroup().addQuestion().setLinkId("link1").setRequired(true).setType(AnswerFormat.STRING);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
qa.getGroup().addQuestion().setLinkId("link1").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("Missing answer to required question with linkId[link0]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleGroupsWithNoLinkIdInQuestionnaire() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
GroupComponent qGroup = q.getGroup().addGroup();
|
||||
qGroup.addQuestion().setLinkId("link0").setRequired(true).setType(AnswerFormat.BOOLEAN);
|
||||
qGroup = q.getGroup().addGroup();
|
||||
qGroup.addQuestion().setLinkId("link1").setRequired(true).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent qaGroup = qa.getGroup().addGroup();
|
||||
qaGroup.addQuestion().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString(
|
||||
"ValidationMessage[level=FATAL,type=BUSINESSRULE,location=//QuestionnaireResponse/group[0],message=Questionnaire in invalid, unable to validate QuestionnaireResponse: Multiple groups found at this position with blank/missing linkId]"));
|
||||
assertEquals(1, errors.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnexpectedAnswer() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
qa.getGroup().addQuestion().setLinkId("link1").addAnswer().setValue(new StringType("FOO"));
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/question[0]"));
|
||||
assertThat(errors.toString(), containsString("message=Found answer with linkId[link1] but this ID is not allowed at this position"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnexpectedGroup() {
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormat.BOOLEAN);
|
||||
|
||||
QuestionnaireResponse qa = new QuestionnaireResponse();
|
||||
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1");
|
||||
qa.getGroup().addGroup().setLinkId("link1");
|
||||
|
||||
myWorkerCtx.getQuestionnaires().put(qa.getQuestionnaire().getReference(), q);
|
||||
List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
|
||||
ourLog.info(errors.toString());
|
||||
assertThat(errors.toString(), containsString("location=//QuestionnaireResponse/group[0]/group[0]"));
|
||||
assertThat(errors.toString(), containsString("Group with linkId[link1] found at this position, but this group does not exist at this position in Questionnaire"));
|
||||
}
|
||||
|
||||
// @Test
|
||||
public void validateHealthConnexExample() throws Exception {
|
||||
String input = IOUtils.toString(QuestionnaireResponseValidatorTest.class.getResourceAsStream("/questionnaireanswers-0f431c50ddbe4fff8e0dd6b7323625fc.xml"));
|
||||
|
||||
QuestionnaireResponse qa = ourCtx.newXmlParser().parseResource(QuestionnaireResponse.class, input);
|
||||
ArrayList<ValidationMessage> errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 0, errors.size());
|
||||
|
||||
/*
|
||||
* Now change a coded value
|
||||
*/
|
||||
// @formatter:off
|
||||
input = input.replaceAll(
|
||||
"<answer>\n" + " <valueCoding>\n" + " <system value=\"f69573b8-cb63-4d31-85a4-23ac784735ab\"/>\n" + " <code value=\"2\"/>\n"
|
||||
+ " <display value=\"Once/twice\"/>\n" + " </valueCoding>\n" + " </answer>",
|
||||
"<answer>\n" + " <valueCoding>\n" + " <system value=\"f69573b8-cb63-4d31-85a4-23ac784735ab\"/>\n" + " <code value=\"GGG\"/>\n"
|
||||
+ " <display value=\"Once/twice\"/>\n" + " </valueCoding>\n" + " </answer>");
|
||||
assertThat(input, containsString("GGG"));
|
||||
// @formatter:on
|
||||
|
||||
qa = ourCtx.newXmlParser().parseResource(QuestionnaireResponse.class, input);
|
||||
errors = new ArrayList<ValidationMessage>();
|
||||
myVal.validate(errors, qa);
|
||||
assertEquals(errors.toString(), 10, errors.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,6 +49,17 @@
|
|||
Narrative generator did not include OperationOutcome.issue.diagnostics in the
|
||||
generated narrative.
|
||||
</action>
|
||||
<action type="add">
|
||||
Clients (generic and annotation) did not populate the Accept header on outgoing
|
||||
requests. This is now populated to indicate that the client supports both XML and
|
||||
JSON unless the user has explicitly requested one or the other (in which case the
|
||||
appropriate type only will be send in the accept header). Thanks to
|
||||
Avinash Shanbhag for reporting!
|
||||
</action>
|
||||
<action type="add">
|
||||
QuestionnaireResponse validator now allows responses to questions of
|
||||
type OPENCHOICE to be of type 'string'
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.2" date="2015-09-18">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue