Merge branch 'master' into reuse-subscription-channels

This commit is contained in:
Ken Stevens 2019-02-07 10:10:10 -05:00
commit b3e80f8da7
76 changed files with 924 additions and 150 deletions

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>

View File

@ -10,7 +10,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>

View File

@ -10,7 +10,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>

View File

@ -10,7 +10,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<artifactId>hapi-fhir-standalone-overlay-example</artifactId> <artifactId>hapi-fhir-standalone-overlay-example</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId> <artifactId>hapi-fhir-cli</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId> <artifactId>hapi-fhir-cli</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>
@ -16,14 +16,14 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>${project.version}</version>
</dependency> </dependency>
<!-- Server --> <!-- Server -->
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId> <artifactId>hapi-fhir-server</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
@ -35,43 +35,43 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId> <artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2.1</artifactId> <artifactId>hapi-fhir-structures-dstu2.1</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId> <artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId> <artifactId>hapi-fhir-structures-r4</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId> <artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId> <artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>${project.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>

View File

@ -32,16 +32,9 @@ import org.hl7.fhir.dstu3.model.ExpansionProfile.SystemVersionProcessingMode;
import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Enumeration;
import org.hl7.fhir.r4.model.Expression.ExpressionLanguage; import org.hl7.fhir.r4.model.Expression.ExpressionLanguage;
import org.hl7.fhir.r4.model.HealthcareService.HealthcareServiceEligibilityComponent; import org.hl7.fhir.r4.model.HealthcareService.HealthcareServiceEligibilityComponent;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.TerminologyCapabilities;
import org.hl7.fhir.r4.model.Type;
import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
@ -76,7 +69,7 @@ import org.hl7.fhir.utilities.Utilities;
public class VersionConvertor_30_40 { public class VersionConvertor_30_40 {
private static List<String> CANONICAL_URLS = new ArrayList<String>(); private static List<String> CANONICAL_URLS = new ArrayList<>();
static { static {
CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-conceptmap"); CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-conceptmap");
CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-valueset"); CANONICAL_URLS.add("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-valueset");
@ -16013,6 +16006,7 @@ public class VersionConvertor_30_40 {
tgt.setType(convertQuestionnaireItemType(src.getType())); tgt.setType(convertQuestionnaireItemType(src.getType()));
for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent t : src.getEnableWhen()) for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent t : src.getEnableWhen())
tgt.addEnableWhen(convertQuestionnaireItemEnableWhenComponent(t)); tgt.addEnableWhen(convertQuestionnaireItemEnableWhenComponent(t));
tgt.setEnableBehavior(Questionnaire.EnableWhenBehavior.ANY);
if (src.hasRequired()) if (src.hasRequired())
tgt.setRequired(src.getRequired()); tgt.setRequired(src.getRequired());
if (src.hasRepeats()) if (src.hasRepeats())
@ -16029,6 +16023,9 @@ public class VersionConvertor_30_40 {
tgt.addInitial().setValue(convertType(src.getInitial())); tgt.addInitial().setValue(convertType(src.getInitial()));
for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent t : src.getItem()) for (org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent t : src.getItem())
tgt.addItem(convertQuestionnaireItemComponent(t)); tgt.addItem(convertQuestionnaireItemComponent(t));
for (org.hl7.fhir.dstu3.model.Extension t : src.getModifierExtension()) {
tgt.addModifierExtension(convertExtension(t));
}
return tgt; return tgt;
} }
@ -16131,8 +16128,10 @@ public class VersionConvertor_30_40 {
tgt.setOperator(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOperator.EXISTS); tgt.setOperator(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOperator.EXISTS);
tgt.setAnswer(convertType(src.getHasAnswerElement())); tgt.setAnswer(convertType(src.getHasAnswerElement()));
} }
else if (src.hasAnswer()) else if (src.hasAnswer()) {
tgt.setAnswer(convertType(src.getAnswer())); tgt.setOperator(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOperator.EQUAL);
tgt.setAnswer(convertType(src.getAnswer()));
}
return tgt; return tgt;
} }

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -11,7 +11,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -144,6 +144,9 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
interceptor.getFinishedLatch().await(10, TimeUnit.SECONDS); interceptor.getFinishedLatch().await(10, TimeUnit.SECONDS);
ResourceDeliveryMessage lastDelivery = interceptor.getLastDelivery(); ResourceDeliveryMessage lastDelivery = interceptor.getLastDelivery();
assertTrue(lastDelivery.getAttribute("ATTR1").isPresent());
assertTrue(lastDelivery.getAttribute("ATTR2").isPresent());
assertTrue(lastDelivery.getAttribute("ATTRBLANK").isPresent());
assertEquals("Some value 1", lastDelivery.getAttribute("ATTR1").get()); assertEquals("Some value 1", lastDelivery.getAttribute("ATTR1").get());
assertEquals("Some value 2", lastDelivery.getAttribute("ATTR2").get()); assertEquals("Some value 2", lastDelivery.getAttribute("ATTR2").get());
assertEquals("", lastDelivery.getAttribute("ATTRBLANK").get()); assertEquals("", lastDelivery.getAttribute("ATTRBLANK").get());

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -10,7 +10,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -158,7 +158,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-converter</artifactId> <artifactId>hapi-fhir-converter</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -87,18 +87,19 @@ public class ElementsParameter implements IParameter {
if (theExclude) { if (theExclude) {
paramName = Constants.PARAM_ELEMENTS + Constants.PARAM_ELEMENTS_EXCLUDE_MODIFIER; paramName = Constants.PARAM_ELEMENTS + Constants.PARAM_ELEMENTS_EXCLUDE_MODIFIER;
} }
String[] summary = theRequest.getParameters().get(paramName); String[] elementsValues = theRequest.getParameters().get(paramName);
if (summary != null && summary.length > 0) { if (elementsValues != null && elementsValues.length > 0) {
Set<String> retVal = new HashSet<String>(); Set<String> retVal = new HashSet<>();
for (String next : summary) { for (String next : elementsValues) {
StringTokenizer tok = new StringTokenizer(next, ","); StringTokenizer tok = new StringTokenizer(next, ",");
while (tok.hasMoreTokens()) { while (tok.hasMoreTokens()) {
String token = tok.nextToken(); String token = tok.nextToken();
if (isNotBlank(token)) { if (isNotBlank(token)) {
if (token.contains(".") && standardMode) { if (token.contains("."))
continue; if (standardMode) {
} continue;
}
retVal.add(token); retVal.add(token);
} }
} }
@ -107,11 +108,6 @@ public class ElementsParameter implements IParameter {
return null; return null;
} }
// Always include the meta element even for subsetted values
if (!theExclude) {
retVal.add("meta");
}
return retVal; return retVal;
} }
return null; return null;

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId> <artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
</parent> </parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId> <artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId> <artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
</parent> </parent>
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId> <artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId> <artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
</parent> </parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId> <artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId> <artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
</parent> </parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jpa</artifactId> <artifactId>hapi-fhir-spring-boot-sample-server-jpa</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId> <artifactId>hapi-fhir-spring-boot</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
</parent> </parent>
<artifactId>hapi-fhir-spring-boot-samples</artifactId> <artifactId>hapi-fhir-spring-boot-samples</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -62,7 +62,7 @@ public class ElementsParamR4Test {
assertThat(responseContent, not(containsString("<div>THE DIV</div>"))); assertThat(responseContent, not(containsString("<div>THE DIV</div>")));
assertThat(responseContent, (containsString("family"))); assertThat(responseContent, (containsString("family")));
assertThat(responseContent, (containsString("maritalStatus"))); assertThat(responseContent, (containsString("maritalStatus")));
assertThat(ourLastElements, containsInAnyOrder("meta", "name", "maritalStatus")); assertThat(ourLastElements, containsInAnyOrder("name", "maritalStatus"));
} }
); );
} }
@ -77,7 +77,7 @@ public class ElementsParamR4Test {
assertThat(responseContent, not(containsString("<div>THE DIV</div>"))); assertThat(responseContent, not(containsString("<div>THE DIV</div>")));
assertThat(responseContent, (containsString("family"))); assertThat(responseContent, (containsString("family")));
assertThat(responseContent, not(containsString("maritalStatus"))); assertThat(responseContent, not(containsString("maritalStatus")));
assertThat(ourLastElements, containsInAnyOrder("meta", "name")); assertThat(ourLastElements, containsInAnyOrder("name"));
} }
); );
} }
@ -93,7 +93,7 @@ public class ElementsParamR4Test {
assertThat(responseContent, not(containsString("THE DIV"))); assertThat(responseContent, not(containsString("THE DIV")));
assertThat(responseContent, containsString("family")); assertThat(responseContent, containsString("family"));
assertThat(responseContent, containsString("maritalStatus")); assertThat(responseContent, containsString("maritalStatus"));
assertThat(ourLastElements, containsInAnyOrder("meta", "name", "maritalStatus")); assertThat(ourLastElements, containsInAnyOrder("name", "maritalStatus"));
} }
); );
} }
@ -108,7 +108,7 @@ public class ElementsParamR4Test {
assertThat(responseContent, containsString("THE DIV")); assertThat(responseContent, containsString("THE DIV"));
assertThat(responseContent, not(containsString("family"))); assertThat(responseContent, not(containsString("family")));
assertThat(responseContent, not(containsString("maritalStatus"))); assertThat(responseContent, not(containsString("maritalStatus")));
assertThat(ourLastElements, containsInAnyOrder("meta", "text")); assertThat(ourLastElements, containsInAnyOrder( "text"));
} }
); );
} }
@ -184,6 +184,34 @@ public class ElementsParamR4Test {
}); });
} }
/**
* A search on procedure, with only resource specific elements filters that are specifically
* on other resources but Procedure, should not affect the output of the procedure resource.
*/
@Test
public void testMultiResourceElementsFilterDoesntAffectFocalResource() throws IOException {
createProcedureWithLongChain();
verifyXmlAndJson(
"http://localhost:" + ourPort + "/Procedure?_include=*&_elements=Observation.subject",
bundle -> {
Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource();
assertEquals(true, procedure.getMeta().isEmpty());
assertEquals("REASON_CODE", procedure.getReasonCode().get(0).getCoding().get(0).getCode());
assertEquals("USED_CODE", procedure.getUsedCode().get(0).getCoding().get(0).getCode());
DiagnosticReport dr = (DiagnosticReport) bundle.getEntry().get(1).getResource();
assertEquals(true, dr.getMeta().isEmpty());
assertEquals(1, dr.getResult().size());
Observation obs = (Observation ) bundle.getEntry().get(2).getResource();
assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode());
assertEquals(null, obs.getStatus());
assertEquals(0, obs.getCode().getCoding().size());
assertEquals(false, obs.hasValue());
assertEquals("Patient/123", obs.getSubject().getReference());
});
}
@Test @Test
public void testMultiResourceElementsFilterWithMetadataExcludedStandardMode() throws IOException { public void testMultiResourceElementsFilterWithMetadataExcludedStandardMode() throws IOException {
ourServlet.setElementsSupport(ElementsSupportEnum.STANDARD); ourServlet.setElementsSupport(ElementsSupportEnum.STANDARD);

View File

@ -95,7 +95,7 @@ public class SearchR4Test {
linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl(); linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl();
assertThat(linkSelf, containsString("_elements=name")); assertThat(linkSelf, containsString("_elements=name"));
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_elements=meta,name")); assertThat(linkNext, containsString("_elements=name"));
ourLog.info(toJson(bundle)); ourLog.info(toJson(bundle));
@ -104,7 +104,7 @@ public class SearchR4Test {
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
assertThat(toJson(bundle), not(containsString("\"active\""))); assertThat(toJson(bundle), not(containsString("\"active\"")));
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_elements=meta,name")); assertThat(linkNext, containsString("_elements=name"));
assertThat(linkNext, containsString("_elements:exclude=active,birthDate")); assertThat(linkNext, containsString("_elements:exclude=active,birthDate"));
// Fetch the next page // Fetch the next page
@ -112,7 +112,7 @@ public class SearchR4Test {
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
assertThat(toJson(bundle), not(containsString("\"active\""))); assertThat(toJson(bundle), not(containsString("\"active\"")));
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_elements=meta,name")); assertThat(linkNext, containsString("_elements=name"));
assertThat(linkNext, containsString("_elements:exclude=active,birthDate")); assertThat(linkNext, containsString("_elements:exclude=active,birthDate"));
// Fetch the next page // Fetch the next page
@ -120,7 +120,7 @@ public class SearchR4Test {
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
assertThat(toJson(bundle), not(containsString("\"active\""))); assertThat(toJson(bundle), not(containsString("\"active\"")));
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
assertThat(linkNext, containsString("_elements=meta,name")); assertThat(linkNext, containsString("_elements=name"));
assertThat(linkNext, containsString("_elements:exclude=active,birthDate")); assertThat(linkNext, containsString("_elements:exclude=active,birthDate"));
} }

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -31,6 +31,8 @@ import org.hl7.fhir.r4.utils.INarrativeGenerator;
import org.hl7.fhir.r4.utils.IResourceValidator; import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r4.utils.IResourceValidator.BestPracticeWarningLevel; import org.hl7.fhir.r4.utils.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.r4.utils.IResourceValidator.IdStatus; import org.hl7.fhir.r4.utils.IResourceValidator.IdStatus;
import org.hl7.fhir.r4.validation.DefaultEnableWhenEvaluator;
import org.hl7.fhir.r4.validation.IEnableWhenEvaluator;
import org.hl7.fhir.r4.validation.InstanceValidator; import org.hl7.fhir.r4.validation.InstanceValidator;
import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
@ -47,6 +49,7 @@ import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@SuppressWarnings({"PackageAccessibility", "Duplicates"}) @SuppressWarnings({"PackageAccessibility", "Duplicates"})
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule { public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
@ -60,6 +63,9 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
private IValidationSupport myValidationSupport; private IValidationSupport myValidationSupport;
private boolean noTerminologyChecks = false; private boolean noTerminologyChecks = false;
private volatile WorkerContextWrapper myWrappedWorkerContext; private volatile WorkerContextWrapper myWrappedWorkerContext;
private Function<IWorkerContext, IEnableWhenEvaluator> enableWhenEvaluatorSupplier;
private boolean errorForUnknownProfiles;
private List<String> extensionDomains = Collections.emptyList(); private List<String> extensionDomains = Collections.emptyList();
/** /**
@ -80,6 +86,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
myDocBuilderFactory = DocumentBuilderFactory.newInstance(); myDocBuilderFactory = DocumentBuilderFactory.newInstance();
myDocBuilderFactory.setNamespaceAware(true); myDocBuilderFactory.setNamespaceAware(true);
myValidationSupport = theValidationSupport; myValidationSupport = theValidationSupport;
setEnableWhenEvaluatorSupplier(ctx -> new DefaultEnableWhenEvaluator());
} }
/** /**
@ -221,6 +228,14 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
public boolean isAnyExtensionsAllowed() { public boolean isAnyExtensionsAllowed() {
return myAnyExtensionsAllowed; return myAnyExtensionsAllowed;
} }
public boolean isErrorForUnknownProfiles() {
return errorForUnknownProfiles;
}
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles) {
this.errorForUnknownProfiles = errorForUnknownProfiles;
}
/** /**
* If set to {@literal true} (default is true) extensions which are not known to the * If set to {@literal true} (default is true) extensions which are not known to the
@ -237,6 +252,14 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
public boolean isNoTerminologyChecks() { public boolean isNoTerminologyChecks() {
return noTerminologyChecks; return noTerminologyChecks;
} }
/**
* Sets a customized {@link IEnableWhenEvaluator} which is injected to created InstanceValidators
*/
public void setEnableWhenEvaluatorSupplier(
Function<IWorkerContext, IEnableWhenEvaluator> enableWhenEvaluatorSupplier) {
this.enableWhenEvaluatorSupplier = enableWhenEvaluatorSupplier;
}
/** /**
* If set to {@literal true} (default is false) the valueSet will not be validate * If set to {@literal true} (default is false) the valueSet will not be validate
@ -270,6 +293,8 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed()); v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
v.setResourceIdRule(IdStatus.OPTIONAL); v.setResourceIdRule(IdStatus.OPTIONAL);
v.setNoTerminologyChecks(isNoTerminologyChecks()); v.setNoTerminologyChecks(isNoTerminologyChecks());
v.setEnableWhenEvaluator(enableWhenEvaluatorSupplier.apply(wrappedWorkerContext));
v.setErrorForUnknownProfiles(isErrorForUnknownProfiles());
v.getExtensionDomains().addAll(extensionDomains); v.getExtensionDomains().addAll(extensionDomains);
List<ValidationMessage> messages = new ArrayList<>(); List<ValidationMessage> messages = new ArrayList<>();

View File

@ -2539,6 +2539,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress); validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress);
else else
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId()); rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId());
} }
} }

View File

@ -390,3 +390,4 @@ public class QuestionnaireResponseValidator extends BaseValidator {
return allowedAnswerTypes; return allowedAnswerTypes;
} }
} }

View File

@ -0,0 +1,48 @@
package org.hl7.fhir.instance.validation;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent;
public class EnableWhenResult {
private final boolean enabled;
private final QuestionnaireItemEnableWhenComponent enableWhenCondition;
private final Element answerItem;
private final String linkId;
/**
* Evaluation result of enableWhen condition
*
* @param enabled
* Evaluation result
* @param linkId
* LinkId of the questionnaire item
* @param enableWhenCondition
* Evaluated enableWhen condition
* @param answerItem
* item in QuestionnaireResponse
*/
public EnableWhenResult(boolean enabled, String linkId, QuestionnaireItemEnableWhenComponent enableWhenCondition,
Element answerItem) {
this.enabled = enabled;
this.linkId = linkId;
this.answerItem = answerItem;
this.enableWhenCondition = enableWhenCondition;
}
public boolean isEnabled() {
return enabled;
}
public String getLinkId() {
return linkId;
}
public Element getAnswerItem() {
return answerItem;
}
public QuestionnaireItemEnableWhenComponent getEnableWhenCondition() {
return enableWhenCondition;
}
}

View File

@ -0,0 +1,216 @@
package org.hl7.fhir.r4.validation;
import java.util.*;
import java.util.stream.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Questionnaire.*;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
/**
* Evaluates Questionnaire.item.enableWhen against a QuestionnaireResponse.
* Ignores possible modifierExtensions and extensions.
*
*/
public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
public static final String LINKID_ELEMENT = "linkId";
public static final String ITEM_ELEMENT = "item";
public static final String ANSWER_ELEMENT = "answer";
@Override
public boolean isQuestionEnabled(QuestionnaireItemComponent questionnaireItem, Element questionnaireResponse) {
if (!questionnaireItem.hasEnableWhen()) {
return true;
}
List<EnableWhenResult> evaluationResults = questionnaireItem.getEnableWhen()
.stream()
.map(enableCondition -> evaluateCondition(enableCondition, questionnaireResponse,
questionnaireItem.getLinkId()))
.collect(Collectors.toList());
return checkConditionResults(evaluationResults, questionnaireItem);
}
public boolean checkConditionResults(List<EnableWhenResult> evaluationResults,
QuestionnaireItemComponent questionnaireItem) {
if (questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ANY){
return evaluationResults.stream().anyMatch(EnableWhenResult::isEnabled);
} if (questionnaireItem.hasEnableBehavior() && questionnaireItem.getEnableBehavior() == EnableWhenBehavior.ALL){
return evaluationResults.stream().allMatch(EnableWhenResult::isEnabled);
}
//TODO: Throw exception? enableBehavior is mandatory when there are multiple conditions
return true;
}
protected EnableWhenResult evaluateCondition(QuestionnaireItemEnableWhenComponent enableCondition,
Element questionnaireResponse, String linkId) {
//TODO: Fix EnableWhenResult stuff
List<Element> answerItems = findQuestionAnswers(questionnaireResponse,
enableCondition.getQuestion());
QuestionnaireItemOperator operator = enableCondition.getOperator();
if (operator == QuestionnaireItemOperator.EXISTS){
Type answer = enableCondition.getAnswer();
if (!(answer instanceof BooleanType)){
throw new UnprocessableEntityException("Exists-operator requires answerBoolean");
}
return new EnableWhenResult(((BooleanType)answer).booleanValue() != answerItems.isEmpty(),
linkId, enableCondition, questionnaireResponse);
}
boolean result = answerItems
.stream()
.anyMatch(answer -> evaluateAnswer(answer, enableCondition.getAnswer(), enableCondition.getOperator()));
return new EnableWhenResult(result, linkId, enableCondition, questionnaireResponse);
}
public Type convertToType(Element element) {
Type b = new Factory().create(element.fhirType());
if (b instanceof PrimitiveType) {
((PrimitiveType<?>) b).setValueAsString(element.primitiveValue());
} else {
for (Element child : element.getChildren()) {
if (!isExtension(child)) {
b.setProperty(child.getName(), convertToType(child));
}
}
}
return b;
}
private boolean isExtension(Element element) {
return "Extension".equals(element.fhirType());
}
protected boolean evaluateAnswer(Element answer, Type expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
Type actualAnswer;
if (isExtension(answer)) {
return false;
}
try {
actualAnswer = convertToType(answer);
} catch (FHIRException e) {
throw new UnprocessableEntityException("Unexpected answer type", e);
}
if (!actualAnswer.getClass().equals(expectedAnswer.getClass())) {
throw new UnprocessableEntityException("Expected answer and actual answer have incompatible types");
}
if (expectedAnswer instanceof Coding) {
return compareCodingAnswer((Coding)expectedAnswer, (Coding)actualAnswer, questionnaireItemOperator);
} else if ((expectedAnswer instanceof PrimitiveType)) {
return comparePrimitiveAnswer((PrimitiveType<?>)actualAnswer, (PrimitiveType<?>)expectedAnswer, questionnaireItemOperator);
} else if (expectedAnswer instanceof Quantity) {
return compareQuantityAnswer((Quantity)actualAnswer, (Quantity)expectedAnswer, questionnaireItemOperator);
}
// TODO: Attachment, reference?
throw new UnprocessableEntityException("Unimplemented answer type: " + expectedAnswer.getClass());
}
private boolean compareQuantityAnswer(Quantity actualAnswer, Quantity expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
return compareComparable(actualAnswer.getValue(), expectedAnswer.getValue(), questionnaireItemOperator);
}
private boolean comparePrimitiveAnswer(PrimitiveType<?> actualAnswer, PrimitiveType<?> expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
if (actualAnswer.getValue() instanceof Comparable){
return compareComparable((Comparable)actualAnswer.getValue(), (Comparable) expectedAnswer.getValue(), questionnaireItemOperator);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return actualAnswer.equalsShallow(expectedAnswer);
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return !actualAnswer.equalsShallow(expectedAnswer);
}
throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison");
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private boolean compareComparable(Comparable actual, Comparable expected,
QuestionnaireItemOperator questionnaireItemOperator) {
int result = actual.compareTo(expected);
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return result == 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return result != 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_OR_EQUAL){
return result >= 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_OR_EQUAL){
return result <= 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_THAN){
return result < 0;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_THAN){
return result > 0;
}
throw new UnprocessableEntityException("Bad operator for PrimitiveType comparison");
}
private List<Element> findQuestionAnswers(Element questionnaireResponse, String question) {
List<Element> matchingItems = questionnaireResponse.getChildren(ITEM_ELEMENT)
.stream()
.flatMap(i -> findSubItems(i).stream())
.filter(i -> hasLinkId(i, question))
.collect(Collectors.toList());
return matchingItems
.stream()
.flatMap(e -> extractAnswer(e).stream())
.collect(Collectors.toList());
}
private List<Element> extractAnswer(Element item) {
return item.getChildrenByName(ANSWER_ELEMENT)
.stream()
.flatMap(c -> c.getChildren().stream())
.collect(Collectors.toList());
}
private boolean compareCodingAnswer(Coding expectedAnswer, Coding actualAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
boolean result = compareSystems(expectedAnswer, actualAnswer) && compareCodes(expectedAnswer, actualAnswer);
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
return result == true;
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
return result == false;
}
throw new UnprocessableEntityException("Bad operator for Coding comparison");
}
private boolean compareCodes(Coding expectedCoding, Coding value) {
if (expectedCoding.hasCode() != value.hasCode()) {
return false;
}
if (expectedCoding.hasCode()) {
return expectedCoding.getCode().equals(value.getCode());
}
return true;
}
private boolean compareSystems(Coding expectedCoding, Coding value) {
if (expectedCoding.hasSystem() && !value.hasSystem()) {
return false;
}
if (expectedCoding.hasSystem()) {
return expectedCoding.getSystem().equals(value.getSystem());
}
return true;
}
private List<Element> findSubItems(Element item) {
List<Element> results = item.getChildren(LINKID_ELEMENT)
.stream()
.flatMap(i -> findSubItems(i).stream())
.collect(Collectors.toList());
results.add(item);
return results;
}
private boolean hasLinkId(Element item, String linkId) {
Element linkIdChild = item.getNamedChild(LINKID_ELEMENT);
if (linkIdChild != null && linkIdChild.getValue().equals(linkId)){
return true;
}
return false;
}
}

View File

@ -0,0 +1,48 @@
package org.hl7.fhir.r4.validation;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent;
public class EnableWhenResult {
private final boolean enabled;
private final QuestionnaireItemEnableWhenComponent enableWhenCondition;
private final Element answerItem;
private final String linkId;
/**
* Evaluation result of enableWhen condition
*
* @param enabled
* Evaluation result
* @param linkId
* LinkId of the questionnaire item
* @param enableWhenCondition
* Evaluated enableWhen condition
* @param responseItem
* item in QuestionnaireResponse
*/
public EnableWhenResult(boolean enabled, String linkId, QuestionnaireItemEnableWhenComponent enableWhenCondition,
Element answerItem) {
this.enabled = enabled;
this.linkId = linkId;
this.answerItem = answerItem;
this.enableWhenCondition = enableWhenCondition;
}
public boolean isEnabled() {
return enabled;
}
public String getLinkId() {
return linkId;
}
public Element getAnswerItem() {
return answerItem;
}
public QuestionnaireItemEnableWhenComponent getEnableWhenCondition() {
return enableWhenCondition;
}
}

View File

@ -0,0 +1,10 @@
package org.hl7.fhir.r4.validation;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
public interface IEnableWhenEvaluator {
public boolean isQuestionEnabled(QuestionnaireItemComponent questionnaireItem,
Element questionnaireResponse);
}

View File

@ -15,6 +15,8 @@ import java.util.UUID;
import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.exceptions.*;
import org.hl7.fhir.convertors.VersionConvertorConstants; import org.hl7.fhir.convertors.VersionConvertorConstants;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
@ -254,6 +256,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private IEvaluationContext externalHostServices; private IEvaluationContext externalHostServices;
private boolean noExtensibleWarnings; private boolean noExtensibleWarnings;
private String serverBase; private String serverBase;
private IEnableWhenEvaluator myEnableWhenEvaluator = new DefaultEnableWhenEvaluator();
/* /*
* Keeps track of whether a particular profile has been checked or not yet * Keeps track of whether a particular profile has been checked or not yet
@ -2464,6 +2468,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
public void setAllowXsiLocation(boolean allowXsiLocation) { public void setAllowXsiLocation(boolean allowXsiLocation) {
this.allowXsiLocation = allowXsiLocation; this.allowXsiLocation = allowXsiLocation;
} }
public void setEnableWhenEvaluator(IEnableWhenEvaluator myEnableWhenEvaluator) {
this.myEnableWhenEvaluator = myEnableWhenEvaluator;
}
/** /**
* *
@ -2734,21 +2742,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
sdTime = sdTime + (System.nanoTime() - t); sdTime = sdTime + (System.nanoTime() - t);
if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire \""+questionnaire+"\" could not be resolved, so no validation can be performed against the base questionnaire")) { if (warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, "The questionnaire \""+questionnaire+"\" could not be resolved, so no validation can be performed against the base questionnaire")) {
boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); boolean inProgress = "in-progress".equals(element.getNamedChildValue("status"));
validateQuestionannaireResponseItems(qsrc, qsrc.getItem(), errors, element, stack, inProgress); validateQuestionannaireResponseItems(qsrc, qsrc.getItem(), errors, element, stack, inProgress, element);
} }
} }
} }
private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress) { private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) {
String text = element.getNamedChildValue("text"); String text = element.getNamedChildValue("text");
rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId "+qItem.getLinkId()); rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), "If text exists, it must match the questionnaire definition for linkId "+qItem.getLinkId());
List<Element> answers = new ArrayList<Element>(); List<Element> answers = new ArrayList<Element>();
element.getNamedChildren("answer", answers); element.getNamedChildren("answer", answers);
if (inProgress) if (inProgress)
warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId()); warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item "+qItem.getLinkId());
else else
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId()); rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item "+qItem.getLinkId());
if (answers.size() > 1) if (answers.size() > 1)
rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed"); rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed");
@ -2823,7 +2831,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// no validation // no validation
break; break;
} }
validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, answer, stack, inProgress); validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot);
} }
if (qItem.getType() == null) { if (qItem.getType() == null) {
fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "Definition for item "+qItem.getLinkId() + " does not contain a type"); fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, "Definition for item "+qItem.getLinkId() + " does not contain a type");
@ -2832,16 +2840,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
element.getNamedChildren("item", items); element.getNamedChildren("item", items);
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), "Items not of type DISPLAY should not have items - linkId {0}", qItem.getLinkId()); rule(errors, IssueType.STRUCTURE, element.line(), element.col(), stack.getLiteralPath(), items.isEmpty(), "Items not of type DISPLAY should not have items - linkId {0}", qItem.getLinkId());
} else { } else {
validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, element, stack, inProgress); validateQuestionannaireResponseItems(qsrc, qItem.getItem(), errors, element, stack, inProgress, questionnaireResponseRoot);
} }
} }
private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, List<Element> elements, NodeStack stack, boolean inProgress) { private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, List<Element> answers) {
return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP;
}
private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List<ValidationMessage> errors, List<Element> elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) {
if (elements.size() > 1) if (elements.size() > 1)
rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId());
for (Element element : elements) { for (Element element : elements) {
NodeStack ns = stack.push(element, -1, null, null); NodeStack ns = stack.push(element, -1, null, null);
validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress); validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot);
} }
} }
@ -2852,8 +2864,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
return -1; return -1;
} }
private void validateQuestionannaireResponseItems(Questionnaire qsrc, List<QuestionnaireItemComponent> qItems, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress) { private void validateQuestionannaireResponseItems(Questionnaire qsrc, List<QuestionnaireItemComponent> qItems, List<ValidationMessage> errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) {
List<Element> items = new ArrayList<Element>(); List<Element> items = new ArrayList<Element>();
element.getNamedChildren("item", items); element.getNamedChildren("item", items);
// now, sort into stacks // now, sort into stacks
@ -2866,9 +2878,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (index == -1) { if (index == -1) {
QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId); QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId);
if (qItem != null) { if (qItem != null) {
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, "Structural Error: item is in the wrong place"); rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem));
NodeStack ns = stack.push(item, -1, null, null); NodeStack ns = stack.push(item, -1, null, null);
validateQuestionannaireResponseItem(qsrc, qItem, errors, element, ns, inProgress); validateQuestionannaireResponseItem(qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot);
} }
else else
rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \""+linkId+"\" not found in questionnaire"); rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, "LinkId \""+linkId+"\" not found in questionnaire");
@ -2877,11 +2889,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
{ {
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order"); rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index >= lastIndex, "Structural Error: items are out of order");
lastIndex = index; lastIndex = index;
List<Element> mapItem = map.get(linkId);
if (mapItem == null) { List<Element> mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>());
mapItem = new ArrayList<Element>();
map.put(linkId, mapItem);
}
mapItem.add(item); mapItem.add(item);
} }
} }
@ -2890,13 +2900,25 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// ok, now we have a list of known items, grouped by linkId. We"ve made an error for anything out of order // ok, now we have a list of known items, grouped by linkId. We"ve made an error for anything out of order
for (QuestionnaireItemComponent qItem : qItems) { for (QuestionnaireItemComponent qItem : qItems) {
List<Element> mapItem = map.get(qItem.getLinkId()); List<Element> mapItem = map.get(qItem.getLinkId());
if (mapItem != null) if (mapItem != null){
validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress); rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), myEnableWhenEvaluator.isQuestionEnabled(qItem, questionnaireResponseRoot), "Item has answer, even though it is not enabled "+qItem.getLinkId());
else validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress, questionnaireResponseRoot);
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId()); } else {
//item is missing, is the question enabled?
if (myEnableWhenEvaluator.isQuestionEnabled(qItem, questionnaireResponseRoot)) {
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId());
}
}
} }
} }
private String misplacedItemError(QuestionnaireItemComponent qItem) {
return qItem.hasLinkId() ?
String.format("Structural Error: item with linkid %s is in the wrong place", qItem.getLinkId())
:
"Structural Error: item is in the wrong place";
}
private void validateQuestionnaireResponseItemQuantity( List<ValidationMessage> errors, Element answer, NodeStack stack) { private void validateQuestionnaireResponseItemQuantity( List<ValidationMessage> errors, Element answer, NodeStack stack) {
} }

View File

@ -0,0 +1,47 @@
package org.hl7.fhir.dstu3.hapi.validation;
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
public class EnableWhenResult {
private final boolean enabled;
private final QuestionnaireItemEnableWhenComponent enableWhenCondition;
private final QuestionnaireResponseItemComponent responseItem;
private final String linkId;
/**
* Evaluation result of enableWhen condition
*
* @param enabled
* Evaluation result
* @param linkId
* LinkId of the questionnaire item
* @param enableWhenCondition
* Evaluated enableWhen condition
* @param responseItem
* item in QuestionnaireResponse
*/
public EnableWhenResult(boolean enabled, String linkId, QuestionnaireItemEnableWhenComponent enableWhenCondition,
QuestionnaireResponseItemComponent responseItem) {
this.enabled = enabled;
this.linkId = linkId;
this.responseItem = responseItem;
this.enableWhenCondition = enableWhenCondition;
}
public boolean isEnabled() {
return enabled;
}
public String getLinkId() {
return linkId;
}
public QuestionnaireResponseItemComponent getResponseItem() {
return responseItem;
}
public QuestionnaireItemEnableWhenComponent getEnableWhenCondition() {
return enableWhenCondition;
}
}

View File

@ -7,35 +7,37 @@ import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult; import ca.uhn.fhir.validation.ValidationResult;
import org.apache.commons.io.IOUtils;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
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;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent;
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemOptionComponent; import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemOptionComponent;
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType; import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent; import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.BOOLEAN;
import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.CHOICE;
import static org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -43,23 +45,26 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
public class QuestionnaireResponseValidatorDstu3Test { public class QuestionnaireResponseValidatorDstu3Test {
private static final String QUESTIONNAIRE_URL = "http://example.com/Questionnaire/q1";
public static final IdType ID_ICC_QUESTIONNAIRE_SETUP = new IdType("Questionnaire/profile"); 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 org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QuestionnaireResponseValidatorDstu3Test.class);
private static final String CODE_ICC_SCHOOLTYPE_PT = "PT"; private static final String CODE_ICC_SCHOOLTYPE_PT = "PT";
private static final IdType ID_VS_SCHOOLTYPE = new IdType("ValueSet/schooltype"); 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 final String SYSTEMURI_ICC_SCHOOLTYPE = "http://ehealthinnovation/icc/ns/schooltype";
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(); private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport();
private static FhirContext ourCtx = FhirContext.forDstu3(); private static FhirContext ourCtx;
private FhirInstanceValidator myInstanceVal; private FhirInstanceValidator myInstanceVal;
private FhirValidator myVal; private FhirValidator myVal;
private IValidationSupport myValSupport; private IValidationSupport myValSupport;
private IWorkerContext myWorkerCtx;
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu3();
}
@Before @Before
public void before() { public void before() {
myValSupport = mock(IValidationSupport.class); myValSupport = mock(IValidationSupport.class);
// new DefaultProfileValidationSupport();
myWorkerCtx = new HapiWorkerContext(ourCtx, myValSupport);
myVal = ourCtx.newValidator(); myVal = ourCtx.newValidator();
myVal.setValidateAgainstStandardSchema(false); myVal.setValidateAgainstStandardSchema(false);
@ -128,7 +133,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
answerValues[11] = new Coding().setSystem("http://codesystems.com/system").setCode("code0"); answerValues[11] = new Coding().setSystem("http://codesystems.com/system").setCode("code0");
answerValues[12] = new StringType("some value"); answerValues[12] = new StringType("some value");
answerValues[13] = new Attachment().setData("some data".getBytes()).setContentType("txt"); answerValues[13] = new Attachment().setData("some data".getBytes()).setContentType("txt");
answerValues[14] = new Reference("http://example.com/Questionnaire/q1"); answerValues[14] = new Reference(QUESTIONNAIRE_URL);
answerValues[15] = new Quantity(42); answerValues[15] = new Quantity(42);
for (int i = 0; i < itemCnt; i++) { for (int i = 0; i < itemCnt; i++) {
@ -138,7 +143,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
reset(myValSupport); reset(myValSupport);
Questionnaire q = new Questionnaire(); Questionnaire q = new Questionnaire();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class),
eq("http://example.com/Questionnaire/q1"))).thenReturn(q); eq(QUESTIONNAIRE_URL))).thenReturn(q);
when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq("http://codesystems.com/system"))).thenReturn(codeSystem); when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq("http://codesystems.com/system"))).thenReturn(codeSystem);
when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options); when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options);
myInstanceVal.flushCaches(); myInstanceVal.flushCaches();
@ -155,7 +160,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
QuestionnaireResponse qa = new QuestionnaireResponse(); QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.INPROGRESS); qa.setStatus(QuestionnaireResponseStatus.INPROGRESS);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qa.addItem().setLinkId(linkId).addAnswer().setValue(answerValues[i]); qa.addItem().setLinkId(linkId).addAnswer().setValue(answerValues[i]);
ValidationResult errors = myVal.validateWithResult(qa); ValidationResult errors = myVal.validateWithResult(qa);
@ -171,7 +176,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
QuestionnaireResponse qa = new QuestionnaireResponse(); QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q);
@ -184,11 +189,11 @@ public class QuestionnaireResponseValidatorDstu3Test {
@Test @Test
public void testCodedAnswer() { public void testCodedAnswer() {
String questionnaireRef = "http://example.com/Questionnaire/q1"; String questionnaireRef = QUESTIONNAIRE_URL;
Questionnaire q = new Questionnaire(); Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setOptions(new Reference("http://somevalueset")); q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setOptions(new Reference("http://somevalueset"));
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq("http://example.com/Questionnaire/q1"))).thenReturn(q); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(QUESTIONNAIRE_URL))).thenReturn(q);
CodeSystem codeSystem = new CodeSystem(); CodeSystem codeSystem = new CodeSystem();
codeSystem.setContent(CodeSystemContentMode.COMPLETE); codeSystem.setContent(CodeSystemContentMode.COMPLETE);
@ -252,7 +257,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
QuestionnaireResponse qa = new QuestionnaireResponse(); QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
QuestionnaireResponseItemComponent qaGroup = qa.addItem(); QuestionnaireResponseItemComponent qaGroup = qa.addItem();
qaGroup.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); qaGroup.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
@ -262,6 +267,43 @@ public class QuestionnaireResponseValidatorDstu3Test {
ourLog.info(errors.toString()); ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No LinkId, so can't be validated")); assertThat(errors.toString(), containsString("No LinkId, so can't be validated"));
} }
@Test
public void testMissingAnswerInNestedStructureIsReported() throws Exception {
Questionnaire q = new Questionnaire();
q.addItem().setType(QuestionnaireItemType.GROUP).setRequired(true)
.addItem().setType(QuestionnaireItemType.GROUP).setRequired(true)
.addItem().setType(QuestionnaireItemType.BOOLEAN).setLinkId("link0").setRequired(true);
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
assertThat(errors.toString(), Matchers.not(containsString("No issues")));
}
@Test
public void testGroupMarkedAsRequiredIsOk() throws Exception {
Questionnaire q = new Questionnaire();
q.addItem().setType(QuestionnaireItemType.GROUP).setRequired(true).setLinkId("link1")
.addItem().setType(QuestionnaireItemType.BOOLEAN).setLinkId("link0").setRequired(true);
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qa.addItem().setLinkId("link1")
.addItem().setLinkId("link0").addAnswer().setValue(new BooleanType(true));
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
assertThat(errors.toString(), containsString("No issues"));
}
@Test @Test
public void testItemWithNoType() { public void testItemWithNoType() {
@ -272,7 +314,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
QuestionnaireResponse qa = new QuestionnaireResponse(); QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
QuestionnaireResponseItemComponent qaGroup = qa.addItem().setLinkId("link0"); QuestionnaireResponseItemComponent qaGroup = qa.addItem().setLinkId("link0");
qaGroup.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); qaGroup.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO"));
@ -293,7 +335,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
QuestionnaireResponse qa = new QuestionnaireResponse(); QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO"));
String reference = qa.getQuestionnaire().getReference(); String reference = qa.getQuestionnaire().getReference();
@ -303,10 +345,271 @@ public class QuestionnaireResponseValidatorDstu3Test {
ourLog.info(errors.toString()); ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No response found for required item link0")); assertThat(errors.toString(), containsString("No response found for required item link0"));
} }
@Test
public void testEnableWhenWithHasAnswerTrueDisablesQuestionWhenNoAnswerIsPresent() {
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING);
q.addItem().setLinkId("link1").setRequired(true).addEnableWhen().setQuestion("link0").setHasAnswer(true);
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
String reference = qa.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No issues"));
}
@Test
public void testRequiredQuestionQuantityWithEnableWhenHidesQuestionHasAnswerTrue() {
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.QUANTITY);
//link1 question is enabled when link0 has answer
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
item1.setLinkId("link1").setRequired(true);
q.addItem(item1);
QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent();
item1.addEnableWhen(enable);
enable.setQuestion("link0");
enable.setHasAnswer(true);
enable.setAnswer(new Quantity().setValue(1L));
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
String reference = qa.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No issues"));
}
@Test
public void testRequiredQuestionQuantityWithEnableWhenHidesQuestionValue() {
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.QUANTITY);
//link1 question is enabled when link0 has answer
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
item1.setLinkId("link1").setRequired(true);
q.addItem(item1);
QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent();
item1.addEnableWhen(enable);
enable.setQuestion("link0");
enable.setAnswer(new Quantity().setValue(2L));
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qa.addItem().setLinkId("link0").addAnswer().setValue(new Quantity().setValue(1L));
String reference = qa.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No issues"));
}
@Test
public void testRequiredQuestionQuantityWithEnableWhenEnablesQuestionValue() {
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.QUANTITY);
//link1 question is enabled when link0 has answer
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
item1.setLinkId("link1").setRequired(true);
q.addItem(item1);
QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent();
item1.addEnableWhen(enable);
enable.setQuestion("link0");
enable.setAnswer(new Quantity().setValue(1L));
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qa.addItem().setLinkId("link0").addAnswer().setValue(new Quantity().setValue(1L));
String reference = qa.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No response found for required item link1"));
}
@Test
public void testRequiredQuestionWithEnableWhenHasAnswerTrueWithAnswer() {
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING);
// create the questionnaire
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
item1.setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING);
q.addItem(item1);
QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent();
item1.addEnableWhen(enable);
enable.setQuestion("link0");
enable.setHasAnswer(true);
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("BAR"));
String reference = qa.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No issues"));
}
@Test
public void testRequiredQuestionWithEnableWheHidesRequiredQuestionnHasAnswerFalse() {
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING);
// create the questionnaire
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
item1.setLinkId("link1").setRequired(true);
q.addItem(item1);
QuestionnaireItemEnableWhenComponent enable = new QuestionnaireItemEnableWhenComponent();
item1.addEnableWhen(enable);
enable.setQuestion("link0");
enable.setHasAnswer(false);
QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
// link1 should be disabled, because the enableWhen enables it when link0 doesn't haven an answer
qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO"));
String reference = qa.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("No issues"));
}
@Test
public void testGivenQuestionIsNotEnabledWithEnableWhenAnswersAreReportedAsErrors() throws Exception {
Questionnaire q = new Questionnaire();
q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.STRING);
q.addItem().setLinkId("link2").setRequired(false).setType(QuestionnaireItemType.STRING).addEnableWhen().setQuestion("link0").setHasAnswer(true);
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.setStatus(QuestionnaireResponseStatus.COMPLETED);
qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qr.addItem().setLinkId("link2").addAnswer().setValue(new StringType("FOO"));
String reference = qr.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qr);
assertThat(errors.toString(), Matchers.not(containsString("No issues")));
}
@Test
public void testGivenQuestionnaireResponseHasSiblingItemsWhenTheyShouldBeChildItems() throws Exception {
Questionnaire q = new Questionnaire();
QuestionnaireItemComponent item = q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.GROUP);
item.addItem().setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING);
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.setStatus(QuestionnaireResponseStatus.COMPLETED);
qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qr.addItem().setLinkId("link0").setText("Text");
qr.addItem().setLinkId("link1").addAnswer().setValue(new StringType("Answer"));
String reference = qr.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qr);
assertThat(errors.toString(), Matchers.not(containsString("No issues")));
assertTrue("Must contain structural error about misplaced link1 item",
errors.getMessages().stream().filter(vm -> vm.getMessage().contains("Structural Error"))
.anyMatch(vm -> vm.getMessage().contains("link1")));
}
@Test
public void testAnswerIsValueCodingWithExtensionInside() throws Exception {
Questionnaire q = new Questionnaire();
Coding qcoding = new Coding();
qcoding.setCode("1293");
q.addItem().setLinkId("1B").setRequired(true).setType(CHOICE).addOption().setValue(qcoding);
q.addItem().setLinkId("2B").setType(BOOLEAN).addEnableWhen().setQuestion("1B").setAnswer(qcoding);
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.setStatus(COMPLETED);
qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
QuestionnaireResponseItemComponent qrItem = qr.addItem().setLinkId("1B");
Coding coding = new Coding();
coding.setCode("1293");
QuestionnaireResponseItemAnswerComponent answer = qrItem.addAnswer();
answer.setValue(coding);
coding.addExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", new BooleanType(true));
qr.addItem().setLinkId("2B").addAnswer().setValue(new BooleanType(true));
String reference = qr.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qr);
assertThat(errors.toString(), containsString("No issues"));
}
@Test
public void testChoiceItemsEnableWhenHasNoSystemYetAnswerHasSystem() throws Exception {
Questionnaire q = new Questionnaire();
Coding qcoding = new Coding();
qcoding.setCode("male");
qcoding.setSystem("http://hl7.org/fhir/administrative-gender");
q.addItem().setLinkId("1B").setRequired(true).setType(CHOICE).addOption().setValue(qcoding);
Coding enablewhenCoding = new Coding();
enablewhenCoding.setCode("male");
q.addItem().setLinkId("2B").setType(BOOLEAN).addEnableWhen().setQuestion("1B").setAnswer(enablewhenCoding);
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.setStatus(COMPLETED);
qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
QuestionnaireResponseItemComponent qrItem = qr.addItem().setLinkId("1B");
Coding coding = new Coding();
coding.setCode("male");
coding.setSystem("http://hl7.org/fhir/administrative-gender");
QuestionnaireResponseItemAnswerComponent answer = qrItem.addAnswer();
answer.setValue(coding);
qr.addItem().setLinkId("2B").addAnswer().setValue(new BooleanType(true));
String reference = qr.getQuestionnaire().getReference();
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q);
ValidationResult errors = myVal.validateWithResult(qr);
assertThat(errors.toString(), containsString("No issues"));
}
@Test @Test
public void testEmbeddedItemInChoice() { public void testEmbeddedItemInChoice() {
String questionnaireRef = "http://example.com/Questionnaire/q1"; String questionnaireRef = QUESTIONNAIRE_URL;
String valueSetRef = "http://somevalueset"; String valueSetRef = "http://somevalueset";
String codeSystemUrl = "http://codesystems.com/system"; String codeSystemUrl = "http://codesystems.com/system";
String codeValue = "code0"; String codeValue = "code0";
@ -362,7 +665,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
@Test @Test
public void testEmbeddedItemInOpenChoice() { public void testEmbeddedItemInOpenChoice() {
String questionnaireRef = "http://example.com/Questionnaire/q1"; String questionnaireRef = QUESTIONNAIRE_URL;
String valueSetRef = "http://somevalueset"; String valueSetRef = "http://somevalueset";
String codeSystemUrl = "http://codesystems.com/system"; String codeSystemUrl = "http://codesystems.com/system";
String codeValue = "code0"; String codeValue = "code0";
@ -418,7 +721,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
@Test @Test
public void testEmbeddedItemInString() { public void testEmbeddedItemInString() {
String questionnaireRef = "http://example.com/Questionnaire/q1"; String questionnaireRef = QUESTIONNAIRE_URL;
// create the questionnaire // create the questionnaire
QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); QuestionnaireItemComponent item1 = new QuestionnaireItemComponent();
@ -519,7 +822,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
.setType(QuestionnaireItemType.STRING) .setType(QuestionnaireItemType.STRING)
.setRequired(true); .setRequired(true);
String reference = "http://example.com/Questionnaire/q1"; String reference = QUESTIONNAIRE_URL;
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))) when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference)))
.thenReturn(q); .thenReturn(q);
@ -543,7 +846,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
@Test @Test
public void testOpenchoiceAnswer() { public void testOpenchoiceAnswer() {
String questionnaireRef = "http://example.com/Questionnaire/q1"; String questionnaireRef = QUESTIONNAIRE_URL;
Questionnaire q = new Questionnaire(); Questionnaire q = new Questionnaire();
QuestionnaireItemComponent item = q.addItem(); QuestionnaireItemComponent item = q.addItem();
@ -666,7 +969,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
QuestionnaireResponse qa = new QuestionnaireResponse(); QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO"));
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q);
@ -684,7 +987,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
QuestionnaireResponse qa = new QuestionnaireResponse(); QuestionnaireResponse qa = new QuestionnaireResponse();
qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED);
qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL);
qa.addItem().setLinkId("link1").addItem().setLinkId("link2"); qa.addItem().setLinkId("link1").addItem().setLinkId("link2");
when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q);

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -73,7 +73,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId> <artifactId>hapi-fhir-structures-r4</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -20,7 +20,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -6,7 +6,7 @@
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<name>HAPI-FHIR</name> <name>HAPI-FHIR</name>
<description>An open-source implementation of the FHIR specification in Java.</description> <description>An open-source implementation of the FHIR specification in Java.</description>
<url>https://hapifhir.io</url> <url>https://hapifhir.io</url>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -8,7 +8,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -6,7 +6,7 @@
<title>HAPI FHIR Changelog</title> <title>HAPI FHIR Changelog</title>
</properties> </properties>
<body> <body>
<release version="3.7.0" date="TBD" description="Gale"> <release version="3.7.0" date="2019-02-06" description="Gale">
<action type="add"> <action type="add">
HAPI FHIR is now built using OpenJDK 11. Users are recommended to upgrade to this version HAPI FHIR is now built using OpenJDK 11. Users are recommended to upgrade to this version
of Java if this is feasible. We are not yet dropping support for Java 8 (aka 1.8), but of Java if this is feasible. We are not yet dropping support for Java 8 (aka 1.8), but
@ -356,6 +356,10 @@
A NullPointerException during validation was fixed. Thanks to GitHub A NullPointerException during validation was fixed. Thanks to GitHub
user zilin375 for the pull request! user zilin375 for the pull request!
</action> </action>
<action type="add" issue="1148">
Support for validating enableWhen in Questionnaires has been added to the Validator. Thanks
to Eeva Turkka and Matti Uutsitalo for the pull request!
</action>
</release> </release>
<release version="3.6.0" date="2018-11-12" description="Food"> <release version="3.6.0" date="2018-11-12" description="Food">
<action type="add"> <action type="add">

