Fix to get context variables right when running invariants + fix for parent not always being populated + check type in derived profiles

This commit is contained in:
Grahame Grieve 2023-07-18 11:05:06 +10:00
parent 6c2c1b21f9
commit ced714305d
4 changed files with 55 additions and 11 deletions

View File

@ -975,3 +975,5 @@ ED_INVARIANT_EXPRESSION_ERROR = Error in invariant ''{0}'' with expression ''{1}
SNAPSHOT_IS_EMPTY = The snapshot for the profile ''{0}'' is empty (which should not happen) SNAPSHOT_IS_EMPTY = The snapshot for the profile ''{0}'' is empty (which should not happen)
TERMINOLOGY_TX_HINT = {1} TERMINOLOGY_TX_HINT = {1}
TERMINOLOGY_TX_WARNING = {1} TERMINOLOGY_TX_WARNING = {1}
SD_ED_TYPE_WRONG_TYPE_one = The element has a type {0} which is different to the type {1} on the base profile {2}
SD_ED_TYPE_WRONG_TYPE_other = The element has a type {0} which is not in the types {1} on the base profile {2}

View File

@ -368,10 +368,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
if (externalHostServices != null) { if (externalHostServices != null) {
return externalHostServices.resolveReference(c.getAppContext(), url, refContext); return setParentsBase(externalHostServices.resolveReference(c.getAppContext(), url, refContext));
} else if (fetcher != null) { } else if (fetcher != null) {
try { try {
return fetcher.fetch(InstanceValidator.this, c.getAppContext(), url); return setParents(fetcher.fetch(InstanceValidator.this, c.getAppContext(), url));
} catch (IOException e) { } catch (IOException e) {
throw new FHIRException(e); throw new FHIRException(e);
} }
@ -5620,11 +5620,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
boolean checkDisplay = true; boolean checkDisplay = true;
SpecialElement special = ei.getElement().getSpecial(); SpecialElement special = ei.getElement().getSpecial();
if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.BUNDLE_ISSUES || special == SpecialElement.PARAMETER) { // this used to say
ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, ei.getElement(), ei.getElement(), localStack, false) && ok; // if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.BUNDLE_ISSUES || special == SpecialElement.PARAMETER) {
} else { // ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, ei.getElement(), ei.getElement(), localStack, false) && ok;
ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, resource, ei.getElement(), localStack, false) && ok; // but this isn't correct - when the invariant is on the element, the invariant is in the context of the resource that contains the element.
} // changed 18-Jul 2023 - see https://chat.fhir.org/#narrow/stream/179266-fhirpath/topic/FHIRPath.20.25resource.20variable
ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, resource, ei.getElement(), localStack, false) && ok;
ei.getElement().markValidation(profile, checkDefn); ei.getElement().markValidation(profile, checkDefn);
boolean elementValidated = false; boolean elementValidated = false;
@ -5770,8 +5771,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
int index = profile.getSnapshot().getElement().indexOf(checkDefn); int index = profile.getSnapshot().getElement().indexOf(checkDefn);
if (index < profile.getSnapshot().getElement().size() - 1) { if (index < profile.getSnapshot().getElement().size() - 1) {
String nextPath = profile.getSnapshot().getElement().get(index + 1).getPath(); String nextPath = profile.getSnapshot().getElement().get(index + 1).getPath();
if (!nextPath.equals(checkDefn.getPath()) && nextPath.startsWith(checkDefn.getPath())) if (!nextPath.equals(checkDefn.getPath()) && nextPath.startsWith(checkDefn.getPath())) {
ok = validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok; if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER) {
ok = validateElement(hostContext.forEntry(ei.getElement(), null), errors, profile, checkDefn, null, null, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
} else if (ei.getElement().getSpecial() == SpecialElement.CONTAINED) {
ok = validateElement(hostContext.forContained(ei.getElement()), errors, profile, checkDefn, null, null, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
} else {
ok = validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
}
}
} }
} }
return ok; return ok;
@ -6560,11 +6568,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath; this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath;
} }
public static void setParents(Element element) { public static Element setParents(Element element) {
if (element != null && !element.hasParentForValidator()) { if (element != null && !element.hasParentForValidator()) {
element.setParentForValidator(null); element.setParentForValidator(null);
setParentsInner(element); setParentsInner(element);
} }
return element;
}
public static Base setParentsBase(Base element) {
if (element instanceof Element) {
setParents((Element) element);
}
return element;
} }
public static void setParentsInner(Element element) { public static void setParentsInner(Element element) {

View File

@ -28,6 +28,7 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.ExpressionNode; import org.hl7.fhir.r5.model.ExpressionNode;
import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.Resource;
@ -760,6 +761,9 @@ public class StructureDefinitionValidator extends BaseValidator {
String code = type.getNamedChildValue("code"); String code = type.getNamedChildValue("code");
if (code == null && path != null) { if (code == null && path != null) {
code = getTypeCodeFromSD(sd, path); code = getTypeCodeFromSD(sd, path);
} else {
Set<String> types = getTypeCodesFromSD(sd, path);
ok = rulePlural(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.getLiteralPath(), types.isEmpty() || types.contains(code), types.size(), I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TYPE, code, types.toString(), sd.getVersionedUrl());
} }
if (code != null) { if (code != null) {
List<Element> profiles = type.getChildrenByName("profile"); List<Element> profiles = type.getChildrenByName("profile");
@ -778,7 +782,7 @@ public class StructureDefinitionValidator extends BaseValidator {
} }
} }
} }
return true; return ok;
} }
private boolean validateProfileTypeOrTarget(List<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) { private boolean validateProfileTypeOrTarget(List<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) {
@ -833,6 +837,18 @@ public class StructureDefinitionValidator extends BaseValidator {
return ed != null && ed.getType().size() == 1 ? ed.getTypeFirstRep().getCode() : null; return ed != null && ed.getType().size() == 1 ? ed.getTypeFirstRep().getCode() : null;
} }
private Set<String> getTypeCodesFromSD(StructureDefinition sd, String path) {
Set<String> codes = new HashSet<>();
for (ElementDefinition t : sd.getSnapshot().getElement()) {
if (t.hasPath() && t.getPath().equals(path)) {
for (TypeRefComponent tr : t.getType()) {
codes.add(tr.getCode());
}
}
}
return codes;
}
private boolean validateTypeProfile(List<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) { private boolean validateTypeProfile(List<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) {
boolean ok = true; boolean ok = true;
String p = profile.primitiveValue(); String p = profile.primitiveValue();

View File

@ -30,14 +30,23 @@ public class ValidatorHostContext {
this.appContext = appContext; this.appContext = appContext;
this.resource = element; this.resource = element;
this.rootResource = element; this.rootResource = element;
check();
// no groupingResource (Bundle or Parameters) // no groupingResource (Bundle or Parameters)
dump("creating"); dump("creating");
} }
private void check() {
if (!rootResource.hasParentForValidator()) {
throw new Error("No parent on root resource");
}
}
public ValidatorHostContext(Object appContext, Element element, Element root) { public ValidatorHostContext(Object appContext, Element element, Element root) {
this.appContext = appContext; this.appContext = appContext;
this.resource = element; this.resource = element;
this.rootResource = root; this.rootResource = root;
check();
// no groupingResource (Bundle or Parameters) // no groupingResource (Bundle or Parameters)
dump("creating"); dump("creating");
} }
@ -47,6 +56,7 @@ public class ValidatorHostContext {
this.resource = element; this.resource = element;
this.rootResource = root; this.rootResource = root;
this.groupingResource = groupingResource; this.groupingResource = groupingResource;
check();
dump("creating"); dump("creating");
} }