Merge branch 'master' of github.com:jamesagnew/hapi-fhir

This commit is contained in:
jamesagnew 2016-04-11 09:08:34 -04:00
commit d7c683f6b9
14 changed files with 407 additions and 15 deletions

View File

@ -171,7 +171,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public BaseConformance conformance() {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
throw new IllegalArgumentException("Must call conformance(" + IBaseConformance.class.getSimpleName() + ") instead of conformance() for HL7.org structures");
throw new IllegalArgumentException("Must call fetchConformance() instead of conformance() for RI/DSTU3+ structures");
}
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext());

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server.interceptor;
import static org.apache.commons.lang3.StringUtils.isBlank;
/*
* #%L
* HAPI FHIR - Core Library
@ -59,6 +61,12 @@ public class RequestValidatingInterceptor extends BaseValidatingInterceptor<Stri
Charset charset = ResourceParameter.determineRequestCharset(theRequestDetails);
String requestText = new String(theRequestDetails.loadRequestContents(), charset);
if (isBlank(requestText)) {
ourLog.trace("Incoming request does not have a body");
return true;
}
validate(requestText, theRequestDetails);
return true;

View File

@ -39,7 +39,10 @@ import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.fusesource.jansi.Ansi;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Resource;
@ -60,6 +63,8 @@ import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
public class ExampleDataUploader extends BaseCommand {
@ -369,6 +374,7 @@ public class ExampleDataUploader extends BaseCommand {
Map<String, Integer> ids = new HashMap<String, Integer>();
Set<String> fullIds = new HashSet<String>();
for (Iterator<BundleEntryComponent> iterator = bundle.getEntry().iterator(); iterator.hasNext();) {
BundleEntryComponent next = iterator.next();
@ -530,6 +536,10 @@ public class ExampleDataUploader extends BaseCommand {
throws IOException, UnsupportedEncodingException {
org.hl7.fhir.dstu3.model.Bundle bundle = new org.hl7.fhir.dstu3.model.Bundle();
bundle.setType(BundleType.TRANSACTION);
FhirValidator val = ctx.newValidator();
val.registerValidatorModule(new FhirInstanceValidator(new DefaultProfileValidationSupport()));
ZipInputStream zis = new ZipInputStream(FileUtils.openInputStream(inputFile));
byte[] buffer = new byte[2048];
@ -567,6 +577,12 @@ public class ExampleDataUploader extends BaseCommand {
}
ourLog.info("Found example {} - {} - {} chars", nextEntry.getName(), parsed.getClass().getSimpleName(), exampleString.length());
ValidationResult result = val.validateWithResult(parsed);
if (result.isSuccessful() == false) {
ourLog.info("FAILED to validate example {}", nextEntry.getName());
continue;
}
if (ctx.getResourceDefinition(parsed).getName().equals("Bundle")) {
BaseRuntimeChildDefinition entryChildDef = ctx.getResourceDefinition(parsed).getChildByName("entry");
BaseRuntimeElementCompositeDefinition<?> entryDef = (BaseRuntimeElementCompositeDefinition<?>) entryChildDef.getChildByName("entry");
@ -577,7 +593,10 @@ public class ExampleDataUploader extends BaseCommand {
continue;
}
for (IBase nextResource : resources) {
if (!ctx.getResourceDefinition(parsed).getName().equals("Bundle") && ctx.getResourceDefinition(parsed).getName().equals("SearchParameter")) {
if (nextResource == null) {
continue;
}
if (!ctx.getResourceDefinition((Class<? extends IBaseResource>) nextResource.getClass()).getName().equals("Bundle") && ctx.getResourceDefinition((Class<? extends IBaseResource>) nextResource.getClass()).getName().equals("SearchParameter")) {
BundleEntryComponent entry = bundle.addEntry();
entry.getRequest().setMethod(HTTPVerb.POST);
entry.setResource((Resource) nextResource);

View File

@ -84,6 +84,9 @@ public class StaleSearchDeletingSvc {
}
});
}
ourLog.info("Deleted {} searches, {} remaining", toDelete.size(), mySearchDao.count());
}
}

View File

@ -26,7 +26,6 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
@ -148,6 +147,14 @@ public class JpaServerDemo extends RestfulServer {
this.registerInterceptor(interceptor);
}
/*
* If you are hosting this server at a specific DNS name, the server will try to
* figure out the FHIR base URL based on what the web container tells it, but
* this doesn't always work. If you are setting links in your search bundles that
* just refer to "localhost", you might want to use a server address strategy:
*/
//setServerAddressStrategy(new HardcodedServerAddressStrategy("http://mydomain.com/fhir/baseDstu2"));
}
}

View File