View File

@ -67,6 +67,29 @@
</section> </section>
<section name="Announcements"> <section name="Announcements">
<p>
<b>Feb 6, 2019 - HAPI FHIR 3.7.0 (Gale) Released</b> -
The next release of HAPI has now been uploaded to the Maven repos and
GitHub's releases section.
</p>
<p>
This release includes support for the now-completed FHIR R4 release (FHIR 4.0.0).
It also brings support for Java 11, along with a big number of bugfixes and
new features.
</p>
<p>
As always, see the
<a href="http://hapifhir.io/changes-report.html">changelog</a> for a full list
of changes.
</p>
<p>
Thanks to everyone who contributed to this release!
</p>
<p>
- <a href="https://github.com/jamesagnew/">James Agnew</a>
</p>
<br/><br/>
<p> <p>
<b>Nov 12, 2018 - HAPI FHIR 3.6.0 (Food) Released</b> - <b>Nov 12, 2018 - HAPI FHIR 3.6.0 (Food) Released</b> -
The next release of HAPI has now been uploaded to the Maven repos and The next release of HAPI has now been uploaded to the Maven repos and
@ -124,7 +147,7 @@
</p> </p>
<br/><br/> <br/><br/>
<p> <p>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>3.7.0-SNAPSHOT</version> <version>3.7.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>