mirror of
https://github.com/hapifhir/org.hl7.fhir.core.git
synced 2025-02-07 05:18:14 +00:00
better validation of example references by validator
This commit is contained in:
parent
d4701a2cad
commit
300c87a657
@ -157,6 +157,14 @@ public interface IResourceValidator {
|
|||||||
public boolean isAssumeValidRestReferences();
|
public boolean isAssumeValidRestReferences();
|
||||||
public void setAssumeValidRestReferences(boolean value);
|
public void setAssumeValidRestReferences(boolean value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if this is true, the validator will accept extensions and references to example.org and acme.com as
|
||||||
|
* valid, on the basis that they are understood to be references to content that could exist in priniple but can't in practice
|
||||||
|
*/
|
||||||
|
public boolean isAllowExamples();
|
||||||
|
public void setAllowExamples(boolean value) ;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate suite
|
* Validate suite
|
||||||
*
|
*
|
||||||
|
@ -511,6 +511,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||||||
private IValidationProfileUsageTracker tracker;
|
private IValidationProfileUsageTracker tracker;
|
||||||
private ValidatorHostServices validatorServices;
|
private ValidatorHostServices validatorServices;
|
||||||
private boolean assumeValidRestReferences;
|
private boolean assumeValidRestReferences;
|
||||||
|
private boolean allowExamples;
|
||||||
|
|
||||||
public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) {
|
public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices) {
|
||||||
super();
|
super();
|
||||||
@ -591,9 +592,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||||||
this.assumeValidRestReferences = value;
|
this.assumeValidRestReferences = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAllowExamples() {
|
||||||
|
return this.allowExamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowExamples(boolean value) {
|
||||||
|
this.allowExamples = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean allowUnknownExtension(String url) {
|
private boolean allowUnknownExtension(String url) {
|
||||||
if (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"))
|
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"))
|
||||||
// Added structuredefinition-expression explicitly because it wasn't defined in the version of the spec it needs to be used with
|
// Added structuredefinition-expression explicitly because it wasn't defined in the version of the spec it needs to be used with
|
||||||
return true;
|
return true;
|
||||||
for (String s : extensionDomains)
|
for (String s : extensionDomains)
|
||||||
@ -604,7 +613,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||||||
|
|
||||||
private boolean isKnownExtension(String url) {
|
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
|
// 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 (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") || url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION))
|
||||||
return true;
|
return true;
|
||||||
for (String s : extensionDomains)
|
for (String s : extensionDomains)
|
||||||
if (url.startsWith(s))
|
if (url.startsWith(s))
|
||||||
@ -1622,19 +1631,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||||||
boolean ok = false;
|
boolean ok = false;
|
||||||
CommaSeparatedStringBuilder contexts = new CommaSeparatedStringBuilder();
|
CommaSeparatedStringBuilder contexts = new CommaSeparatedStringBuilder();
|
||||||
List<String> plist = new ArrayList<>();
|
List<String> plist = new ArrayList<>();
|
||||||
|
plist.add(stripIndexes(stack.getLiteralPath()));
|
||||||
|
for (String s : stack.getLogicalPaths()) {
|
||||||
|
String p = stripIndexes(s);
|
||||||
|
// all extensions are always allowed in ElementDefinition.example.value, and in fixed and pattern values. TODO: determine the logical paths from the path stated in the element definition....
|
||||||
|
if (Utilities.existsInList(p, "ElementDefinition.example.value", "ElementDefinition.pattern", "ElementDefinition.fixed")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
plist.add(p);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) {
|
for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) {
|
||||||
if (ok) { break; }
|
if (ok) { break; }
|
||||||
if (ctxt.getType() == ExtensionContextType.ELEMENT) {
|
if (ctxt.getType() == ExtensionContextType.ELEMENT) {
|
||||||
if (plist.isEmpty()) {
|
|
||||||
plist.add(stripIndexes(stack.getLiteralPath()));
|
|
||||||
for (String s : stack.getLogicalPaths()) {
|
|
||||||
plist.add(stripIndexes(s));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String en = ctxt.getExpression();
|
String en = ctxt.getExpression();
|
||||||
contexts.append("e:"+en);
|
contexts.append("e:"+en);
|
||||||
if (en.equals("Element")) {
|
if ("Element".equals(en)) {
|
||||||
ok = true;
|
ok = true;
|
||||||
} else if (en.equals("Resource") && container.isResource()) {
|
} else if (en.equals("Resource") && container.isResource()) {
|
||||||
ok = true;
|
ok = true;
|
||||||
@ -1898,22 +1911,23 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()), "boolean values must be 'true' or 'false'");
|
rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()), "boolean values must be 'true' or 'false'");
|
||||||
}
|
}
|
||||||
if (type.equals("uri") || type.equals("oid") || type.equals("uuid") || type.equals("url") || type.equals("canonical")) {
|
if (type.equals("uri") || type.equals("oid") || type.equals("uuid") || type.equals("url") || type.equals("canonical")) {
|
||||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !e.primitiveValue().startsWith("oid:"), "URI values cannot start with oid:");
|
String url = e.primitiveValue();
|
||||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !e.primitiveValue().startsWith("uuid:"), "URI values cannot start with uuid:");
|
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"), "URI values cannot start with oid:");
|
||||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().equals(e.primitiveValue().trim().replace(" ", ""))
|
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"), "URI values cannot start with uuid:");
|
||||||
|
rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", ""))
|
||||||
// work around an old invalid example in a core package
|
// work around an old invalid example in a core package
|
||||||
&& !"http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(e.primitiveValue()), "URI values cannot have whitespace('"+e.primitiveValue()+"')");
|
&& !"http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url), "URI values cannot have whitespace('"+url+"')");
|
||||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength()==0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength());
|
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength()==0 || url.length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength());
|
||||||
|
|
||||||
|
|
||||||
if (type.equals("oid")) {
|
if (type.equals("oid")) {
|
||||||
if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().startsWith("urn:oid:"), "OIDs must start with urn:oid:"))
|
if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:oid:"), "OIDs must start with urn:oid:"))
|
||||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(e.primitiveValue().substring(8)), "OIDs must be valid");
|
rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isOid(url.substring(8)), "OIDs must be valid");
|
||||||
}
|
}
|
||||||
if (type.equals("uuid")) {
|
if (type.equals("uuid")) {
|
||||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().startsWith("urn:uuid:"), "UUIDs must start with urn:uuid:");
|
rule(errors, IssueType.INVALID, e.line(), e.col(), path, url.startsWith("urn:uuid:"), "UUIDs must start with urn:uuid:");
|
||||||
try {
|
try {
|
||||||
UUID.fromString(e.primitiveValue().substring(8));
|
UUID.fromString(url.substring(8));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "UUIDs must be valid ("+ex.getMessage()+")");
|
rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, "UUIDs must be valid ("+ex.getMessage()+")");
|
||||||
}
|
}
|
||||||
@ -1923,11 +1937,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||||||
if (fetcher != null) {
|
if (fetcher != null) {
|
||||||
boolean found;
|
boolean found;
|
||||||
try {
|
try {
|
||||||
found = fetcher.resolveURL(appContext, path, e.primitiveValue());
|
found = (allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || fetcher.resolveURL(appContext, path, url);
|
||||||
} catch (IOException e1) {
|
} catch (IOException e1) {
|
||||||
found = false;
|
found = false;
|
||||||
}
|
}
|
||||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, "URL value '"+e.primitiveValue()+"' does not resolve");
|
rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, "URL value '"+url+"' does not resolve");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (type.equals("id")) {
|
if (type.equals("id")) {
|
||||||
@ -2236,7 +2250,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||||||
we = ext == null ? null : makeExternalRef(ext, path);
|
we = ext == null ? null : makeExternalRef(ext, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS, "Unable to resolve resource '"+ref+"'");
|
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS), "Unable to resolve resource '"+ref+"'");
|
||||||
}
|
}
|
||||||
|
|
||||||
String ft;
|
String ft;
|
||||||
|
@ -175,6 +175,11 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
|
|||||||
} else {
|
} else {
|
||||||
val.setDebug(false);
|
val.setDebug(false);
|
||||||
}
|
}
|
||||||
|
if (content.has("examples")) {
|
||||||
|
val.setAllowExamples(content.get("examples").getAsBoolean());
|
||||||
|
} else {
|
||||||
|
val.setAllowExamples(true);
|
||||||
|
}
|
||||||
if (name.endsWith(".json"))
|
if (name.endsWith(".json"))
|
||||||
val.validate(null, errors, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON);
|
val.validate(null, errors, IOUtils.toInputStream(testCaseContent, Charsets.UTF_8), FhirFormat.JSON);
|
||||||
else
|
else
|
||||||
@ -412,7 +417,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException {
|
public boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException {
|
||||||
return true;
|
return !url.contains("example.org");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
2
pom.xml
2
pom.xml
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<hapi_fhir_version>4.1.0</hapi_fhir_version>
|
<hapi_fhir_version>4.1.0</hapi_fhir_version>
|
||||||
<validator_test_case_version>1.0.33-SNAPSHOT</validator_test_case_version>
|
<validator_test_case_version>1.0.34-SNAPSHOT</validator_test_case_version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<artifactId>org.hl7.fhir.core</artifactId>
|
<artifactId>org.hl7.fhir.core</artifactId>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user