Fix validation error when Bundle has no type

This commit is contained in:
James Agnew 2017-10-11 10:20:12 -04:00
parent fddda443a2
commit 78b7188fbc
9 changed files with 204 additions and 39 deletions

View File

@ -71,10 +71,12 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
private void deleteSearch(final Long theSearchPid) { private void deleteSearch(final Long theSearchPid) {
Search searchToDelete = mySearchDao.findOne(theSearchPid); Search searchToDelete = mySearchDao.findOne(theSearchPid);
ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned()); if (searchToDelete != null) {
mySearchIncludeDao.deleteForSearch(searchToDelete.getId()); ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned());
mySearchResultDao.deleteForSearch(searchToDelete.getId()); mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
mySearchDao.delete(searchToDelete); mySearchResultDao.deleteForSearch(searchToDelete.getId());
mySearchDao.delete(searchToDelete);
}
} }
@Override @Override

View File

@ -42,7 +42,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class SubscriptionsRequireManualActivationInterceptorDstu2 extends ServerOperationInterceptorAdapter { public class SubscriptionsRequireManualActivationInterceptorDstu2 extends ServerOperationInterceptorAdapter {
@Autowired @Autowired
@Qualifier("mySubscriptionDaoR4") @Qualifier("mySubscriptionDaoDstu2")
private IFhirResourceDao<Subscription> myDao; private IFhirResourceDao<Subscription> myDao;
@Override @Override

View File

@ -13,8 +13,8 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.conformance.ProfileUtilities; import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
import org.hl7.fhir.dstu3.context.IWorkerContext; import org.hl7.fhir.dstu3.context.IWorkerContext;
import org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult; import org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult;
@ -105,6 +105,7 @@ import org.w3c.dom.Node;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.stream.JsonWriter;
import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.ObjectUtil;
@ -292,8 +293,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
source = Source.InstanceValidator; source = Source.InstanceValidator;
} }
@Override @Override
public boolean isNoInvariantChecks() { public boolean isNoInvariantChecks() {
return noInvariantChecks; return noInvariantChecks;
@ -1358,33 +1357,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checkFixedValue(errors, path + ".denominator", focus.getNamedChild("denominator"), fixed.getDenominator(), "denominator", focus); checkFixedValue(errors, path + ".denominator", focus.getNamedChild("denominator"), fixed.getDenominator(), "denominator", focus);
} }
private Reference readAsReference(Element item) {
Reference r = new Reference();
r.setDisplay(item.getNamedChildValue("display"));
r.setReference(item.getNamedChildValue("reference"));
List<Element> identifier = item.getChildrenByName("identifier");
if (identifier.isEmpty() == false) {
r.setIdentifier(readAsIdentifier(identifier.get(0)));
}
return r;
}
private Identifier readAsIdentifier(Element item) {
Identifier r = new Identifier();
r.setSystem(item.getNamedChildValue("system"));
r.setValue(item.getNamedChildValue("value"));
return r;
}
private void checkReference(Object appContext, List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition container, String parentType, NodeStack stack) throws FHIRException, IOException { private void checkReference(Object appContext, List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition container, String parentType, NodeStack stack) throws FHIRException, IOException {
Reference reference = readAsReference(element); String ref = null;
try {
String ref = reference.getReference(); // Do this inside a try because invalid instances might provide more than one reference.
if (Utilities.noString(ref)) { ref = element.getNamedChildValue("reference");
if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) { } catch (Error e) {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), "A Reference without an actual reference or identifier should have a display");
} }
return; if (Utilities.noString(ref)) {
} // todo - what should we do in this case?
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), "A Reference without an actual reference should have a display");
return;
}
Element we = localResolve(ref, stack, errors, path); Element we = localResolve(ref, stack, errors, path);
String refType; String refType;
@ -2816,6 +2801,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
List<Element> entries = new ArrayList<Element>(); List<Element> entries = new ArrayList<Element>();
bundle.getNamedChildren("entry", entries); bundle.getNamedChildren("entry", entries);
String type = bundle.getNamedChildValue("type"); String type = bundle.getNamedChildValue("type");
type = StringUtils.defaultString(type);
if (entries.size() == 0) { if (entries.size() == 0) {
rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")), "Documents or Messages must contain at least one entry"); rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")), "Documents or Messages must contain at least one entry");
} else { } else {

View File

@ -667,9 +667,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try { try {
if (context.fetchResourceWithException(ValueSet.class, system) != null) { if (context.fetchResourceWithException(ValueSet.class, system) != null) {
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: "+system+" - cannot use a value set URI as a system"); rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: "+system+" - cannot use a value set URI as a system");
return false; // Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back.
} else }
return true; return true;
} }
catch (Exception e) { catch (Exception e) {
return true; return true;
@ -1445,7 +1445,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
refType = "bundle"; refType = "bundle";
} }
} }
ReferenceValidationPolicy pol = refType.equals("contained") ? ReferenceValidationPolicy.CHECK_VALID : fetcher == null ? ReferenceValidationPolicy.IGNORE : fetcher.validationPolicy(hostContext.appContext, path, ref); ReferenceValidationPolicy pol = refType.equals("contained") || refType.equals("bundle") ? ReferenceValidationPolicy.CHECK_VALID : fetcher == null ? ReferenceValidationPolicy.IGNORE : fetcher.validationPolicy(hostContext.appContext, path, ref);
if (pol.checkExists()) { if (pol.checkExists()) {
if (we == null) { if (we == null) {
@ -2783,6 +2783,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
List<Element> entries = new ArrayList<Element>(); List<Element> entries = new ArrayList<Element>();
bundle.getNamedChildren("entry", entries); bundle.getNamedChildren("entry", entries);
String type = bundle.getNamedChildValue("type"); String type = bundle.getNamedChildValue("type");
type = StringUtils.defaultString(type);
if (entries.size() == 0) { if (entries.size() == 0) {
rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")), "Documents or Messages must contain at least one entry"); rule(errors, IssueType.INVALID, stack.getLiteralPath(), !(type.equals("document") || type.equals("message")), "Documents or Messages must contain at least one entry");
} else { } else {

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.validation; package ca.uhn.fhir.r4.validation;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
@ -17,7 +17,12 @@ import java.io.InputStream;
import java.util.*; import java.util.*;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidatorDstu3Test;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.hapi.ctx.*; import org.hl7.fhir.r4.hapi.ctx.*;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport.CodeValidationResult; import org.hl7.fhir.r4.hapi.ctx.IValidationSupport.CodeValidationResult;
@ -533,6 +538,16 @@ public class FhirInstanceValidatorR4Test {
assertEquals(output.toString(), 0, output.getMessages().size()); assertEquals(output.toString(), 0, output.getMessages().size());
} }
@Test
public void testValidateBundleWithNoType() throws Exception {
String vsContents = IOUtils.toString(FhirInstanceValidatorR4Test.class.getResourceAsStream("/r4/bundle-with-no-type.json"), "UTF-8");
ValidationResult output = myVal.validateWithResult(vsContents);
logResultsAndReturnNonInformationalOnes(output);
assertThat(output.getMessages().toString(), containsString("Element 'Bundle.type': minimum required = 1"));
}
@Test @Test
public void testValidateRawXmlResourceWithEmptyPrimitive() { public void testValidateRawXmlResourceWithEmptyPrimitive() {
// @formatter:off // @formatter:off

View File

@ -336,6 +336,15 @@ public class FhirInstanceValidatorDstu3Test {
assertTrue(output.isSuccessful()); assertTrue(output.isSuccessful());
} }
@Test
public void testValidateBundleWithNoType() throws Exception {
String vsContents = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3/bundle-with-no-type.json"), "UTF-8");
ValidationResult output = myVal.validateWithResult(vsContents);
logResultsAndReturnNonInformationalOnes(output);
assertThat(output.getMessages().toString(), containsString("Element 'Bundle.type': minimum required = 1"));
}
/** /**
* See #739 * See #739
*/ */

View File

@ -0,0 +1,73 @@
{
"resourceType": "Bundle",
"entry": [
{
"fullUrl": "http://localhost:5555/phr/baseDstu3/Person/PERSON1",
"resource": {
"resourceType": "Person",
"id": "PERSON1",
"meta": {
"versionId": "1",
"lastUpdated": "2017-08-17T16:19:40.109+03:00"
},
"identifier": [
{
"value": "081181-9984"
}
],
"name": [
{
"text": "Anna Testi",
"family": "Testi",
"given": [
"Anna"
]
},
{
"family": "asdfas"
}
],
"telecom": [
{
"use": "home"
},
{
"system": "phone",
"value": "(044) 1234567",
"use": "work"
}
],
"gender": "female",
"birthDate": "1981-11-08",
"address": [
{
"line": [
"Osuuspankkitie 2"
],
"city": "Helsinki",
"postalCode": "00120",
"country": "Suomi"
},
{
"city": "Blo-49847020"
}
],
"active": true,
"link": [
{
"target": {
"reference": "Patient/PATIENT1",
"display": "Anna Testi"
}
}
]
},
"request": {
"method": "PUT",
"url": "Person/PERSON1",
"ifMatch": "1"
}
}
]
}

View File

@ -0,0 +1,73 @@
{
"resourceType": "Bundle",
"entry": [
{
"fullUrl": "http://localhost:5555/phr/baseDstu3/Person/PERSON1",
"resource": {
"resourceType": "Person",
"id": "PERSON1",
"meta": {
"versionId": "1",
"lastUpdated": "2017-08-17T16:19:40.109+03:00"
},
"identifier": [
{
"value": "081181-9984"
}
],
"name": [
{
"text": "Anna Testi",
"family": "Testi",
"given": [
"Anna"
]
},
{
"family": "asdfas"
}
],
"telecom": [
{
"use": "home"
},
{
"system": "phone",
"value": "(044) 1234567",
"use": "work"
}
],
"gender": "female",
"birthDate": "1981-11-08",
"address": [
{
"line": [
"Osuuspankkitie 2"
],
"city": "Helsinki",
"postalCode": "00120",
"country": "Suomi"
},
{
"city": "Blo-49847020"
}
],
"active": true,
"link": [
{
"target": {
"reference": "Patient/PATIENT1",
"display": "Anna Testi"
}
}
]
},
"request": {
"method": "PUT",
"url": "Person/PERSON1",
"ifMatch": "1"
}
}
]
}

View File

@ -55,6 +55,10 @@
the ID of the search, meaning that if a search returns results from the Query the ID of the search, meaning that if a search returns results from the Query
cache, it will reuse the ID of the previously returned Bundle cache, it will reuse the ID of the previously returned Bundle
</action> </action>
<action type="fix">
Fix a NullPointerException when validating a Bundle (in DSTU3/R4) with no
<![CDATA[<code>Bundle.type</code>]]> value
</action>
</release> </release>
<release version="3.0.0" date="2017-09-27"> <release version="3.0.0" date="2017-09-27">
<action type="add"> <action type="add">