From 88157fe1a6b759bec527eed6ff186026a43c01a1 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 8 Apr 2016 16:31:42 -0400 Subject: [PATCH] Test data uploader fixes --- .../uhn/fhir/rest/client/GenericClient.java | 2 +- .../RequestValidatingInterceptor.java | 8 + .../ca/uhn/fhir/cli/ExampleDataUploader.java | 23 +- .../jpa/search/StaleSearchDeletingSvc.java | 3 + .../ca/uhn/fhir/jpa/demo/JpaServerDemo.java | 9 +- .../src/main/webapp/WEB-INF/web.xml | 2 +- .../src/test/resources/logback-test.xml | 2 +- .../server/ServerConformanceProvider.java | 2 +- .../fhir/dstu3/utils/ProfileUtilities.java | 10 +- .../dstu3/validation/InstanceValidator.java | 10 +- ...RequestValidatingInterceptorDstu3Test.java | 21 ++ .../FhirInstanceValidatorDstu3Test.java | 16 + .../src/test/resources/testscript-search.json | 298 ++++++++++++++++++ .../main/java/ca/uhn/fhir/to/Controller.java | 16 +- 14 files changed, 407 insertions(+), 15 deletions(-) create mode 100644 hapi-fhir-structures-dstu3/src/test/resources/testscript-search.json diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index cb0732c53ab..7aa3bda5ab6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -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()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java index c8cd9aa1ef6..60138147dcb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java @@ -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 ids = new HashMap(); Set fullIds = new HashSet(); + for (Iterator iterator = bundle.getEntry().iterator(); iterator.hasNext();) { BundleEntryComponent next = iterator.next(); - + // DataElement have giant IDs that seem invalid, need to investigate this.. if ("DataElement".equals(next.getResource().getResourceType().name()) || "OperationOutcome".equals(next.getResource().getResourceType().name()) || "OperationDefinition".equals(next.getResource().getResourceType().name())) { ourLog.info("Skipping " + next.getResource().getResourceType().name() + " example"); @@ -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) nextResource.getClass()).getName().equals("Bundle") && ctx.getResourceDefinition((Class) nextResource.getClass()).getName().equals("SearchParameter")) { BundleEntryComponent entry = bundle.addEntry(); entry.getRequest().setMethod(HTTPVerb.POST); entry.setResource((Resource) nextResource); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvc.java index 928539f2c9b..d3bc9842339 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvc.java @@ -84,6 +84,9 @@ public class StaleSearchDeletingSvc { } }); } + + ourLog.info("Deleted {} searches, {} remaining", toDelete.size(), mySearchDao.count()); + } } diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index ebd4cb014ba..a2985c0f7c1 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -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")); + } } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml index 3b76c566159..8e2dabd4a34 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml @@ -102,7 +102,7 @@ ca.uhn.fhirtest.TestRestfulServer ImplementationDescription - UHN Test Server (DSTU 2.1 Resources) + UHN Test Server (DSTU 3 Resources) FhirVersion diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/test/resources/logback-test.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/test/resources/logback-test.xml index ef20f6603ff..19fc9e6c7e8 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/test/resources/logback-test.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/test/resources/logback-test.xml @@ -9,7 +9,7 @@ - + diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProvider.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProvider.java index a6fbd7730e7..d2dd294d9e0 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProvider.java @@ -187,7 +187,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider")); } + @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); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorDstu3Test.java index 2b937c07714..9b40178e0c8 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorDstu3Test.java @@ -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 diff --git a/hapi-fhir-structures-dstu3/src/test/resources/testscript-search.json b/hapi-fhir-structures-dstu3/src/test/resources/testscript-search.json new file mode 100644 index 00000000000..8fb07ae10a5 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/resources/testscript-search.json @@ -0,0 +1,298 @@ +{ + "resourceType": "TestScript", + "id": "search", + "text": { + "status": "generated", + "div": "

Generated Narrative with Details

id: search

name: Read, Search and Conditional Create and Delete

description: Test Script for testing search, read, and conditional create and delete

Fixtures

-Resource
*Patient/patient-example.xml

variable

name: V1

sourceId: R1

headerField: Location

variable

name: V2

sourceId: R3

path: fhir:Patient/fhir:name/fhir:given/@value

setup

action

Operations

-TypeResourceParams
*deletePatientgiven=John&family=Doe

test

name: Create

description: Create, read, search, conditional create, conditional delete.

metadata

Links

-UrlDescription
*http://hl7.org/implement/standards/FHIR-Develop/patient.htmlFHIR Patient

operation

type: create

resource: Patient

description: Conditional Create Operation

link: http://hl7-fhir.github.io/http.html#2.1.0.13.1

required: true

validated: true

operation

type: delete

resource: Patient

description: Conditional Delete Operation

link: http://hl7-fhir.github.io/http.html#2.1.0.12.1

required: true

validated: true

operation

type: read

resource: Patient

description: Patient Read Operation

link: http://hl7.org/implement/standards/FHIR-Develop/http.html#read

validated: true

operation

type: search

resource: Patient

description: Patient Search Operation

link: http://hl7-fhir.github.io/http.html#2.1.0.14

validated: true

action

Operations

-
*

action

Asserts

-
*

action

Operations

-
*

action

Asserts

-
*

action

Asserts

-
*

action

Operations

-
*

action

Asserts

-
*

action

Asserts

-
*

action

Operations

-
*

action

Asserts

-
*

action

Asserts

-
*

action

Asserts

-
*

action

Asserts

-
*

action

Operations

-
*

action

Asserts

-
*

action

Operations

-
*

action

Operations

-
*

action

Asserts

-
*
" + }, + "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" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java index 0c1405400ce..4eee4801e7d 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java @@ -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 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); }