This commit is contained in:
de Beaubien, Bill 2015-09-01 09:22:02 -04:00
commit 6139e498be
12 changed files with 137 additions and 71 deletions

View File

@ -7,7 +7,7 @@ HAPI FHIR - Java API for HL7 FHIR Clients and Servers
[![Coverage Status](https://coveralls.io/repos/jamesagnew/hapi-fhir/badge.svg?branch=master&service=github)](https://coveralls.io/github/jamesagnew/hapi-fhir?branch=master) [![Coverage Status](https://coveralls.io/repos/jamesagnew/hapi-fhir/badge.svg?branch=master&service=github)](https://coveralls.io/github/jamesagnew/hapi-fhir?branch=master)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg)](http://search.maven.org/#search|ga|1|ca.uhn.hapi.fhir) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg)](http://search.maven.org/#search|ga|1|ca.uhn.hapi.fhir)
[![Dependency Status](https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e/badge.svg?style=flat)](https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e) [![Dependency Status](https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e/badge.svg?style=flat)](https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e)
[![License](https://img.shields.io/badge/license-apache%202.0-ff69b4.svg)](https://github.com/jamesagnew/hapi-fhir/blob/master/LICENSE.txt) [![License](https://img.shields.io/badge/license-apache%202.0-ff69b4.svg)](http://jamesagnew.github.io/hapi-fhir/license.html)
Complete project documentation is available here: Complete project documentation is available here:
http://jamesagnew.github.io/hapi-fhir/ http://jamesagnew.github.io/hapi-fhir/

View File

@ -152,7 +152,7 @@ public class LoggingInterceptor implements IClientInterceptor {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} }
myLog.info("Client response body:\n{}", new String(bytes)); myLog.info("Client response body:\n{}", new String(bytes, "UTF-8"));
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes)); theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
} else { } else {
myLog.info("Client response body: (none)"); myLog.info("Client response body: (none)");

View File

@ -50,7 +50,6 @@ import org.codehaus.stax2.XMLOutputFactory2;
import org.codehaus.stax2.io.EscapingWriterFactory; import org.codehaus.stax2.io.EscapingWriterFactory;
import com.ctc.wstx.api.WstxInputProperties; import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.stax.WstxInputFactory;
import com.ctc.wstx.stax.WstxOutputFactory; import com.ctc.wstx.stax.WstxOutputFactory;
/** /**
@ -1550,6 +1549,7 @@ public class XmlUtil {
if (ourInputFactory == null) { if (ourInputFactory == null) {
try { try {
// Detect if we're running with the Android lib, and force repackaged Woodstox to be used
Class.forName("ca.uhn.fhir.repackage.javax.xml.stream.XMLInputFactory"); Class.forName("ca.uhn.fhir.repackage.javax.xml.stream.XMLInputFactory");
System.setProperty("javax.xml.stream.XMLInputFactory", "com.ctc.wstx.stax.WstxInputFactory"); System.setProperty("javax.xml.stream.XMLInputFactory", "com.ctc.wstx.stax.WstxInputFactory");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
@ -1589,6 +1589,7 @@ public class XmlUtil {
if (ourOutputFactory == null) { if (ourOutputFactory == null) {
try { try {
// Detect if we're running with the Android lib, and force repackaged Woodstox to be used
Class.forName("ca.uhn.fhir.repackage.javax.xml.stream.XMLOutputFactory"); Class.forName("ca.uhn.fhir.repackage.javax.xml.stream.XMLOutputFactory");
System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory"); System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
@ -1708,7 +1709,7 @@ public class XmlUtil {
@Override @Override
public Writer createEscapingWriterFor(OutputStream theOut, String theEnc) throws UnsupportedEncodingException { public Writer createEscapingWriterFor(OutputStream theOut, String theEnc) throws UnsupportedEncodingException {
return createEscapingWriterFor(new OutputStreamWriter(theOut), theEnc); return createEscapingWriterFor(new OutputStreamWriter(theOut, theEnc), theEnc);
} }
@Override @Override

View File

@ -38,6 +38,7 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator; import javax.xml.validation.Validator;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.BOMInputStream; import org.apache.commons.io.input.BOMInputStream;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.w3c.dom.ls.LSInput; import org.w3c.dom.ls.LSInput;
@ -203,6 +204,7 @@ class SchemaBaseValidator implements IValidatorModule {
InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase); InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase);
if (baseIs == null) { if (baseIs == null) {
IOUtils.closeQuietly(baseIs);
throw new InternalErrorException("Schema file not found: " + pathToBase); throw new InternalErrorException("Schema file not found: " + pathToBase);
} }

View File

@ -28,6 +28,7 @@ import java.util.Map;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.oclc.purl.dsdl.svrl.SchematronOutputType; import org.oclc.purl.dsdl.svrl.SchematronOutputType;
@ -120,12 +121,14 @@ public class SchematronBaseValidator implements IValidatorModule {
return retVal; return retVal;
} }
String pathToBase = myCtx.getVersion().getPathToSchemaDefinitions() + '/' + theCtx.getFhirContext().getResourceDefinition(theCtx.getResource()).getBaseDefinition().getName().toLowerCase() String pathToBase = myCtx.getVersion().getPathToSchemaDefinitions() + '/' + theCtx.getFhirContext().getResourceDefinition(theCtx.getResource()).getBaseDefinition().getName().toLowerCase() + ".sch";
+ ".sch";
InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase); InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase);
if (baseIs == null) { try {
throw new InternalErrorException("No schematron found for resource type: " if (baseIs == null) {
+ theCtx.getFhirContext().getResourceDefinition(theCtx.getResource()).getBaseDefinition().getImplementingClass().getCanonicalName()); throw new InternalErrorException("No schematron found for resource type: " + theCtx.getFhirContext().getResourceDefinition(theCtx.getResource()).getBaseDefinition().getImplementingClass().getCanonicalName());
}
} finally {
IOUtils.closeQuietly(baseIs);
} }
retVal = SchematronResourceSCH.fromClassPath(pathToBase); retVal = SchematronResourceSCH.fromClassPath(pathToBase);

View File

@ -160,7 +160,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher); retVal.setPublisher(myPublisher);
retVal.setDate(DateTimeDt.withCurrentTime()); retVal.setDate(DateTimeDt.withCurrentTime());
retVal.setFhirVersion("0.5.0"); // TODO: pull from model retVal.setFhirVersion("1.0.0"); // TODO: pull from model
retVal.setAcceptUnknown(UnknownContentCodeEnum.UNKNOWN_EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser retVal.setAcceptUnknown(UnknownContentCodeEnum.UNKNOWN_EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser
// needs to be modified to actually allow it // needs to be modified to actually allow it

View File

@ -166,7 +166,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher); retVal.setPublisher(myPublisher);
retVal.setDate(new Date()); retVal.setDate(new Date());
retVal.setFhirVersion("0.5.0"); // TODO: pull from model retVal.setFhirVersion("1.0.0"); // TODO: pull from model
retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser
// needs to be modified to actually allow it // needs to be modified to actually allow it

View File

@ -890,7 +890,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String p = stack.addToLiteralPath("meta", "profile", ":"+Integer.toString(i)); String p = stack.addToLiteralPath("meta", "profile", ":"+Integer.toString(i));
if (rule(errors, IssueType.INVALID, element.line(), element.col(), p, !Utilities.noString(ref), "StructureDefinition reference invalid")) { if (rule(errors, IssueType.INVALID, element.line(), element.col(), p, !Utilities.noString(ref), "StructureDefinition reference invalid")) {
StructureDefinition pr = context.fetchResource(StructureDefinition.class, ref); StructureDefinition pr = context.fetchResource(StructureDefinition.class, ref);
if (warning(errors, IssueType.INVALID, element.line(), element.col(), p, pr != null, "StructureDefinition reference could not be resolved")) { if (warning(errors, IssueType.INVALID, element.line(), element.col(), p, pr != null, "StructureDefinition reference \"{0}\" could not be resolved", ref)) {
if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), p, pr.hasSnapshot(), "StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided")) { if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), p, pr.hasSnapshot(), "StructureDefinition has no snapshot - validation is against the snapshot, so it must be provided")) {
validateElement(errors, pr, pr.getSnapshot().getElement().get(0), null, null, element, element.getName(), stack); validateElement(errors, pr, pr.getSnapshot().getElement().get(0), null, null, element, element.getName(), stack);
} }

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.validation; package ca.uhn.fhir.validation;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -8,6 +9,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.hl7.fhir.instance.model.CodeType; import org.hl7.fhir.instance.model.CodeType;
@ -23,6 +25,8 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.primitive.IdDt;
public class FhirInstanceValidatorTest { public class FhirInstanceValidatorTest {
@ -120,20 +124,29 @@ public class FhirInstanceValidatorTest {
} }
@Test @Test
public void testValidateJsonResource() { public void testValidateRawJsonResource() {
// @formatter:off //@formatter:off
String input = "{" + "\"resourceType\":\"Patient\"," + "\"id\":\"123\"" + "}"; String input =
// @formatter:on "{" +
"\"resourceType\":\"Patient\"," +
"\"id\":\"123\"" +
"}";
//@formatter:on
ValidationResult output = myVal.validateWithResult(input); ValidationResult output = myVal.validateWithResult(input);
assertEquals(output.toString(), 0, output.getMessages().size()); assertEquals(output.toString(), 0, output.getMessages().size());
} }
@Test @Test
public void testValidateJsonResourceBadAttributes() { public void testValidateRawJsonResourceBadAttributes() {
// @formatter:off //@formatter:off
String input = "{" + "\"resourceType\":\"Patient\"," + "\"id\":\"123\"," + "\"foo\":\"123\"" + "}"; String input =
// @formatter:on "{" +
"\"resourceType\":\"Patient\"," +
"\"id\":\"123\"," +
"\"foo\":\"123\"" +
"}";
//@formatter:on
ValidationResult output = myVal.validateWithResult(input); ValidationResult output = myVal.validateWithResult(input);
assertEquals(output.toString(), 1, output.getMessages().size()); assertEquals(output.toString(), 1, output.getMessages().size());
@ -143,6 +156,34 @@ public class FhirInstanceValidatorTest {
assertEquals("Element is unknown or does not match any slice", output.getMessages().get(0).getMessage()); assertEquals("Element is unknown or does not match any slice", output.getMessages().get(0).getMessage());
} }
@Test
public void testValidateRawXmlResource() {
//@formatter:off
String input =
"<Patient xmlns=\"http://hl7.org/fhir\">" +
"<id value=\"123\"/>" +
"</Patient>";
//@formatter:on
ValidationResult output = myVal.validateWithResult(input);
assertEquals(output.toString(), 0, output.getMessages().size());
}
@Test
public void testValidateRawXmlResourceBadAttributes() {
// @formatter:off
String input = "<Patient xmlns=\"http://hl7.org/fhir\">" + "<id value=\"123\"/>" + "<foo value=\"222\"/>"
+ "</Patient>";
// @formatter:on
ValidationResult output = myVal.validateWithResult(input);
assertEquals(output.toString(), 1, output.getMessages().size());
ourLog.info(output.getMessages().get(0).getLocationString());
ourLog.info(output.getMessages().get(0).getMessage());
assertEquals("/f:Patient/f:foo", output.getMessages().get(0).getLocationString());
assertEquals("Element is unknown or does not match any slice", output.getMessages().get(0).getMessage());
}
@Test @Test
public void testValidateResourceFailingInvariant() { public void testValidateResourceFailingInvariant() {
Observation input = new Observation(); Observation input = new Observation();
@ -158,6 +199,68 @@ public class FhirInstanceValidatorTest {
} }
@Test
public void testValidateResourceContainingProfileDeclarationDoesntResolve() {
addValidConcept("http://loinc.org", "12345");
Observation input = new Observation();
input.getMeta().addProfile("http://foo/myprofile");
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");
input.setStatus(ObservationStatus.FINAL);
myInstanceVal.setValidationSupport(myMockSupport);
ValidationResult output = myVal.validateWithResult(input);
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
assertEquals(errors.toString(), 1, errors.size());
assertEquals("StructureDefinition reference \"http://foo/myprofile\" could not be resolved", errors.get(0).getMessage());
}
@Test
public void testValidateResourceContainingProfileDeclaration() {
addValidConcept("http://loinc.org", "12345");
Observation input = new Observation();
input.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/devicemetricobservation");
input.addIdentifier().setSystem("http://acme").setValue("12345");
input.getEncounter().setReference("http://foo.com/Encounter/9");
input.setStatus(ObservationStatus.FINAL);
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");
myInstanceVal.setValidationSupport(myMockSupport);
ValidationResult output = myVal.validateWithResult(input);
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
assertThat(errors.toString(), containsString("Element '/f:Observation.subject': minimum required = 1, but only found 0"));
assertThat(errors.toString(), containsString("Element encounter @ /f:Observation: max allowed = 0, but found 1"));
assertThat(errors.toString(), containsString("Element '/f:Observation.device': minimum required = 1, but only found 0"));
assertThat(errors.toString(), containsString(""));
}
@Test
public void testValidateResourceWithDefaultValueset() {
Observation input = new Observation();
input.setStatus(ObservationStatus.FINAL);
input.getCode().setText("No code here!");
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(input));
ValidationResult output = myVal.validateWithResult(input);
assertEquals(output.getMessages().size(), 0);
}
@Test
public void testValidateResourceWithDefaultValuesetBadCode() {
String input = "<Observation xmlns=\"http://hl7.org/fhir\">\n" + " <status value=\"notvalidcode\"/>\n"
+ " <code>\n" + " <text value=\"No code here!\"/>\n" + " </code>\n" + "</Observation>";
ValidationResult output = myVal.validateWithResult(input);
assertEquals(
"Coded value notvalidcode is not in value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status)",
output.getMessages().get(0).getMessage());
}
@Test @Test
public void testValidateResourceWithExampleBindingCodeValidationFailing() { public void testValidateResourceWithExampleBindingCodeValidationFailing() {
Observation input = new Observation(); Observation input = new Observation();
@ -188,52 +291,4 @@ public class FhirInstanceValidatorTest {
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output); List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
assertEquals(errors.toString(), 0, errors.size()); assertEquals(errors.toString(), 0, errors.size());
} }
@Test
public void testValidateResourceWithDefaultValueset() {
Observation input = new Observation();
input.setStatus(ObservationStatus.FINAL);
input.getCode().setText("No code here!");
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(input));
ValidationResult output = myVal.validateWithResult(input);
assertEquals(output.getMessages().size(), 0);
}
@Test
public void testValidateResourceWithDefaultValuesetBadCode() {
String input = "<Observation xmlns=\"http://hl7.org/fhir\">\n" + " <status value=\"notvalidcode\"/>\n"
+ " <code>\n" + " <text value=\"No code here!\"/>\n" + " </code>\n" + "</Observation>";
ValidationResult output = myVal.validateWithResult(input);
assertEquals(
"Coded value notvalidcode is not in value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status)",
output.getMessages().get(0).getMessage());
}
@Test
public void testValidateXmlResource() {
// @formatter:off
String input = "<Patient xmlns=\"http://hl7.org/fhir\">" + "<id value=\"123\"/>" + "</Patient>";
// @formatter:on
ValidationResult output = myVal.validateWithResult(input);
assertEquals(output.toString(), 0, output.getMessages().size());
}
@Test
public void testValidateXmlResourceBadAttributes() {
// @formatter:off
String input = "<Patient xmlns=\"http://hl7.org/fhir\">" + "<id value=\"123\"/>" + "<foo value=\"222\"/>"
+ "</Patient>";
// @formatter:on
ValidationResult output = myVal.validateWithResult(input);
assertEquals(output.toString(), 1, output.getMessages().size());
ourLog.info(output.getMessages().get(0).getLocationString());
ourLog.info(output.getMessages().get(0).getMessage());
assertEquals("/f:Patient/f:foo", output.getMessages().get(0).getLocationString());
assertEquals("Element is unknown or does not match any slice", output.getMessages().get(0).getMessage());
}
} }

View File

@ -783,9 +783,11 @@
<copy todir="target/site/xref-base"> <copy todir="target/site/xref-base">
<fileset dir="hapi-fhir-base/target/site/xref" /> <fileset dir="hapi-fhir-base/target/site/xref" />
</copy> </copy>
<!--
<copy todir="target/site/cobertura"> <copy todir="target/site/cobertura">
<fileset dir="hapi-fhir-cobertura/target/site/cobertura" /> <fileset dir="hapi-fhir-cobertura/target/site/cobertura" />
</copy> </copy>
-->
<copy todir="target/site"> <copy todir="target/site">
<fileset dir="hapi-fhir-base/target/site" includes="checkstyle.*" /> <fileset dir="hapi-fhir-base/target/site" includes="checkstyle.*" />
</copy> </copy>
@ -1164,7 +1166,9 @@
<module>hapi-fhir-structures-dstu2</module> <module>hapi-fhir-structures-dstu2</module>
<module>hapi-fhir-structures-hl7org-dstu2</module> <module>hapi-fhir-structures-hl7org-dstu2</module>
<module>hapi-fhir-jpaserver-base</module> <module>hapi-fhir-jpaserver-base</module>
<!--
<module>hapi-fhir-cobertura</module> <module>hapi-fhir-cobertura</module>
-->
<module>examples</module> <module>examples</module>
</modules> </modules>
<reporting> <reporting>

View File

@ -137,10 +137,9 @@
</menu> </menu>
<menu name="Maven Reports" inherit="bottom"> <menu name="Reports" inherit="bottom">
<item name="Project Developers" href="team-list.html" /> <item name="Project Developers" href="team-list.html" />
<item name="Cobertura (Test Coverage)" href="cobertura/index.html" /> <item name="Coveralls (Test Coverage)" href="https://coveralls.io/github/jamesagnew/hapi-fhir" />
<item name="Surefire (Test Status)" href="surefire-report.html" />
<item name="FindBugs" href="findbugs.html" /> <item name="FindBugs" href="findbugs.html" />
<item name="Checkstyle" href="checkstyle.html" /> <item name="Checkstyle" href="checkstyle.html" />
</menu> </menu>

View File

@ -24,6 +24,8 @@
<a href="https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg"><img src="https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg" alt="Maven Central"/></a> <a href="https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg"><img src="https://maven-badges.herokuapp.com/maven-central/ca.uhn.hapi.fhir/hapi-fhir-base/badge.svg" alt="Maven Central"/></a>
<br/> <br/>
<a href="https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e"><img src="https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e/badge.svg?style=flat" alt="VersionEye"/></a> <a href="https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e"><img src="https://www.versioneye.com/user/projects/55e1d0d9c6d8f2001c00043e/badge.svg?style=flat" alt="VersionEye"/></a>
<br/>
<a href="http://jamesagnew.github.io/hapi-fhir/license.html"><img src="https://img.shields.io/badge/license-apache%202.0-ff69b4.svg" alt="Apache 2.0 Licensed"/></a>
</p> </p>
<p> <p>