Merge pull request #1801 from hapifhir/2024-11-gg-slice-validation
2024 11 gg slice validation
This commit is contained in:
commit
1a2c25b531
|
@ -1,14 +1,16 @@
|
|||
## Validator Changes
|
||||
|
||||
* Support authentication for terminology servers (see https://confluence.hl7.org/display/FHIR/Using+fhir-settings.json)
|
||||
* Fix issue where valdiator not retaining extension context when checking constraint expressions in profiles
|
||||
* Validate min-length when found in extension
|
||||
* Correct bug parsing json-property-key values with meant validation failed
|
||||
* Fix problem validating json-property-key value pairs
|
||||
* Fix special case r5 loading of terminology to fix validation error on ExampleScenario
|
||||
* Improve handling of JSON format errors
|
||||
* Fix bug where extension slices defined in other profiles are not found when processing slices based on extension
|
||||
* Validate fhirpath expression in slice discriminators
|
||||
* Fix slicing by type and profile to allow multiple options per slice
|
||||
* List measure choices when a match by version can't be found
|
||||
* Validate fhirpath expression in slice discriminators
|
||||
|
||||
## Other code changes
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ import org.hl7.fhir.utilities.xml.SchematronWriter.Rule;
|
|||
import org.hl7.fhir.utilities.xml.SchematronWriter.SchematronType;
|
||||
import org.hl7.fhir.utilities.xml.SchematronWriter.Section;
|
||||
|
||||
|
||||
/**
|
||||
* This class provides a set of utility operations for working with Profiles.
|
||||
* Key functionality:
|
||||
|
@ -490,8 +491,8 @@ public class ProfileUtilities {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SourcedChildDefinitions getChildMap(StructureDefinition profile, ElementDefinition element) throws DefinitionException {
|
||||
String cacheKey = "cm."+profile.getVersionedUrl()+"#"+(element.hasId() ? element.getId() : element.getPath());
|
||||
public SourcedChildDefinitions getChildMap(StructureDefinition profile, ElementDefinition element, boolean chaseTypes) throws DefinitionException {
|
||||
String cacheKey = "cm."+profile.getVersionedUrl()+"#"+(element.hasId() ? element.getId() : element.getPath())+"."+chaseTypes;
|
||||
if (childMapCache.containsKey(cacheKey)) {
|
||||
return childMapCache.get(cacheKey);
|
||||
}
|
||||
|
@ -519,7 +520,7 @@ public class ProfileUtilities {
|
|||
|
||||
for (ElementDefinition e : list) {
|
||||
if (id.equals(e.getId()))
|
||||
return getChildMap(src, e);
|
||||
return getChildMap(src, e, true);
|
||||
}
|
||||
throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_NAME_REFERENCE__AT_PATH_, element.getContentReference(), element.getPath()));
|
||||
|
||||
|
@ -536,6 +537,30 @@ public class ProfileUtilities {
|
|||
} else
|
||||
break;
|
||||
}
|
||||
if (res.isEmpty() && chaseTypes) {
|
||||
// we've got no in-line children. Some consumers of this routine will figure this out for themselves but most just want to walk into
|
||||
// the type children.
|
||||
src = null;
|
||||
if (element.getType().isEmpty()) {
|
||||
throw new DefinitionException("No defined children and no type information on element '"+element.getId()+"'");
|
||||
} else if (element.getType().size() > 1) {
|
||||
throw new DefinitionException("No defined children and multiple possible types '"+element.typeSummary()+"' on element '"+element.getId()+"'");
|
||||
} else if (element.getType().get(0).getProfile().size() > 1) {
|
||||
throw new DefinitionException("No defined children and multiple possible type profiles '"+element.typeSummary()+"' on element '"+element.getId()+"'");
|
||||
} else if (element.getType().get(0).hasProfile()) {
|
||||
src = context.fetchResource(StructureDefinition.class, element.getType().get(0).getProfile().get(0).getValue());
|
||||
if (src == null) {
|
||||
throw new DefinitionException("No defined children and unknown type profile '"+element.typeSummary()+"' on element '"+element.getId()+"'");
|
||||
}
|
||||
} else {
|
||||
src = context.fetchTypeDefinition(element.getType().get(0).getWorkingCode());
|
||||
if (src == null) {
|
||||
throw new DefinitionException("No defined children and unknown type '"+element.typeSummary()+"' on element '"+element.getId()+"'");
|
||||
}
|
||||
}
|
||||
SourcedChildDefinitions scd = getChildMap(src, src.getSnapshot().getElementFirstRep(), false);
|
||||
res = scd.list;
|
||||
}
|
||||
SourcedChildDefinitions result = new SourcedChildDefinitions(src, res);
|
||||
childMapCache.put(cacheKey, result);
|
||||
return result;
|
||||
|
@ -4085,7 +4110,7 @@ public class ProfileUtilities {
|
|||
private org.hl7.fhir.r5.elementmodel.Element generateExample(StructureDefinition profile, ExampleValueAccessor accessor) throws FHIRException {
|
||||
ElementDefinition ed = profile.getSnapshot().getElementFirstRep();
|
||||
org.hl7.fhir.r5.elementmodel.Element r = new org.hl7.fhir.r5.elementmodel.Element(ed.getPath(), new Property(context, ed, profile));
|
||||
SourcedChildDefinitions children = getChildMap(profile, ed);
|
||||
SourcedChildDefinitions children = getChildMap(profile, ed, true);
|
||||
for (ElementDefinition child : children.getList()) {
|
||||
if (child.getPath().endsWith(".id")) {
|
||||
org.hl7.fhir.r5.elementmodel.Element id = new org.hl7.fhir.r5.elementmodel.Element("id", new Property(context, child, profile));
|
||||
|
@ -4107,7 +4132,7 @@ public class ProfileUtilities {
|
|||
} else {
|
||||
org.hl7.fhir.r5.elementmodel.Element res = new org.hl7.fhir.r5.elementmodel.Element(tail(ed.getPath()), new Property(context, ed, profile));
|
||||
boolean hasValue = false;
|
||||
SourcedChildDefinitions children = getChildMap(profile, ed);
|
||||
SourcedChildDefinitions children = getChildMap(profile, ed, true);
|
||||
for (ElementDefinition child : children.getList()) {
|
||||
if (!child.hasContentReference()) {
|
||||
org.hl7.fhir.r5.elementmodel.Element e = createExampleElement(profile, child, accessor);
|
||||
|
|
|
@ -93,7 +93,7 @@ public class ObjectConverter {
|
|||
if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE)
|
||||
res.setValue(((PrimitiveType) base).asStringValue());
|
||||
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep());
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep(), true);
|
||||
for (ElementDefinition child : children.getList()) {
|
||||
String n = tail(child.getPath());
|
||||
if (sd.getKind() != StructureDefinitionKind.PRIMITIVETYPE || !"value".equals(n)) {
|
||||
|
|
|
@ -385,7 +385,7 @@ public class Property {
|
|||
ElementDefinition ed = definition;
|
||||
StructureDefinition sd = structure;
|
||||
boolean isCDA = isCDAElement(structure);
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed);
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed, false);
|
||||
String url = null;
|
||||
if (children.getList().isEmpty() || isElementWithOnlyExtension(ed, children.getList())) {
|
||||
// ok, find the right definitions
|
||||
|
@ -459,7 +459,7 @@ public class Property {
|
|||
sd = context.fetchResource(StructureDefinition.class, url);
|
||||
if (sd == null)
|
||||
throw new DefinitionException("Unable to find definition '"+url+"' for type '"+t+"' for name '"+elementName+"' on property "+definition.getPath());
|
||||
children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0));
|
||||
children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0), false);
|
||||
}
|
||||
}
|
||||
List<Property> properties = new ArrayList<Property>();
|
||||
|
@ -493,7 +493,7 @@ public class Property {
|
|||
protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException {
|
||||
ElementDefinition ed = definition;
|
||||
StructureDefinition sd = structure;
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed);
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed, false);
|
||||
if (children.getList().isEmpty()) {
|
||||
// ok, find the right definitions
|
||||
String t = null;
|
||||
|
@ -519,7 +519,7 @@ public class Property {
|
|||
sd = context.fetchResource(StructureDefinition.class, t);
|
||||
if (sd == null)
|
||||
throw new DefinitionException("Unable to find class '"+t+"' for name '"+ed.getPath()+"' on property "+definition.getPath());
|
||||
children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0));
|
||||
children = profileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0), false);
|
||||
}
|
||||
}
|
||||
List<Property> properties = new ArrayList<Property>();
|
||||
|
|
|
@ -6619,7 +6619,7 @@ public class FHIRPathEngine {
|
|||
focus = element;
|
||||
} else {
|
||||
SourcedChildDefinitions childDefinitions;
|
||||
childDefinitions = profileUtilities.getChildMap(sd, element.getElement());
|
||||
childDefinitions = profileUtilities.getChildMap(sd, element.getElement(), false);
|
||||
// if that's empty, get the children of the type
|
||||
if (childDefinitions.getList().isEmpty()) {
|
||||
|
||||
|
@ -6627,7 +6627,7 @@ public class FHIRPathEngine {
|
|||
if (sd == null) {
|
||||
throw makeException(expr, I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_CANT_FIND, element.getElement().getType().get(0).getProfile(), element.getElement().getId());
|
||||
}
|
||||
childDefinitions = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep());
|
||||
childDefinitions = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep(), false);
|
||||
}
|
||||
for (ElementDefinition t : childDefinitions.getList()) {
|
||||
if (tailMatches(t, expr.getName()) && !t.hasSlicing()) { // GG: slicing is a problem here. This is for an exetnsion with a fixed value (type slicing)
|
||||
|
@ -6657,7 +6657,7 @@ public class FHIRPathEngine {
|
|||
focus = new TypedElementDefinition(sd.getSnapshot().getElementFirstRep());
|
||||
} else if ("extension".equals(expr.getName())) {
|
||||
String targetUrl = expr.getParameters().get(0).getConstant().primitiveValue();
|
||||
SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(sd, element.getElement());
|
||||
SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(sd, element.getElement(), true);
|
||||
for (ElementDefinition t : childDefinitions.getList()) {
|
||||
if (t.getPath().endsWith(".extension") && t.hasSliceName()) {
|
||||
StructureDefinition exsd = (t.getType() == null || t.getType().isEmpty() || t.getType().get(0).getProfile().isEmpty()) ?
|
||||
|
@ -6666,7 +6666,7 @@ public class FHIRPathEngine {
|
|||
exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition(), exsd);
|
||||
}
|
||||
if (exsd != null && exsd.getUrl().equals(targetUrl)) {
|
||||
if (profileUtilities.getChildMap(sd, t).getList().isEmpty()) {
|
||||
if (profileUtilities.getChildMap(sd, t, false).getList().isEmpty()) {
|
||||
sd = exsd;
|
||||
}
|
||||
focus = new TypedElementDefinition(t);
|
||||
|
|
|
@ -99,7 +99,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
if (sd == null)
|
||||
return "unknown resource " +res.fhirType();
|
||||
else {
|
||||
SourcedChildDefinitions childDefs = context.getProfileUtilities().getChildMap(sd, ed);
|
||||
SourcedChildDefinitions childDefs = context.getProfileUtilities().getChildMap(sd, ed, true);
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("; ");
|
||||
for (NamedResourceWrapperList p : res.childrenInGroups()) {
|
||||
ElementDefinition pDefn = getElementDefinition(childDefs, p);
|
||||
|
|
|
@ -2700,7 +2700,7 @@ public class StructureMapUtilities {
|
|||
|
||||
private void addChildMappings(StringBuilder b, String id, String indent, StructureDefinition sd, ElementDefinition ed, boolean inner) throws DefinitionException {
|
||||
boolean first = true;
|
||||
List<ElementDefinition> children = profileUtilities.getChildMap(sd, ed).getList();
|
||||
List<ElementDefinition> children = profileUtilities.getChildMap(sd, ed, true).getList();
|
||||
for (ElementDefinition child : children) {
|
||||
if (first && inner) {
|
||||
b.append(" then {\r\n");
|
||||
|
|
|
@ -589,6 +589,7 @@ public class I18nConstants {
|
|||
public static final String SD_ELEMENT_FIXED_WRONG_TYPE = "SD_ELEMENT_FIXED_WRONG_TYPE";
|
||||
public static final String SD_ELEMENT_NOT_IN_CONSTRAINT = "SD_ELEMENT_NOT_IN_CONSTRAINT";
|
||||
public static final String SD_ELEMENT_PATTERN_WRONG_TYPE = "SD_ELEMENT_PATTERN_WRONG_TYPE";
|
||||
public static final String SD_ELEMENT_PATTERN_NO_FIXED = "SD_ELEMENT_PATTERN_NO_FIXED";
|
||||
public static final String SD_ELEMENT_REASON_DERIVED = "SD_ELEMENT_REASON_DERIVED";
|
||||
public static final String SD_EXTENSION_URL_MISMATCH = "SD_EXTENSION_URL_MISMATCH";
|
||||
public static final String SD_EXTENSION_URL_MISSING = "SD_EXTENSION_URL_MISSING";
|
||||
|
|
|
@ -593,7 +593,8 @@ SD_ED_TYPE_PROFILE_WRONG_TYPE_one = The type {0} is not in the list of allowed t
|
|||
SD_ED_TYPE_PROFILE_WRONG_TYPE_other = The type {0} is not in the list of allowed types {1} in the profile {2}
|
||||
SD_ELEMENT_FIXED_WRONG_TYPE = The base element has a fixed type of ''{0}'', so this element must have a fixed value of the same type, not ''{1}''
|
||||
SD_ELEMENT_NOT_IN_CONSTRAINT = The element definition for {1} has a property {0} which is not allowed in a profile
|
||||
SD_ELEMENT_PATTERN_WRONG_TYPE = The base element has a pattern type of ''{0}'', so this element must have a pattern value of the same type, not ''{1}''
|
||||
SD_ELEMENT_PATTERN_WRONG_TYPE = The base element has a pattern type of ''{0}'', so this element must have a fixed value of the same type, not ''{1}''
|
||||
SD_ELEMENT_PATTERN_NO_FIXED = The base element has a pattern type of ''{0}'', so this element must have a fixed value but it doesn''t
|
||||
SD_ELEMENT_REASON_DERIVED = , because the value must match the fixed value define in ''{0}''
|
||||
SD_EXTENSION_URL_MISMATCH = The fixed value for the extension URL is {1} which doesn''t match the canonical URL {0}
|
||||
SD_EXTENSION_URL_MISSING = The value of Extension.url is not fixed to the extension URL {0}
|
||||
|
|
|
@ -6368,7 +6368,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
|
||||
// get the list of direct defined children, including slices
|
||||
SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(profile, definition);
|
||||
SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(profile, definition, false);
|
||||
if (childDefinitions.getList().isEmpty()) {
|
||||
if (actualType == null) {
|
||||
vi.setValid(false);
|
||||
|
@ -6457,7 +6457,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
throw new DefinitionException(context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_ACTUAL_TYPE_, actualType));
|
||||
trackUsage(dt, valContext, element);
|
||||
|
||||
childDefinitions = profileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0));
|
||||
childDefinitions = profileUtilities.getChildMap(dt, dt.getSnapshot().getElement().get(0), false);
|
||||
return childDefinitions;
|
||||
}
|
||||
|
||||
|
@ -6998,7 +6998,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (ed.hasFixedCoding() && "http://loinc.org".equals(ed.getFixedCoding().getSystem())) {
|
||||
return ed.getFixedCoding().getCode();
|
||||
}
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(profile, ed);
|
||||
SourcedChildDefinitions children = profileUtilities.getChildMap(profile, ed, true);
|
||||
if (children != null) {
|
||||
for (ElementDefinition t : children.getList()) {
|
||||
if (t.getPath().endsWith(".code") && t.hasFixed()) {
|
||||
|
|
|
@ -503,9 +503,13 @@ public class StructureDefinitionValidator extends BaseValidator {
|
|||
} else {
|
||||
Element pattern = element.getNamedChild("pattern");
|
||||
if (pattern != null) {
|
||||
NodeStack fn = stack.push(pattern, 0, null, null);
|
||||
if (rule(errors, "2024-03-26", IssueType.INVALID, fn, pattern.fhirType().equals(ed.getFixed().fhirType()), I18nConstants.SD_ELEMENT_PATTERN_WRONG_TYPE, pattern.fhirType(), ed.getFixed().fhirType())) {
|
||||
ok = ((org.hl7.fhir.validation.instance.InstanceValidator) parent).checkFixedValue(errors, path, pattern, ed.getFixed(), base.getVersionedUrl(), "pattern", element, true, context.formatMessage(I18nConstants.SD_ELEMENT_REASON_DERIVED, base.getVersionedUrl())) && ok;
|
||||
NodeStack fn = stack.push(pattern, 0, null, null);
|
||||
if (rule(errors, "2024-03-26", IssueType.INVALID, fn, ed.hasFixed(), I18nConstants.SD_ELEMENT_PATTERN_NO_FIXED, pattern.fhirType())) {
|
||||
if (rule(errors, "2024-03-26", IssueType.INVALID, fn, pattern.fhirType().equals(ed.getFixed().fhirType()), I18nConstants.SD_ELEMENT_PATTERN_WRONG_TYPE, pattern.fhirType(), ed.getFixed().fhirType())) {
|
||||
ok = ((org.hl7.fhir.validation.instance.InstanceValidator) parent).checkFixedValue(errors, path, pattern, ed.getFixed(), base.getVersionedUrl(), "pattern", element, true, context.formatMessage(I18nConstants.SD_ELEMENT_REASON_DERIVED, base.getVersionedUrl())) && ok;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue