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 { 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) { if (object == null) {
System.out.println("What?"); System.out.println("What?");
} }
@ -187,16 +191,16 @@ public class JsonParser extends ParserBase {
return null; return null;
} }
} }
path = name; path = statedPath == null ? name : statedPath;
} else { } else {
name = sd.getType(); 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); 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); checkObject(errors, object, baseElement, path);
baseElement.markLocation(line(object), col(object)); baseElement.markLocation(line(object), col(object));
baseElement.setType(name); baseElement.setType(name);
baseElement.setPath(baseElement.fhirTypeRoot()); baseElement.setPath(statedPath == null ? baseElement.fhirTypeRoot() : statedPath);
parseChildren(errors, path, object, baseElement, true, null); parseChildren(errors, path, object, baseElement, true, null);
baseElement.numberChildren(); baseElement.numberChildren();
return baseElement; return baseElement;

View File

@ -167,7 +167,7 @@ public class SHCParser extends ParserBase {
return res; return res;
} }
// ok. all checks passed, we can now validate the bundle // 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); bnd.setElementPath(path);
} }
return res; return res;

View File

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

View File

@ -37,7 +37,7 @@ public class NodeStack {
this.context = context; this.context = context;
ids = new HashMap<>(); ids = new HashMap<>();
this.element = element; this.element = element;
literalPath = (initialPath == null ? "" : initialPath+".") + element.getPath(); literalPath = (initialPath == null ? "" : initialPath+".") + buildPathForElement(element, true);
workingLang = validationLanguage; workingLang = validationLanguage;
if (!element.getName().equals(element.fhirType())) { if (!element.getName().equals(element.fhirType())) {
logicalPaths = new HashSet<>(); 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) { public NodeStack(IWorkerContext context, Element element, String refPath, String validationLanguage) {
this.context = context; this.context = context;
ids = new HashMap<>(); ids = new HashMap<>();

View File

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