@ -102,7 +102,7 @@
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
<init-param>
<param-name>ImplementationDescription</param-name>
<param-value>UHN Test Server (DSTU 2.1 Resources)</param-value>
<param-value>UHN Test Server (DSTU 3 Resources)</param-value>
</init-param>
<init-param>
<param-name>FhirVersion</param-name>

View File

@ -9,7 +9,7 @@
</encoder>
</appender>
<logger name="org.hibernate.SQL" additivity="false" level="trace">
<logger name="org.hibernate.SQL" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>

View File

@ -187,7 +187,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher);
retVal.setDateElement(conformanceDate());
retVal.setFhirVersion("1.0.2"); // TODO: pull from model
retVal.setFhirVersion("1.4.0"); // TODO: pull from model
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

View File

@ -179,14 +179,16 @@ public class ProfileUtilities {
// if we have a name reference, we have to find it, and iterate it's children
if (contentReference != null) {
boolean found = false;
String candidateName = profile.getName() + "." + contentReference.substring(1);
for (ElementDefinition e : profile.getSnapshot().getElement()) {
if (e.getPath().equals(candidateName)) {
String elementPath = e.getPath();
if (elementPath.startsWith(profile.getName()) && elementPath.endsWith("." + contentReference.substring(1))) {
found = true;
path = e.getPath();
path = elementPath;
break;
} else if (contentReference.equals("#"+e.getId())) {
found = true;
path = e.getPath();
path = elementPath;
break;
}
}
if (!found)

View File

@ -1333,6 +1333,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// }
private boolean passesCodeWhitespaceRules(String v) {
if (v == null) {
return true;
}
if (!v.trim().equals(v))
return false;
boolean lastWasSpace = true;
@ -2161,7 +2164,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
NodeStack firstStack = stack.push(firstEntry, 0, null, null);
String fullUrl = firstEntry.getNamedChildValue("fullUrl");
if (type.equals("document")) {
if ("document".equals(type)) {
WrapperElement res = firstEntry.getNamedChild("resource");
NodeStack localStack = firstStack.push(res, -1, null, null);
WrapperElement resource = res.getFirstChild();
@ -2173,7 +2176,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
validateDocument(errors, entries, res, localStack, fullUrl, id);
}
}
if (type.equals("message"))
if ("message".equals(type))
validateMessage(errors, bundle);
}
}
@ -2475,12 +2478,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
match = sliceMatches(ei.element, ei.path, slice, ed, profile);
}
if (match) {
if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.path, ei.definition == null, "Element matches more than one slice"))
if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.path, ei.definition == null, "Element matches more than one slice")) {
ei.definition = ed;
}
}
}
}
}
for (ElementInfo ei : children)
if (ei.path.endsWith(".extension"))
rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.path, ei.definition != null, "Element is unknown or does not match any slice (url=\"" + ei.element.getAttribute("url") + "\")");

View File

