Fix #209 - _revinclude results from JPA server should have a Bundle.entry.search.mode of "include" and not "match". Thanks to Josh Mandel for reporting!

This commit is contained in:
jamesagnew 2015-08-23 22:25:25 -04:00
parent 11376024fa
commit 60339d6f93
6 changed files with 59 additions and 55 deletions

View File

@ -526,7 +526,7 @@ public abstract class BaseParser implements IParser {
@SuppressWarnings("cast")
protected List<? extends IBase> preProcessValues(BaseRuntimeChildDefinition metaChildUncast, List<? extends IBase> theValues) {
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
if (shouldAddSubsettedTag()) {
if (shouldAddSubsettedTag() && metaChildUncast.getValidChildNames().contains("meta")) {
BaseRuntimeElementDefinition<?> childByName = metaChildUncast.getChildByName("meta");
if (childByName instanceof BaseRuntimeElementCompositeDefinition<?>) {
BaseRuntimeElementCompositeDefinition<?> metaChildUncast1 = (BaseRuntimeElementCompositeDefinition<?>) childByName;

View File

@ -1446,7 +1446,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
return retVal;
}
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, BundleEntrySearchModeEnum theBundleEntryStatus) {
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids) {
if (theIncludePids.isEmpty()) {
return;
}
@ -1472,15 +1472,19 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
continue;
}
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(resource, theBundleEntryStatus);
if (theRevIncludedPids.contains(next.getId())) {
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(resource, BundleEntrySearchModeEnum.INCLUDE);
} else {
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(resource, BundleEntrySearchModeEnum.MATCH);
}
theResourceListToPopulate.set(index, resource);
}
}
protected void loadReverseIncludes(List<Long> theMatches, Set<Include> theRevIncludes) {
private Set<Long> loadReverseIncludes(List<Long> theMatches, Set<Include> theRevIncludes) {
if (theMatches.size() == 0) {
return;
return Collections.emptySet();
}
HashSet<Long> pidsToInclude = new HashSet<Long>();
@ -1528,6 +1532,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
}
theMatches.addAll(pidsToInclude);
return pidsToInclude;
}
@Override
@ -1890,8 +1895,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
}
// Load _revinclude resources
final Set<Long> revIncludedPids;
if (theParams.getRevIncludes() != null && theParams.getRevIncludes().isEmpty() == false) {
loadReverseIncludes(pids, theParams.getRevIncludes());
revIncludedPids = loadReverseIncludes(pids, theParams.getRevIncludes());
} else {
revIncludedPids = Collections.emptySet();
}
IBundleProvider retVal = new IBundleProvider() {
@ -1910,8 +1918,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
// Execute the query and make sure we return distinct results
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
loadResourcesByPid(pidsSubList, retVal, BundleEntrySearchModeEnum.MATCH);
loadResourcesByPid(pidsSubList, retVal, revIncludedPids);
/*
* Load _include resources - Note that _revincludes are handled differently than _include ones, as
* they are counted towards the total count and paged, so they are loaded outside the bundle provider

View File

@ -69,6 +69,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
@ -1304,10 +1305,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION));
IBundleProvider resultsP = ourOrganizationDao.search(map);
assertEquals(2, resultsP.size());
List<IBaseResource> results = resultsP.getResources(0, resultsP.size());
assertEquals(2, results.size());
assertEquals(Organization.class, results.get(0).getClass());
assertEquals(BundleEntrySearchModeEnum.MATCH, ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get((IResource) results.get(0)));
assertEquals(Patient.class, results.get(1).getClass());
assertEquals(BundleEntrySearchModeEnum.INCLUDE, ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get((IResource) results.get(1)));
}
@Test

View File

@ -214,7 +214,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
@Override
public void validate(List<ValidationMessage> errors, Element element, String profile) throws Exception {
StructureDefinition p = context.getProfiles().get(profile);
StructureDefinition p = context.getProfile(profile);
if (p == null)
throw new Exception("StructureDefinition '"+profile+"' not found");
validateResource(errors, new DOMWrapperElement(element), p, requiresResourceId, null);
@ -231,7 +231,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
@Override
public void validate(List<ValidationMessage> errors, JsonObject object, String profile) throws Exception {
StructureDefinition p = context.getProfiles().get(profile);
StructureDefinition p = context.getProfile(profile);
if (p == null)
throw new Exception("StructureDefinition '"+profile+"' not found");
validateResource(errors, new JsonWrapperElement(object), p, requiresResourceId, null);
@ -245,7 +245,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
@Override
public void validate(List<ValidationMessage> errors, Document document, String profile) throws Exception {
checkForProcessingInstruction(errors, document);
StructureDefinition p = context.getProfiles().get(profile);
StructureDefinition p = context.getProfile(profile);
if (p == null)
throw new Exception("StructureDefinition '"+profile+"' not found");
validateResource(errors, new DOMWrapperElement(document.getDocumentElement()), p, requiresResourceId, null);
@ -833,7 +833,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (ok) {
String resourceName = element.getResourceType();
if (profile == null) {
profile = context.getProfiles().get("http://hl7.org/fhir/StructureDefinition/"+resourceName);
profile = context.getProfile("http://hl7.org/fhir/StructureDefinition/"+resourceName);
ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), profile != null, "No profile found for resource type '"+resourceName+"'");
} else {
String type = profile.hasConstrainedType() ? profile.getConstrainedType() : profile.getName();
@ -891,7 +891,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String ref = profile.getAttribute("value");
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")) {
StructureDefinition pr = context.getProfiles().get(ref);
StructureDefinition pr = context.getProfile(ref);
if (warning(errors, IssueType.INVALID, element.line(), element.col(), p, pr != null, "StructureDefinition reference could not be resolved")) {
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);
@ -1008,7 +1008,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
private StructureDefinition getProfileForType(String type) throws Exception {
return context.getProfiles().get("http://hl7.org/fhir/StructureDefinition/"+type);
return context.getProfile("http://hl7.org/fhir/StructureDefinition/"+type);
}
private void validateObservation(List<ValidationMessage> errors, WrapperElement element, NodeStack stack) {
@ -1267,7 +1267,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private ElementDefinition resolveType(String type) {
String url = "http://hl7.org/fhir/StructureDefinition/"+type;
StructureDefinition sd = context.getProfiles().get(url);
StructureDefinition sd = context.getProfile(url);
if (sd == null || !sd.hasSnapshot())
return null;
else
@ -1422,7 +1422,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
private String checkResourceType(String type) {
if (context.getProfiles().containsKey("http://hl7.org/fhir/StructureDefinition/"+type))
if (context.getProfile("http://hl7.org/fhir/StructureDefinition/"+type) != null)
return type;
else
return null;
@ -1451,7 +1451,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return null;
}
else
return context.getProfiles().get(pr);
return context.getProfile(pr);
}
private StructureDefinition checkExtension(List<ValidationMessage> errors, String path, WrapperElement element, ElementDefinition def, StructureDefinition profile, NodeStack stack) throws Exception {
@ -1493,7 +1493,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
private boolean isKnownType(String code) {
return context.getProfiles().get(code.toLowerCase()) != null;
return context.getProfile(code.toLowerCase()) != null;
}
private ElementDefinition getElementByPath(StructureDefinition definition, String path) {
@ -1613,7 +1613,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private void validateContains(List<ValidationMessage> errors, String path, ElementDefinition child, ElementDefinition context, WrapperElement element, NodeStack stack, boolean needsId) throws Exception {
WrapperElement e = element.isXml() ? element.getFirstChild() : element;
String resourceName = e.getResourceType();
StructureDefinition profile = this.context.getProfiles().get("http://hl7.org/fhir/StructureDefinition/"+resourceName);
StructureDefinition profile = this.context.getProfile("http://hl7.org/fhir/StructureDefinition/"+resourceName);
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), profile != null, "No profile found for contained resource of type '"+resourceName+"'"))
validateResource(errors, e, profile, needsId, stack);
}

View File

@ -14,8 +14,10 @@ import ca.uhn.fhir.context.FhirContext;
public class FhirInstanceValidatorTest {
private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidatorTest.class);
private FhirValidator val;
@Before
public void before() {
val = ourCtx.newValidator();
@ -23,65 +25,51 @@ public class FhirInstanceValidatorTest {
val.setValidateAgainstStandardSchematron(false);
val.registerValidatorModule(new FhirInstanceValidator());
}
@Test
public void testValidateJsonResource() {
String input = "{"
+ "\"resourceType\":\"Patient\","
+ "\"id\":\"123\""
+ "}";
String input = "{" + "\"resourceType\":\"Patient\"," + "\"id\":\"123\"" + "}";
ValidationResult output = val.validateWithResult(input);
assertEquals(output.toString(), 0, output.getMessages().size());
}
@Test
public void testValidateJsonResourceBadAttributes() {
String input = "{"
+ "\"resourceType\":\"Patient\","
+ "\"id\":\"123\","
+ "\"foo\":\"123\""
+ "}";
String input = "{" + "\"resourceType\":\"Patient\"," + "\"id\":\"123\"," + "\"foo\":\"123\"" + "}";
ValidationResult output = val.validateWithResult(input);
assertEquals(output.toString(), 1, output.getMessages().size());
assertEquals("Element is unknown or does not match any slice", output.getMessages().get(0).getMessage());
}
@Test
public void testValidateResourceWithReference() {
QuestionnaireResponse resp = new QuestionnaireResponse();
resp.setStatus(QuestionnaireResponseStatus.COMPLETED);
resp.getQuestionnaire().setReference("Questionnaire/1234");
ValidationResult output = val.validateWithResult(resp);
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output.toOperationOutcome()));
// TODO: get this working
// assertEquals(output.toString(), 0, output.getMessages().size());
}
@Test
public void testValidateXmlResource() {
String input = "<Patient xmlns=\"http://hl7.org/fhir\">"
+ "<id value=\"123\"/>"
+ "</Patient>";
String input = "<Patient xmlns=\"http://hl7.org/fhir\">" + "<id value=\"123\"/>" + "</Patient>";
ValidationResult output = val.validateWithResult(input);
assertEquals(output.toString(), 0, output.getMessages().size());
}
@Test
public void testValidateXmlResourceBadAttributes() {
String input = "<Patient xmlns=\"http://hl7.org/fhir\">"
+ "<id value=\"123\"/>"
+ "<foo value=\"222\"/>"
+ "</Patient>";
String input = "<Patient xmlns=\"http://hl7.org/fhir\">" + "<id value=\"123\"/>" + "<foo value=\"222\"/>" + "</Patient>";
ValidationResult output = val.validateWithResult(input);
assertEquals(output.toString(), 1, output.getMessages().size());
assertEquals("Element is unknown or does not match any slice", output.getMessages().get(0).getMessage());
}
@Test
public void testValidateResourceWithReference() {
QuestionnaireResponse resp = new QuestionnaireResponse();
resp.setStatus(QuestionnaireResponseStatus.COMPLETED);
resp.getQuestionnaire().setReference("Questionnaire/1234");
ValidationResult output = val.validateWithResult(resp);
assertEquals(output.toString(), 0, output.getMessages().size());
}
}

View File

@ -100,6 +100,10 @@
HAPI-FHIR now has support for _summary and _elements parameters, in server, client,
and JPA server.
</action>
<action type="fix" fix="209">
_revinclude results from JPA server should have a Bundle.entry.search.mode of
"include" and not "match". Thanks to Josh Mandel for reporting!
</action>
</release>
<release version="1.1" date="2015-07-13">
<action type="add">