more validation for canonical URLs

This commit is contained in:
Grahame Grieve 2021-10-20 17:06:59 +11:00
parent e21a9830b8
commit 4cdead6f80
6 changed files with 40 additions and 11 deletions

View File

@ -1093,6 +1093,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED;
} else if (it == IssueType.NOTSUPPORTED) {
err = TerminologyServiceErrorClass.VALUESET_UNSUPPORTED;
} else {
err = null;
}
} catch (FHIRException e) {
}

View File

@ -68,6 +68,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public class Utilities {
private static final String UUID_REGEX = "[0-9a-f]{8}\\-[0-9a-f]{4}\\-[0-9a-f]{4}\\-[0-9a-f]{4}\\-[0-9a-f]{12}";
private static final String OID_REGEX = "[0-2](\\.(0|[1-9][0-9]*))+";
/**
@ -1476,7 +1477,7 @@ public class Utilities {
}
final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};
static {
Arrays.sort(illegalChars);
}
@ -1493,5 +1494,13 @@ public class Utilities {
return cleanName.toString();
}
public static boolean isValidUUID(String uuid) {
return uuid.matches(UUID_REGEX);
}
public static boolean isValidOID(String oid) {
return oid.matches(OID_REGEX);
}
}

View File

@ -461,6 +461,7 @@ public class I18nConstants {
public static final String TYPE_ON_FIRST_DIFFERENTIAL_ELEMENT = "type_on_first_differential_element";
public static final String TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_ = "type_on_first_snapshot_element_for__in__from_";
public static final String TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE = "TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE";
public static final String TYPE_SPECIFIC_CHECKS_CANONICAL_CONTAINED = "TYPE_SPECIFIC_CHECKS_CANONICAL_CONTAINED";
public static final String TYPE_SPECIFIC_CHECKS_DT_ATT_NO_CONTENT = "TYPE_SPECIFIC_CHECKS_DT_ATT_NO_CONTENT";
public static final String TYPE_SPECIFIC_CHECKS_DT_ATT_NO_FETCHER = "TYPE_SPECIFIC_CHECKS_DT_ATT_NO_FETCHER";
public static final String TYPE_SPECIFIC_CHECKS_DT_ATT_SIZE_CORRECT = "TYPE_SPECIFIC_CHECKS_DT_ATT_SIZE_CORRECT";

View File

@ -208,7 +208,7 @@ Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid:
Type_Specific_Checks_DT_URI_WS = URI values cannot have whitespace(''{0}'')
Type_Specific_Checks_DT_URL_Resolve = URL value ''{0}'' does not resolve
Type_Specific_Checks_DT_UUID_Strat = UUIDs must start with urn:uuid:
Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid ({0})
Type_Specific_Checks_DT_UUID_Vaid = UUIDs must be valid
Validation_BUNDLE_Message = The first entry in a message must be a MessageHeader
Validation_VAL_Content_Unknown = Unrecognised Content {0}
Validation_VAL_NoType = Unknown type {0}
@ -469,7 +469,8 @@ MEASURE_M_GROUP_POP_NO_CODE = A measure group population should have a code when
MEASURE_M_GROUP_STRATA_NO_CODE = A measure group stratifier should have a code when there is more than one population
MEASURE_M_GROUP_STRATA_COMP_NO_CODE = A measure group stratifier component should have a code when there is more than one population
MEASURE_M_LIB_UNKNOWN = The Library {0} could not be resolved, so expression validation may not be correct
TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE = Canonical URLs must be absolute URLs if they are not fragment references ({0})
TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE = Canonical URLs must be absolute URLs if they are not fragment references ({0})
TYPE_SPECIFIC_CHECKS_CANONICAL_CONTAINED = Canonical URLs in contained resources must be absolute URLs if present ({0})
MEASURE_MR_SCORE_PROHIBITED_RT = No measureScore when the type of the report is ''data-collection''
MEASURE_MR_SCORE_PROHIBITED_MS = No measureScore when the scoring of the mesage is ''cohort''
MEASURE_MR_SCORE_REQUIRED = A measureScore is required when the Measure.scoring={0}

View File

@ -2025,23 +2025,27 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength());
if (type.equals("oid")) {
if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_OID_START))
rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_OID_VALID);
rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_OID_START);
}
if (type.equals("uuid")) {
rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_UUID_STRAT);
try {
UUID.fromString(url.substring(8));
} catch (Exception ex) {
rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_UUID_VAID, ex.getMessage());
}
}
if (type.equals("canonical")) {
rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("#") || Utilities.isAbsoluteUrl(url), I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE, url);
}
if (url != null && url.startsWith("urn:uuid:")) {
rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isValidUUID(url.substring(9)), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_UUID_VAID);
}
if (url != null && url.startsWith("urn:oid:")) {
rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_OID_VALID);
}
if (isCanonicalURLElement(e)) {
// for now, no validation. Need to think about authority.
// we get to here if this is a defining canonical URL (e.g. CodeSystem.url)
// the URL must be an IRI if present
rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isAbsoluteUrl(url),
node.isContained() ? I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_CONTAINED : I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE, url);
} else {
// now, do we check the URI target?
if (fetcher != null && !type.equals("uuid")) {
@ -4317,6 +4321,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
idstatus = IdStatus.OPTIONAL;
break;
case CONTAINED:
stack.setContained(true);
idstatus = IdStatus.REQUIRED;
break;
case PARAMETER:
@ -5372,6 +5377,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) {
return "probablility.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))";
}
if ("enableWhen.count() > 2 implies enableBehavior.exists()".equals(expr)) {
return "enableWhen.count() >= 2 implies enableBehavior.exists()";
}

View File

@ -25,6 +25,7 @@ public class NodeStack {
private String workingLang;
private Map<String, Element> ids;
private boolean resetPoint = false;
private boolean contained = false;
public NodeStack(IWorkerContext context) {
this.context = context;
@ -101,6 +102,7 @@ public class NodeStack {
res.workingLang = this.workingLang;
res.element = element;
res.definition = definition;
res.contained = contained;
res.literalPath = getLiteralPath() + sep + element.getName();
if (count > -1)
res.literalPath = res.literalPath + "[" + Integer.toString(count) + "]";
@ -195,5 +197,13 @@ public class NodeStack {
}
}
public boolean isContained() {
return contained;
}
public void setContained(boolean contained) {
this.contained = contained;
}
}