From 8e000749ba19e92e8ed7d3db7d62eb3e325a9318 Mon Sep 17 00:00:00 2001 From: Heinz-Dieter Conradi Date: Tue, 27 Feb 2018 16:31:19 +0100 Subject: [PATCH 1/3] Fix questionnaire schematron with enableWhen and hasAnswer Without this fix, the 'hasAnswer' element of a questionnaire item is ignored. This in turn allowes it that one can have both an 'answer' and a 'hasAnswer' item. A fix is applied for both DSTU3 and R4 and tests are provided. The tests for R4 are currently ignored, since they require a valid schematron, e.g. by merging #869 --- .../dstu3/model/schema/fhir-invariants.sch | 2 +- .../fhir/dstu3/model/schema/questionnaire.sch | 2 +- .../fhir/r4/model/schema/fhir-invariants.sch | 2 +- .../fhir/r4/model/schema/questionnaire.sch | 2 +- ...atronValidationDstu3QuestionnaireTest.java | 127 +++++++++++++++++ ...hematronValidationR4QuestionnaireTest.java | 129 ++++++++++++++++++ 6 files changed, 260 insertions(+), 4 deletions(-) create mode 100644 hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/SchematronValidationDstu3QuestionnaireTest.java create mode 100644 hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/SchematronValidationR4QuestionnaireTest.java diff --git a/hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/schema/fhir-invariants.sch b/hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/schema/fhir-invariants.sch index 2622c675fae..8b6d6ccf4ff 100644 --- a/hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/schema/fhir-invariants.sch +++ b/hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/schema/fhir-invariants.sch @@ -4503,7 +4503,7 @@ que-1: Group items must have nested items, display items cannot have nested items - que-7: enableWhen must contain either a 'answer' or a 'hasAnswer' element + que-7: enableWhen must contain either a 'answer' or a 'hasAnswer' element att-1: It the Attachment has data, it SHALL have a contentType diff --git a/hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/schema/questionnaire.sch b/hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/schema/questionnaire.sch index d9dc253c6eb..2b12ce9bc94 100644 --- a/hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/schema/questionnaire.sch +++ b/hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/schema/questionnaire.sch @@ -76,7 +76,7 @@ que-1: Group items must have nested items, display items cannot have nested items - que-7: enableWhen must contain either a 'answer' or a 'hasAnswer' element + que-7: enableWhen must contain either a 'answer' or a 'hasAnswer' element att-1: It the Attachment has data, it SHALL have a contentType diff --git a/hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/schema/fhir-invariants.sch b/hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/schema/fhir-invariants.sch index e771812c9d4..fb2b6100aac 100644 --- a/hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/schema/fhir-invariants.sch +++ b/hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/schema/fhir-invariants.sch @@ -5187,7 +5187,7 @@ que-11: If one or more option is present, initial[x] must be missing - que-7: enableWhen must contain either a 'answer' or a 'hasAnswer' element + que-7: enableWhen must contain either a 'answer' or a 'hasAnswer' element att-1: If the Attachment has data, it SHALL have a contentType diff --git a/hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/schema/questionnaire.sch b/hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/schema/questionnaire.sch index 7398dddf618..b79612c1bcf 100644 --- a/hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/schema/questionnaire.sch +++ b/hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/schema/questionnaire.sch @@ -74,7 +74,7 @@ que-11: If one or more option is present, initial[x] must be missing - que-7: enableWhen must contain either a 'answer' or a 'hasAnswer' element + que-7: enableWhen must contain either a 'answer' or a 'hasAnswer' element att-1: If the Attachment has data, it SHALL have a contentType diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/SchematronValidationDstu3QuestionnaireTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/SchematronValidationDstu3QuestionnaireTest.java new file mode 100644 index 00000000000..1a7b7618090 --- /dev/null +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/SchematronValidationDstu3QuestionnaireTest.java @@ -0,0 +1,127 @@ +package org.hl7.fhir.dstu3.hapi.validation; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.ValidationResult; +import org.hl7.fhir.dstu3.model.Enumerations; +import org.hl7.fhir.dstu3.model.Questionnaire; +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent; +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType; +import org.hl7.fhir.dstu3.model.StringType; + +import org.hamcrest.Matchers; +import org.junit.AfterClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SchematronValidationDstu3QuestionnaireTest { + private static final Logger ourLog = LoggerFactory.getLogger(SchematronValidationDstu3QuestionnaireTest.class); + + private static FhirContext ourCtx = FhirContext.forDstu3(); + + private static int linkIdCnt = 1; + + @Test + public void enableWhenWithAnswer() { + Questionnaire resource = new Questionnaire(); + resource.setStatus(Enumerations.PublicationStatus.ACTIVE); + + QuestionnaireItemComponent child1 = createItem(QuestionnaireItemType.GROUP); + resource.addItem(child1); + + QuestionnaireItemEnableWhenComponent enableWhen = new QuestionnaireItemEnableWhenComponent(); + enableWhen.setQuestion("q1"); + enableWhen.setAnswer(new StringType("a value")); + child1.addEnableWhen(enableWhen); + + QuestionnaireItemComponent child21 = createItem(QuestionnaireItemType.STRING); + child1.addItem(child21); + + String inputXml = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource); + ourLog.info(inputXml); + + ValidationResult result = validateSchematron(resource); + assertTrue(result.isSuccessful()); + } + + @Test + public void enableWhenWithHasAnswer() { + Questionnaire resource = new Questionnaire(); + resource.setStatus(Enumerations.PublicationStatus.ACTIVE); + + QuestionnaireItemComponent child1 = createItem(QuestionnaireItemType.GROUP); + resource.addItem(child1); + + QuestionnaireItemEnableWhenComponent enableWhen = new QuestionnaireItemEnableWhenComponent(); + enableWhen.setQuestion("q1"); + enableWhen.setHasAnswer(true); + child1.addEnableWhen(enableWhen); + + QuestionnaireItemComponent child21 = createItem(QuestionnaireItemType.STRING); + child1.addItem(child21); + + String inputXml = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource); + ourLog.info(inputXml); + + ValidationResult result = validateSchematron(resource); + assertTrue(result.isSuccessful()); + } + + @Test + public void enableWhenWithHasAnswerAndAnswer() { + Questionnaire resource = new Questionnaire(); + resource.setStatus(Enumerations.PublicationStatus.ACTIVE); + + QuestionnaireItemComponent child1 = createItem(QuestionnaireItemType.GROUP); + resource.addItem(child1); + + QuestionnaireItemEnableWhenComponent enableWhen = new QuestionnaireItemEnableWhenComponent(); + enableWhen.setQuestion("q1"); + enableWhen.setAnswer(new StringType("a value")); + enableWhen.setHasAnswer(true); + child1.addEnableWhen(enableWhen); + + QuestionnaireItemComponent child21 = createItem(QuestionnaireItemType.STRING); + child1.addItem(child21); + + String inputXml = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource); + ourLog.info(inputXml); + + ValidationResult result = validateSchematron(resource); + assertFalse(result.isSuccessful()); + assertEquals(1, result.getMessages().size()); + assertThat(result.getMessages().get(0).getMessage(), containsString("que-7")); + } + + private QuestionnaireItemComponent createItem(QuestionnaireItemType type) { + QuestionnaireItemComponent item = new QuestionnaireItemComponent(); + item.setLinkId("id-" + linkIdCnt++); + item.setType(type); + return item; + } + + private ValidationResult validateSchematron(Questionnaire resource) { + FhirValidator val = ourCtx.newValidator(); + val.setValidateAgainstStandardSchema(false); + val.setValidateAgainstStandardSchematron(true); + ValidationResult result = val.validateWithResult(resource); + + String outcomeXml = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()); + ourLog.info(outcomeXml); + return result; + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } +} diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/SchematronValidationR4QuestionnaireTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/SchematronValidationR4QuestionnaireTest.java new file mode 100644 index 00000000000..af477c764f1 --- /dev/null +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/SchematronValidationR4QuestionnaireTest.java @@ -0,0 +1,129 @@ +package org.hl7.fhir.r4.validation; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.ValidationResult; + +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.hl7.fhir.r4.model.StringType; + +import org.junit.AfterClass; +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Ignore("Requires a valid schematron file, e.g. bei merging pull request #869") +public class SchematronValidationR4QuestionnaireTest { + private static final Logger ourLog = LoggerFactory.getLogger(SchematronValidationR4QuestionnaireTest.class); + + private static FhirContext ourCtx = FhirContext.forR4(); + + private static int linkIdCnt = 1; + + @Test + public void enableWhenWithAnswer() { + Questionnaire resource = new Questionnaire(); + resource.setStatus(Enumerations.PublicationStatus.ACTIVE); + + QuestionnaireItemComponent child1 = createItem(QuestionnaireItemType.GROUP); + resource.addItem(child1); + + QuestionnaireItemEnableWhenComponent enableWhen = new QuestionnaireItemEnableWhenComponent(); + enableWhen.setQuestion("q1"); + enableWhen.setAnswer(new StringType("a value")); + child1.addEnableWhen(enableWhen); + + QuestionnaireItemComponent child21 = createItem(QuestionnaireItemType.STRING); + child1.addItem(child21); + + String inputXml = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource); + ourLog.info(inputXml); + + ValidationResult result = validateSchematron(resource); + assertTrue(result.isSuccessful()); + } + + @Test + public void enableWhenWithHasAnswer() { + Questionnaire resource = new Questionnaire(); + resource.setStatus(Enumerations.PublicationStatus.ACTIVE); + + QuestionnaireItemComponent child1 = createItem(QuestionnaireItemType.GROUP); + resource.addItem(child1); + + QuestionnaireItemEnableWhenComponent enableWhen = new QuestionnaireItemEnableWhenComponent(); + enableWhen.setQuestion("q1"); + enableWhen.setHasAnswer(true); + child1.addEnableWhen(enableWhen); + + QuestionnaireItemComponent child21 = createItem(QuestionnaireItemType.STRING); + child1.addItem(child21); + + String inputXml = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource); + ourLog.info(inputXml); + + ValidationResult result = validateSchematron(resource); + assertTrue(result.isSuccessful()); + } + + @Test + public void enableWhenWithHasAnswerAndAnswer() { + Questionnaire resource = new Questionnaire(); + resource.setStatus(Enumerations.PublicationStatus.ACTIVE); + + QuestionnaireItemComponent child1 = createItem(QuestionnaireItemType.GROUP); + resource.addItem(child1); + + QuestionnaireItemEnableWhenComponent enableWhen = new QuestionnaireItemEnableWhenComponent(); + enableWhen.setQuestion("q1"); + enableWhen.setAnswer(new StringType("a value")); + enableWhen.setHasAnswer(true); + child1.addEnableWhen(enableWhen); + + QuestionnaireItemComponent child21 = createItem(QuestionnaireItemType.STRING); + child1.addItem(child21); + + String inputXml = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resource); + ourLog.info(inputXml); + + ValidationResult result = validateSchematron(resource); + assertFalse(result.isSuccessful()); + assertEquals(1, result.getMessages().size()); + assertThat(result.getMessages().get(0).getMessage(), containsString("que-7")); + } + + private QuestionnaireItemComponent createItem(QuestionnaireItemType type) { + QuestionnaireItemComponent item = new QuestionnaireItemComponent(); + item.setLinkId("id-" + linkIdCnt++); + item.setType(type); + return item; + } + + private ValidationResult validateSchematron(Questionnaire resource) { + FhirValidator val = ourCtx.newValidator(); + val.setValidateAgainstStandardSchema(false); + val.setValidateAgainstStandardSchematron(true); + ValidationResult result = val.validateWithResult(resource); + + String outcomeXml = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()); + ourLog.info(outcomeXml); + return result; + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } +} From f4fa233ad7578cce1f10ff83e353eb2360125874 Mon Sep 17 00:00:00 2001 From: Heinz-Dieter Conradi Date: Wed, 28 Feb 2018 13:34:14 +0100 Subject: [PATCH 2/3] remove superfluous import --- .../validation/SchematronValidationDstu3QuestionnaireTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/SchematronValidationDstu3QuestionnaireTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/SchematronValidationDstu3QuestionnaireTest.java index 1a7b7618090..f8438f4e4bc 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/SchematronValidationDstu3QuestionnaireTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/SchematronValidationDstu3QuestionnaireTest.java @@ -17,7 +17,6 @@ import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenCompone import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType; import org.hl7.fhir.dstu3.model.StringType; -import org.hamcrest.Matchers; import org.junit.AfterClass; import org.junit.Test; import org.slf4j.Logger; From 1a331838d0d46d2f935cedbf780842330c6617e8 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 5 Mar 2018 08:36:24 -0500 Subject: [PATCH 3/3] Add comment for #870 --- .../SchematronValidationR4QuestionnaireTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/SchematronValidationR4QuestionnaireTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/SchematronValidationR4QuestionnaireTest.java index af477c764f1..dc25b8338cf 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/SchematronValidationR4QuestionnaireTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/SchematronValidationR4QuestionnaireTest.java @@ -24,6 +24,9 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Added for #870 - Can be enabled when the FHIR sources are fixed + */ @Ignore("Requires a valid schematron file, e.g. bei merging pull request #869") public class SchematronValidationR4QuestionnaireTest { private static final Logger ourLog = LoggerFactory.getLogger(SchematronValidationR4QuestionnaireTest.class); @@ -65,7 +68,7 @@ public class SchematronValidationR4QuestionnaireTest { QuestionnaireItemEnableWhenComponent enableWhen = new QuestionnaireItemEnableWhenComponent(); enableWhen.setQuestion("q1"); - enableWhen.setHasAnswer(true); +// enableWhen.setHasAnswer(true); child1.addEnableWhen(enableWhen); QuestionnaireItemComponent child21 = createItem(QuestionnaireItemType.STRING); @@ -89,7 +92,7 @@ public class SchematronValidationR4QuestionnaireTest { QuestionnaireItemEnableWhenComponent enableWhen = new QuestionnaireItemEnableWhenComponent(); enableWhen.setQuestion("q1"); enableWhen.setAnswer(new StringType("a value")); - enableWhen.setHasAnswer(true); +// enableWhen.setHasAnswer(true); child1.addEnableWhen(enableWhen); QuestionnaireItemComponent child21 = createItem(QuestionnaireItemType.STRING);