Fix up path management in the validator for resolving references in Bundles

This commit is contained in:
Grahame Grieve 2024-04-10 08:18:06 +10:00
parent a939e14db9
commit 0038e3a57b
5 changed files with 58 additions and 23 deletions

View File

@ -165,6 +165,10 @@ public class JsonParser extends ParserBase {
}
public Element parse(List<ValidationMessage> errors, JsonObject object) throws FHIRException {
return parse(errors, object, null);
}
public Element parse(List<ValidationMessage> errors, JsonObject object, String statedPath) throws FHIRException {
if (object == null) {
System.out.println("What?");
}
@ -187,16 +191,16 @@ public class JsonParser extends ParserBase {
return null;
}
}
path = name;
path = statedPath == null ? name : statedPath;
} else {
name = sd.getType();
path = sd.getTypeTail();
path = statedPath == null ? sd.getTypeTail() : statedPath;
}
baseElement = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities())).setFormat(FhirFormat.JSON);
checkObject(errors, object, baseElement, path);
baseElement.markLocation(line(object), col(object));
baseElement.setType(name);
baseElement.setPath(baseElement.fhirTypeRoot());
baseElement.setPath(statedPath == null ? baseElement.fhirTypeRoot() : statedPath);
parseChildren(errors, path, object, baseElement, true, null);
baseElement.numberChildren();
return baseElement;

View File

@ -167,7 +167,7 @@ public class SHCParser extends ParserBase {
return res;
}
// ok. all checks passed, we can now validate the bundle
bnd.setElement(jsonParser.parse(bnd.getErrors(), cs.getJsonObject("fhirBundle")));
bnd.setElement(jsonParser.parse(bnd.getErrors(), cs.getJsonObject("fhirBundle"), path));
bnd.setElementPath(path);
}
return res;

View File

