Add new validation for must-support on types / profiles / targets + improve extension validation

This commit is contained in:
Grahame Grieve 2020-09-25 03:25:59 +10:00
parent c2fb9e8fd3
commit 73d5a845bf
6 changed files with 118 additions and 5 deletions

View File

@ -341,6 +341,8 @@ public class I18nConstants {
public static final String RESOURCE_TYPE_MISMATCH_FOR___ = "Resource_type_mismatch_for___";
public static final String SAME_ID_ON_MULTIPLE_ELEMENTS__IN_ = "Same_id_on_multiple_elements__in_";
public static final String SD_MUST_HAVE_DERIVATION = "SD_MUST_HAVE_DERIVATION";
public static final String SD_NESTED_MUST_SUPPORT_DIFF = "SD_NESTED_MUST_SUPPORT_DIFF";
public static final String SD_NESTED_MUST_SUPPORT_SNAPSHOT = "SD_NESTED_MUST_SUPPORT_SNAPSHOT";
public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG";
public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG";
public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND";

View File

@ -608,4 +608,7 @@ REFERENCE_REF_SUSPICIOUS = The syntax of the reference ''{0}'' looks incorrect,
TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS = UCUM Codes that contain human readable annotations like {0} can be misleading. Best Practice is not to use annotations in the UCUM code, and rather to make sure that Quantity.unit is correctly human readable
XHTML_XHTML_ELEMENT_ILLEGAL_IN_PARA = Illegal element name inside in a paragraph in the XHTML (''{0}'')
UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported property {3} on type {2} for pattern for discriminator({0}) for slice {1}
UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported: no properties with values found on type {2} for pattern for discriminator({0}) for slice {1}
UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported: no properties with values found on type {2} for pattern for discriminator({0}) for slice {1}
SD_NESTED_MUST_SUPPORT_DIFF = The element {0} has types/profiles/targets that are marked as must support, but the element itself is not marked as must-support. The inner must-supports will be ignored unless the element inherits must-support = true
SD_NESTED_MUST_SUPPORT_SNAPSHOT = The element {0} has types/profiles/targets that are marked as must support, but the element itself is not marked as must-support

View File

@ -296,9 +296,10 @@ public class BaseValidator {
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
protected boolean hint(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.INFORMATION, null);
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.INFORMATION, null);
}
return thePass;
}

View File

@ -483,7 +483,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean isKnownExtension(String url) {
// Added structuredefinition-expression and following extensions explicitly because they weren't defined in the version of the spec they need to be used with
if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression") || url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION))
if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression"))
return true;
for (String s : extensionDomains)
if (url.startsWith(s))
@ -1521,6 +1521,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (extensionUrl.equals(profile.getUrl())) {
rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url), I18nConstants.EXTENSION_EXT_SUBEXTENSION_INVALID, url, profile.getUrl());
}
} else if (SpecialExtensions.isKnownExtension(url)) {
ex = SpecialExtensions.getDefinition(url);
} else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN_NOTHERE, url)) {
hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN, url);
}
@ -1556,6 +1558,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ex;
}
private boolean hasExtensionSlice(StructureDefinition profile, String sliceName) {
for (ElementDefinition ed : profile.getSnapshot().getElement()) {
if (ed.getPath().equals("Extension.extension.url") && ed.hasFixed() && sliceName.equals(ed.getFixed().primitiveValue())) {
@ -1933,7 +1936,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (fetcher != null) {
boolean found;
try {
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url);
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url) ||
SpecialExtensions.isKnownExtension(url);
} catch (IOException e1) {
found = false;
}

File diff suppressed because one or more lines are too long

View File

@ -28,6 +28,7 @@ import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.SearchParameter;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
@ -95,6 +96,69 @@ public class StructureDefinitionValidator extends BaseValidator {
} catch (FHIRException | IOException e) {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
}
List<Element> differentials = src.getChildrenByName("differential");
List<Element> snapshots = src.getChildrenByName("snapshot");
for (Element differential : differentials) {
validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0);
}
for (Element snapshot : snapshots) {
validateElementList(errors, snapshot, stack.push(snapshot, -1, null, null), true, true);
}
}
private void validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot) {
List<Element> elements = elementList.getChildrenByName("element");
int cc = 0;
for (Element element : elements) {
validateElementDefinition(errors, element, stack.push(element, cc, null, null), snapshot, hasSnapshot);
cc++;
}
}
private void validateElementDefinition(List<ValidationMessage> errors, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot) {
boolean typeMustSupport = false;
List<Element> types = element.getChildrenByName("type");
for (Element type : types) {
if (hasMustSupportExtension(type)) {
typeMustSupport = true;
}
}
if (typeMustSupport) {
if (snapshot) {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_SNAPSHOT, element.getNamedChildValue("path"));
} else {
hint(errors, IssueType.EXCEPTION, stack.getLiteralPath(), hasSnapshot || "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_DIFF, element.getNamedChildValue("path"));
}
}
}
private boolean hasMustSupportExtension(Element type) {
if ("true".equals(getExtensionValue(type, ToolingExtensions.EXT_MUST_SUPPORT))) {
return true;
}
List<Element> profiles = type.getChildrenByName("profile");
for (Element profile : profiles) {
if ("true".equals(getExtensionValue(profile, ToolingExtensions.EXT_MUST_SUPPORT))) {
return true;
}
}
profiles = type.getChildrenByName("targetProfile");
for (Element profile : profiles) {
if ("true".equals(getExtensionValue(profile, ToolingExtensions.EXT_MUST_SUPPORT))) {
return true;
}
}
return false;
}
private String getExtensionValue(Element element, String url) {
List<Element> extensions = element.getChildrenByName("extension");
for (Element extension : extensions) {
if (url.equals(extension.getNamedChildValue("url"))) {
return extension.getNamedChildValue("value");
}
}
return null;
}
private StructureDefinition loadAsSD(Element src) throws FHIRException, IOException {