@ -104,6 +104,27 @@ public class RequestValidatingInterceptorDstu3Test {
assertThat(responseContent, containsString("<severity value=\"error\"/>"));
}
@Test
public void testFetchMetadata() throws Exception {
myInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata");
// This header caused a crash
httpGet.addHeader("Content-Type", "application/xml+fhir");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", status);
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, containsString("Conformance"));
}
@Test
public void testCreateJsonInvalidNoFailure() throws Exception {
myInterceptor.setFailOnSeverity(null);

View File

@ -225,6 +225,22 @@ public class FhirInstanceValidatorDstu3Test {
assertEquals("Element is unknown or does not match any slice", output.getMessages().get(0).getMessage());
}
@Test
public void testValidateRawJsonResourceFromExamples() throws Exception {
// @formatter:off
String input = IOUtils.toString(FhirInstanceValidator.class.getResourceAsStream("/testscript-search.json"));
// @formatter:on
ValidationResult output = myVal.validateWithResult(input);
logResultsAndReturnNonInformationalOnes(output);
// assertEquals(output.toString(), 1, output.getMessages().size());
// ourLog.info(output.getMessages().get(0).getLocationString());
// ourLog.info(output.getMessages().get(0).getMessage());
// assertEquals("/foo", output.getMessages().get(0).getLocationString());
// assertEquals("Element is unknown or does not match any slice", output.getMessages().get(0).getMessage());
}
@Test
public void testValidateRawXmlResource() {
// @formatter:off

View File

@ -0,0 +1,298 @@
{
"resourceType": "TestScript",
"id": "search",
"text": {
"status": "generated",
"div": "<div><p><b>Generated Narrative with Details</b></p><p><b>id</b>: search</p><p><b>name</b>: Read, Search and Conditional Create and Delete</p><p><b>description</b>: Test Script for testing search, read, and conditional create and delete</p><h3>Fixtures</h3><table><tr><td>-</td><td><b>Resource</b></td></tr><tr><td>*</td><td><a>Patient/patient-example.xml</a></td></tr></table><blockquote><p><b>variable</b></p><p><b>name</b>: V1</p><p><b>sourceId</b>: R1</p><p><b>headerField</b>: Location</p></blockquote><blockquote><p><b>variable</b></p><p><b>name</b>: V2</p><p><b>sourceId</b>: R3</p><p><b>path</b>: fhir:Patient/fhir:name/fhir:given/@value</p></blockquote><blockquote><p><b>setup</b></p><blockquote><p><b>action</b></p><h3>Operations</h3><table><tr><td>-</td><td><b>Type</b></td><td><b>Resource</b></td><td><b>Params</b></td></tr><tr><td>*</td><td>delete</td><td>Patient</td><td>given=John&amp;family=Doe</td></tr></table></blockquote></blockquote><blockquote><p><b>test</b></p><p><b>name</b>: Create</p><p><b>description</b>: Create, read, search, conditional create, conditional delete.</p><blockquote><p><b>metadata</b></p><h3>Links</h3><table><tr><td>-</td><td><b>Url</b></td><td><b>Description</b></td></tr><tr><td>*</td><td><a>http://hl7.org/implement/standards/FHIR-Develop/patient.html</a></td><td>FHIR Patient</td></tr></table><blockquote><p><b>operation</b></p><p><b>type</b>: create</p><p><b>resource</b>: Patient</p><p><b>description</b>: Conditional Create Operation</p><p><b>link</b>: <a>http://hl7-fhir.github.io/http.html#2.1.0.13.1</a></p><p><b>required</b>: true</p><p><b>validated</b>: true</p></blockquote><blockquote><p><b>operation</b></p><p><b>type</b>: delete</p><p><b>resource</b>: Patient</p><p><b>description</b>: Conditional Delete Operation</p><p><b>link</b>: <a>http://hl7-fhir.github.io/http.html#2.1.0.12.1</a></p><p><b>required</b>: true</p><p><b>validated</b>: true</p></blockquote><blockquote><p><b>operation</b></p><p><b>type</b>: read</p><p><b>resource</b>: Patient</p><p><b>description</b>: Patient Read Operation</p><p><b>link</b>: <a>http://hl7.org/implement/standards/FHIR-Develop/http.html#read</a></p><p><b>validated</b>: true</p></blockquote><blockquote><p><b>operation</b></p><p><b>type</b>: search</p><p><b>resource</b>: Patient</p><p><b>description</b>: Patient Search Operation</p><p><b>link</b>: <a>http://hl7-fhir.github.io/http.html#2.1.0.14</a></p><p><b>validated</b>: true</p></blockquote></blockquote><blockquote><p><b>action</b></p><h3>Operations</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Operations</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Operations</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Operations</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Operations</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Operations</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Operations</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote><blockquote><p><b>action</b></p><h3>Asserts</h3><table><tr><td>-</td></tr><tr><td>*</td></tr></table></blockquote></blockquote></div>"
},
"url": "http://hl7.org/fhir/TestScript/search",
"name": "Read, Search and Conditional Create and Delete",
"status": "draft",
"description": "Test Script for testing search, read, and conditional create and delete",
"metadata": {
"capability": [
{
"required": true,
"description": "Patient Create and Read Operations",
"link": [
"http://hl7.org/implement/standards/FHIR-Develop/http.html#create",
"http://hl7.org/implement/standards/FHIR-Develop/http.html#read"
],
"conformance": {
"reference": "Conformance/example"
}
},
{
"required": true,
"description": "Patient Conditional Delete Operation",
"link": [
"http://hl7-fhir.github.io/http.html#2.1.0.12.1"
],
"conformance": {
"reference": "Conformance/example"
}
},
{
"required": true,
"description": "Patient Conditional Create Operation",
"link": [
"http://hl7-fhir.github.io/http.html#2.1.0.13.1"
],
"conformance": {
"reference": "Conformance/example"
}
}
]
},
"fixture": [
{
"id": "example-patient",
"resource": {
"reference": "Patient/example"
}
}
],
"variable": [
{
"name": "V1",
"headerField": "Location",
"sourceId": "R1"
},
{
"name": "V2",
"path": "fhir:Patient/fhir:name/fhir:given/@value",
"sourceId": "R3"
},
{
"name": "DefaultValue",
"defaultValue": "Replace at Runtime"
}
],
"setup": {
"action": [
{
"fhir_comments": [
" Conditional Delete "
],
"operation": {
"type": {
"code": "delete"
},
"resource": "Patient",
"params": "given=John&family=Doe"
}
}
]
},
"test": [
{
"id": "Test1",
"name": "Create",
"description": "Create, read, search, conditional create, conditional delete.",
"metadata": {
"link": [
{
"url": "http://hl7.org/implement/standards/FHIR-Develop/patient.html",
"description": "FHIR Patient"
}
],
"capability": [
{
"validated": true,
"description": "Patient Search Operation",
"link": [
"http://hl7.org/implement/standards/FHIR-Develop/http.html#search"
],
"conformance": {
"reference": "Conformance/example"
}
}
]
},
"action": [
{
"fhir_comments": [
" Create the patient using fixture "
],
"operation": {
"type": {
"code": "create"
},
"sourceId": "example-patient"
}
},
{
"assert": {
"responseCode": "201"
}
},
{
"fhir_comments": [
" Patient search by name. Save the responseBody in 'F1' fixture.\n\t\t\t\tSave the responseHeader in H1 "
],
"operation": {
"type": {
"code": "search"
},
"resource": "Patient",
"contentType": "json",
"params": "?given=John&family=Doe",
"responseId": "R1"
}
},
{
"fhir_comments": [
" Verify that the Location in response-header is valid "
],
"assert": {
"headerField": "Location",
"operator": "notEmpty",
"warningOnly": true
}
},
{
"fhir_comments": [
" Verify that the birthdate got persisted and is being returned properly "
],
"assert": {
"operator": "equals",
"path": "fhir:Patient/fhir:birthDate/@value",
"sourceId": "R1",
"value": "1974-12-31"
}
},
{
"fhir_comments": [
" Verify that the navigation links are valid "
],
"assert": {
"navigationLinks": true,
"warningOnly": true
}
},
{
"fhir_comments": [
" Use the Location returned earlier to grab the resource\n\t\t \t\tto verify that Location was pointing to correct resource. "
],
"operation": {
"type": {
"code": "search"
},
"accept": "json",
"responseId": "R2",
"url": "${V1}"
}
},
{
"assert": {
"contentType": "json"
}
},
{
"assert": {
"response": "okay"
}
},
{
"fhir_comments": [
" Search for the resource but this time using the birthdate\n\t\t\t\tas a search parameter to make sure search by birthDate works "
],
"operation": {
"type": {
"code": "search"
},
"resource": "Patient",
"accept": "json",
"params": "?given=John&family=Doe&birthdate=1974-12-31",
"responseId": "R3"
}
},
{
"assert": {
"contentType": "json"
}
},
{
"assert": {
"response": "okay"
}
},
{
"fhir_comments": [
" Verify that the birthDate matches expectations "
],
"assert": {
"compareToSourceId": "R2",
"compareToSourcePath": "fhir:Patient/fhir:birthDate/@value",
"path": "fhir:Patient/fhir:birthDate/@value",
"sourceId": "R3"
}
},
{
"fhir_comments": [
" Verify that the name matches expectations "
],
"assert": {
"path": "fhir:Patient/fhir:name/fhir:given/@value",
"sourceId": "R3",
"value": "John"
}
},
{
"fhir_comments": [
" Conditional Create "
],
"operation": {
"type": {
"code": "create"
},
"requestHeader": [
{
"field": "If-None-Exist",
"value": "Patient?given=John&Doe&birthdate=1974-12-31"
}
],
"sourceId": "F1"
}
},
{
"fhir_comments": [
" The response code of 200 verifies that the resource\n\t\t\t\talready exists and did not get created "
],
"assert": {
"responseCode": "200"
}
},
{
"fhir_comments": [
" Conditional Delete "
],
"operation": {
"type": {
"code": "delete"
},
"resource": "Patient",
"params": "?given=John&family=Doe&birthdate=1974-12-31"
}
},
{
"fhir_comments": [
" Search again and make sure the patient has been deleted.\n\t\t \t\t This time perform read by id using variable "
],
"operation": {
"type": {
"code": "read"
},
"resource": "Patient",
"params": "/${V2}"
}
},
{
"assert": {
"responseCode": "410"
}
}
]
}
]
}

View File

@ -23,6 +23,7 @@ import org.hl7.fhir.dstu3.model.Conformance.ConformanceRestComponent;
import org.hl7.fhir.dstu3.model.Conformance.ConformanceRestResourceComponent;
import org.hl7.fhir.dstu3.model.Conformance.ConformanceRestResourceSearchParamComponent;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseConformance;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
@ -90,7 +91,20 @@ public class Controller extends BaseController {
long start = System.currentTimeMillis();
try {
client.conformance();
Class<? extends IBaseConformance> type;
switch (getContext(theRequest).getVersion().getVersion()) {
default:
case DSTU1:
type = Conformance.class;
break;
case DSTU2:
type = ca.uhn.fhir.model.dstu2.resource.Conformance.class;
break;
case DSTU3:
type = org.hl7.fhir.dstu3.model.Conformance.class;
break;
}
client.fetchConformance().ofType(type).execute();
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}