@ -992,7 +992,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
setParents(element);
long t = System.nanoTime();
NodeStack stack = new NodeStack(context, path, element, validationLanguage);
NodeStack stack = new NodeStack(context, null, element, validationLanguage);
if (profiles == null || profiles.isEmpty()) {
validateResource(new ValidationContext(appContext, element), errors, element, element, null, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.BaseDefinition), false, false);
} else {
@ -4695,18 +4695,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else {
// work back through the parent list - if any of them are bundles, try to resolve
// the resource in the bundle
// 2024-04-05 - must work through the element parents not the stack parents, as the stack is not necessarily reflective of the full parent list
Element focus = stack.getElement();
String fullUrl = null; // we're going to try to work this out as we go up
while (stack != null && stack.getElement() != null) {
if (stack.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY && fullUrl == null && stack.getParent() != null && stack.getParent().getElement().getName().equals(ENTRY)) {
String type = stack.getParent().getParent().getElement().getChildValue(TYPE);
fullUrl = stack.getParent().getElement().getChildValue(FULL_URL); // we don't try to resolve contained references across this boundary
while (focus != null) {
// track the stack while we can
if (focus.getSpecial() == SpecialElement.BUNDLE_ENTRY && fullUrl == null && focus != null && focus.getParentForValidator().getName().equals(ENTRY)) {
String type = focus.getParentForValidator().getChildValue(TYPE);
fullUrl = focus.getParentForValidator().getChildValue(FULL_URL); // we don't try to resolve contained references across this boundary
if (fullUrl == null)
bh.see(rule(errors, NO_RULE_DATE, IssueType.REQUIRED, stack.getParent().getElement().line(), stack.getParent().getElement().col(), stack.getParent().getLiteralPath(),
bh.see(rule(errors, NO_RULE_DATE, IssueType.REQUIRED, focus.getParentForValidator().line(), focus.getParentForValidator().col(), focus.getParentForValidator().getPath(),
Utilities.existsInList(type, "batch-response", "transaction-response") || fullUrl != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFULLURL));
}
if (BUNDLE.equals(stack.getElement().getType())) {
String type = stack.getElement().getChildValue(TYPE);
IndexedElement res = getFromBundle(stack.getElement(), ref, fullUrl, errors, path, type, "transaction".equals(type), bh);
if (BUNDLE.equals(focus.getType())) {
String type = focus.getChildValue(TYPE);
IndexedElement res = getFromBundle(focus, ref, fullUrl, errors, path, type, "transaction".equals(type), bh);
if (res == null) {
return null;
} else {
@ -4714,15 +4718,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
rr.setResource(res.getMatch());
rr.setFocus(res.getMatch());
rr.setExternal(false);
rr.setStack(stack.push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(),
res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1,
res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition()));
rr.setStack(new NodeStack(context, null, res.getMatch(), validationLanguage));
rr.setVia(stack);
//
// !stack.push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(),
// res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1,
// res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition()));
rr.getStack().pathComment(rr.getResource().fhirType()+"/"+rr.getResource().getIdBase());
return rr;
}
}
if (stack.getElement().getSpecial() == SpecialElement.PARAMETER && stack.getParent() != null) {
NodeStack tgt = findInParams(stack.getParent().getParent(), ref);
if (focus.getSpecial() == SpecialElement.PARAMETER && focus.getParentForValidator() != null) {
NodeStack tgt = findInParams(focus.getParentForValidator().getParentForValidator(), ref, stack);
if (tgt != null) {
ResolvedReference rr = new ResolvedReference();
rr.setResource(tgt.getElement());
@ -4733,7 +4740,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return rr;
}
}
stack = stack.getParent();
focus = focus.getParentForValidator();
}
// we can get here if we got called via FHIRPath conformsTo which breaks the stack continuity.
if (groupingResource != null && BUNDLE.equals(groupingResource.fhirType())) { // it could also be a Parameters resource - that case isn't handled yet
@ -4759,10 +4766,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return null;
}
private NodeStack findInParams(NodeStack params, String ref) {
private NodeStack findInParams(Element params, String ref, NodeStack stack) {
int i = 0;
for (Element child : params.getElement().getChildren("parameter")) {
NodeStack p = params.push(child, i, child.getProperty().getDefinition(), child.getProperty().getDefinition());
for (Element child : params.getChildren("parameter")) {
NodeStack p = stack.push(child, i, child.getProperty().getDefinition(), child.getProperty().getDefinition());
if (child.hasChild("resource", false)) {
Element res = child.getNamedChild("resource", false);
if ((res.fhirType()+"/"+res.getIdBase()).equals(ref)) {

View File

@ -37,7 +37,7 @@ public class NodeStack {
this.context = context;
ids = new HashMap<>();
this.element = element;
literalPath = (initialPath == null ? "" : initialPath+".") + element.getPath();
literalPath = (initialPath == null ? "" : initialPath+".") + buildPathForElement(element, true);
workingLang = validationLanguage;
if (!element.getName().equals(element.fhirType())) {
logicalPaths = new HashSet<>();
@ -45,6 +45,21 @@ public class NodeStack {
}
}
private String buildPathForElement(Element e, boolean first) {
if (e.getParentForValidator() != null) {
String node = e.getName().contains("/") ? e.getName().substring(e.getName().lastIndexOf("/")+1) : e.getName();
if (e.hasIndex() && e.getProperty().isList() && e.getSpecial() == null) {
node = node+"["+Integer.toString(e.getIndex())+"]";
}
if (!first && e.isResource()) {
node = node +"/*"+e.fhirType()+"/"+e.getIdBase()+"*/";
}
return buildPathForElement(e.getParentForValidator(), false)+"."+node;
} else {
return e.getPath();
}
}
public NodeStack(IWorkerContext context, Element element, String refPath, String validationLanguage) {
this.context = context;
ids = new HashMap<>();

View File

@ -9,6 +9,7 @@ public class ResolvedReference {
private Element focus;
private boolean external;
private NodeStack stack;
private NodeStack via;
public ResolvedReference setResource(Element resource) {
this.resource = resource;
@ -50,6 +51,14 @@ public class ResolvedReference {
return focus;
}
public NodeStack getVia() {
return via;
}
public void setVia(NodeStack via) {
this.via = via;
}
public ValidationContext valContext(ValidationContext valContext, StructureDefinition profile) {
if (external) {
return valContext.forRemoteReference(profile, resource);