Gg 202110 misc validation (#617)
* NPE fixes * Smart Health Cards support in validator * Fix bug generating spreadsheets due to sheet name length limitations * Implement descendent-of filter * more NPE fixes * add Element.removeChild * fix issue generation snapshot and content reference, and work around old erroneous binding description in R4 * improve SHC validation error * fix for NPE generating ConceptMap spreadsheet * fix crash in IG publisher rendering illegal content * Improve slicing error messages * more improving error message resolving slicing * add missing code + track prohibited / required elements (improve rendering of IGs) * fix for broken links in R4B IGs * fix bug related to logger in FHIRToolingClient * fix bug related to logger in context * enable detection of whether tx server knows about value set and better track returned errors from tx server * make likely source URL visible outside ProfileUtilities * fix renderers - don't make nonvalid URLs into html links + fix NPE + * fix bug with sheetnames generating spreadsheets * supper branches in current version of packages * report slicing information automatically where slicing is based on profile + fix shc support + support codesystem-properties-mode + fix value set validation on profiles + fix wrong entry point on vaildating contained resources with profiles * fix misleading validation message + add -ips parameter for validator * hint not warning when it's an example questionnaire
This commit is contained in:
parent
df724155e0
commit
237897965b
|
@ -126,7 +126,9 @@ public class VersionConvertorContext<T> {
|
|||
*/
|
||||
public T getVersionConvertor() {
|
||||
T result = threadLocalVersionConverter.get();
|
||||
logger.debug(result.toString());
|
||||
if (result != null && logger != null) {
|
||||
logger.debug(result.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -516,7 +516,7 @@ public class FHIRToolingClient {
|
|||
return p_out;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException("Error performing operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
handleException("Error performing tx2 operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -488,39 +488,39 @@ public class FHIRToolingClient {
|
|||
// }
|
||||
|
||||
|
||||
public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) {
|
||||
boolean complex = false;
|
||||
for (ParametersParameterComponent p : params.getParameter())
|
||||
complex = complex || !(p.getValue() instanceof PrimitiveType);
|
||||
Parameters searchResults = null;
|
||||
String ps = "";
|
||||
try {
|
||||
if (!complex)
|
||||
for (ParametersParameterComponent p : params.getParameter())
|
||||
if (p.getValue() instanceof PrimitiveType)
|
||||
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue())+"&";
|
||||
ResourceRequest<T> result;
|
||||
if (complex)
|
||||
result = utils.issuePostRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG);
|
||||
else
|
||||
result = utils.issueGetResourceRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG);
|
||||
result.addErrorStatus(410);//gone
|
||||
result.addErrorStatus(404);//unknown
|
||||
result.addSuccessStatus(200);//Only one for now
|
||||
if(result.isUnsuccessfulRequest())
|
||||
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload());
|
||||
if (result.getPayload() instanceof Parameters)
|
||||
return (Parameters) result.getPayload();
|
||||
else {
|
||||
Parameters p_out = new Parameters();
|
||||
p_out.addParameter().setName("return").setResource(result.getPayload());
|
||||
return p_out;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException("Error performing operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) {
|
||||
boolean complex = false;
|
||||
for (ParametersParameterComponent p : params.getParameter())
|
||||
complex = complex || !(p.getValue() instanceof PrimitiveType);
|
||||
Parameters searchResults = null;
|
||||
String ps = "";
|
||||
try {
|
||||
if (!complex)
|
||||
for (ParametersParameterComponent p : params.getParameter())
|
||||
if (p.getValue() instanceof PrimitiveType)
|
||||
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue())+"&";
|
||||
ResourceRequest<T> result;
|
||||
if (complex)
|
||||
result = utils.issuePostRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG);
|
||||
else
|
||||
result = utils.issueGetResourceRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG);
|
||||
result.addErrorStatus(410);//gone
|
||||
result.addErrorStatus(404);//unknown
|
||||
result.addSuccessStatus(200);//Only one for now
|
||||
if(result.isUnsuccessfulRequest())
|
||||
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload());
|
||||
if (result.getPayload() instanceof Parameters)
|
||||
return (Parameters) result.getPayload();
|
||||
else {
|
||||
Parameters p_out = new Parameters();
|
||||
p_out.addParameter().setName("return").setResource(result.getPayload());
|
||||
return p_out;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException("Error performing 2b operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public Bundle transaction(Bundle batch) {
|
||||
|
|
|
@ -277,11 +277,15 @@ public class FHIRToolingClient {
|
|||
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
|
||||
if (complex) {
|
||||
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
|
||||
client.getLogger().logRequest("POST", url.toString(), null, body);
|
||||
if (client.getLogger() != null) {
|
||||
client.getLogger().logRequest("POST", url.toString(), null, body);
|
||||
}
|
||||
result = client.issuePostRequest(url, body, getPreferredResourceFormat(),
|
||||
"POST " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
} else {
|
||||
client.getLogger().logRequest("GET", url.toString(), null, null);
|
||||
if (client.getLogger() != null) {
|
||||
client.getLogger().logRequest("GET", url.toString(), null, null);
|
||||
}
|
||||
result = client.issueGetResourceRequest(url, getPreferredResourceFormat(), generateHeaders(), "GET " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
}
|
||||
if (result.isUnsuccessfulRequest()) {
|
||||
|
@ -295,7 +299,7 @@ public class FHIRToolingClient {
|
|||
return p_out;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException("Error performing operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
handleException("Error performing tx3 operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -267,32 +267,36 @@ public class FHIRToolingClient {
|
|||
String ps = "";
|
||||
try {
|
||||
if (!complex)
|
||||
for (ParametersParameterComponent p : params.getParameter())
|
||||
if (p.getValue() instanceof PrimitiveType)
|
||||
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue()) + "&";
|
||||
ResourceRequest<T> result;
|
||||
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
|
||||
if (complex) {
|
||||
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
|
||||
client.getLogger().logRequest("POST", url.toString(), null, body);
|
||||
result = client.issuePostRequest(url, body, getPreferredResourceFormat(),
|
||||
"POST " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
} else {
|
||||
client.getLogger().logRequest("GET", url.toString(), null, null);
|
||||
result = client.issueGetResourceRequest(url, getPreferredResourceFormat(), generateHeaders(), "GET " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
}
|
||||
if (result.isUnsuccessfulRequest()) {
|
||||
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload());
|
||||
}
|
||||
if (result.getPayload() instanceof Parameters) {
|
||||
return (Parameters) result.getPayload();
|
||||
} else {
|
||||
Parameters p_out = new Parameters();
|
||||
p_out.addParameter().setName("return").setResource(result.getPayload());
|
||||
return p_out;
|
||||
}
|
||||
for (ParametersParameterComponent p : params.getParameter())
|
||||
if (p.getValue() instanceof PrimitiveType)
|
||||
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue()) + "&";
|
||||
ResourceRequest<T> result;
|
||||
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
|
||||
if (complex) {
|
||||
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
|
||||
if (client.getLogger() != null) {
|
||||
client.getLogger().logRequest("POST", url.toString(), null, body);
|
||||
}
|
||||
result = client.issuePostRequest(url, body, getPreferredResourceFormat(),
|
||||
"POST " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
} else {
|
||||
if (client.getLogger() != null) {
|
||||
client.getLogger().logRequest("GET", url.toString(), null, null);
|
||||
}
|
||||
result = client.issueGetResourceRequest(url, getPreferredResourceFormat(), generateHeaders(), "GET " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
}
|
||||
if (result.isUnsuccessfulRequest()) {
|
||||
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload());
|
||||
}
|
||||
if (result.getPayload() instanceof Parameters) {
|
||||
return (Parameters) result.getPayload();
|
||||
} else {
|
||||
Parameters p_out = new Parameters();
|
||||
p_out.addParameter().setName("return").setResource(result.getPayload());
|
||||
return p_out;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException("Error performing operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
handleException("Error performing tx4 operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -2452,19 +2452,19 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
if (webUrl != null) {
|
||||
// also, must touch up the markdown
|
||||
if (element.hasDefinition())
|
||||
element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl));
|
||||
element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl, baseSpecUrl()));
|
||||
if (element.hasComment())
|
||||
element.setComment(processRelativeUrls(element.getComment(), webUrl));
|
||||
element.setComment(processRelativeUrls(element.getComment(), webUrl, baseSpecUrl()));
|
||||
if (element.hasRequirements())
|
||||
element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl));
|
||||
element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl, baseSpecUrl()));
|
||||
if (element.hasMeaningWhenMissing())
|
||||
element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl));
|
||||
element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl, baseSpecUrl()));
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
private String processRelativeUrls(String markdown, String webUrl) {
|
||||
public static String processRelativeUrls(String markdown, String webUrl, String basePath) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
int i = 0;
|
||||
while (i < markdown.length()) {
|
||||
|
@ -2487,7 +2487,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
//
|
||||
if (isLikelySourceURLReference(url)) {
|
||||
b.append("](");
|
||||
b.append(baseSpecUrl());
|
||||
b.append(basePath);
|
||||
i = i + 1;
|
||||
} else {
|
||||
b.append("](");
|
||||
|
@ -2507,11 +2507,13 @@ public class ProfileUtilities extends TranslatingUtilities {
|
|||
}
|
||||
|
||||
|
||||
private boolean isLikelySourceURLReference(String url) {
|
||||
public static boolean isLikelySourceURLReference(String url) {
|
||||
return
|
||||
url.startsWith("extensibility.html") ||
|
||||
url.startsWith("terminologies.html") ||
|
||||
url.startsWith("observation.html") ||
|
||||
url.startsWith("datatypes.html") ||
|
||||
url.startsWith("narrative.html") ||
|
||||
(url.startsWith("extension-") || url.contains(".html")) ||
|
||||
url.startsWith("resource-definitions.html");
|
||||
}
|
||||
|
|
|
@ -906,9 +906,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
|||
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
|
||||
}
|
||||
setTerminologyOptions(options, pIn);
|
||||
res = validateOnServer(vs, pIn);
|
||||
res = validateOnServer(vs, pIn, options);
|
||||
} catch (Exception e) {
|
||||
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog == null ? null : txLog.getLastId());
|
||||
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(TerminologyServiceErrorClass.SERVER_ERROR);
|
||||
}
|
||||
if (res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
|
||||
unsupportedCodeSystems.add(code.getSystem());
|
||||
|
@ -967,7 +967,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
|||
Parameters pIn = new Parameters();
|
||||
pIn.addParameter().setName("codeableConcept").setValue(code);
|
||||
setTerminologyOptions(options, pIn);
|
||||
res = validateOnServer(vs, pIn);
|
||||
res = validateOnServer(vs, pIn, options);
|
||||
} catch (Exception e) {
|
||||
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId());
|
||||
}
|
||||
|
@ -975,7 +975,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
|||
return res;
|
||||
}
|
||||
|
||||
private ValidationResult validateOnServer(ValueSet vs, Parameters pin) throws FHIRException {
|
||||
private ValidationResult validateOnServer(ValueSet vs, Parameters pin, ValidationOptions options) throws FHIRException {
|
||||
boolean cache = false;
|
||||
if (vs != null) {
|
||||
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
|
||||
|
@ -988,6 +988,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
|||
if (vs != null) {
|
||||
if (isTxCaching && cacheId != null && cached.contains(vs.getUrl()+"|"+vs.getVersion())) {
|
||||
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()+(vs.hasVersion() ? "|"+vs.getVersion() : "")));
|
||||
} else if (options.getVsAsUrl()){
|
||||
pin.addParameter().setName("url").setValue(new StringType(vs.getUrl()));
|
||||
} else {
|
||||
pin.addParameter().setName("valueSet").setResource(vs);
|
||||
cached.add(vs.getUrl()+"|"+vs.getVersion());
|
||||
|
|
|
@ -613,6 +613,11 @@ public interface IWorkerContext {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ValidationResult setErrorClass(TerminologyServiceErrorClass errorClass) {
|
||||
this.errorClass = errorClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTxLink() {
|
||||
return txLink;
|
||||
}
|
||||
|
|
|
@ -634,7 +634,14 @@ public class Element extends Base {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name+"="+fhirType() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]";
|
||||
if (name.equals(fhirType()) && isResource()) {
|
||||
return fhirType()+"/"+getIdBase() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]";
|
||||
|
||||
} else if (isResource()) {
|
||||
return name+"="+fhirType()+"/"+getIdBase()+ "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]";
|
||||
} else {
|
||||
return name+"="+fhirType() + "["+(children == null || hasValue() ? value : Integer.toString(children.size())+" children")+"]";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -62,6 +62,8 @@ public class Manager {
|
|||
return "txt";
|
||||
case VBAR:
|
||||
return "hl7";
|
||||
case SHC:
|
||||
return "shc";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -422,7 +422,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
|
|||
if (first) first = false; else td.addText(", ");
|
||||
if (pcv.hasValueCoding()) {
|
||||
td.addText(pcv.getValueCoding().getCode());
|
||||
} else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrl(pcv.getValue().primitiveValue())) {
|
||||
} else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrlLinkable(pcv.getValue().primitiveValue())) {
|
||||
td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue());
|
||||
} else {
|
||||
td.addText(pcv.getValue().primitiveValue());
|
||||
|
|
|
@ -450,8 +450,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
|||
x.addText(((Enumeration<?>) e).getValue().toString()); // todo: look up a display name if there is one
|
||||
return true;
|
||||
} else if (e instanceof BooleanType) {
|
||||
if (((BooleanType) e).getValue()) {
|
||||
if (((BooleanType) e).hasValue()) {
|
||||
x.addText(name);
|
||||
x.addText(": ");
|
||||
x.addText(((BooleanType) e).getValueAsString());
|
||||
return true;
|
||||
}
|
||||
} else if (e instanceof CodeableReference) {
|
||||
|
|
|
@ -1013,7 +1013,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
private void defn(XhtmlNode tbl, String name, String url, Resource res) throws UnsupportedEncodingException, IOException {
|
||||
if (res != null && res.hasUserData("path")) {
|
||||
defn(tbl, "Definition", RendererFactory.factory(res, context).display(res), res.getUserString("path"));
|
||||
} else if (Utilities.isAbsoluteUrl(url)) {
|
||||
} else if (Utilities.isAbsoluteUrlLinkable(url)) {
|
||||
defn(tbl, "Definition", url, url);
|
||||
} {
|
||||
defn(tbl, "Definition", url);
|
||||
|
|
|
@ -84,7 +84,7 @@ public class SpreadsheetGenerator {
|
|||
if (name.length() > MAX_SENSITIVE_SHEET_NAME_LEN - 2) {
|
||||
name = name.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN - 2);
|
||||
}
|
||||
String s = name;
|
||||
String s = fixSheetNameChars(name);
|
||||
if (sheetNames.contains(s)) {
|
||||
int i = 1;
|
||||
do {
|
||||
|
@ -96,6 +96,26 @@ public class SpreadsheetGenerator {
|
|||
return wb.createSheet(s);
|
||||
}
|
||||
|
||||
private String fixSheetNameChars(String name) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (char ch : name.toCharArray()) {
|
||||
switch (ch) {
|
||||
case '/':
|
||||
case '\\':
|
||||
case '?':
|
||||
case '*':
|
||||
case ']':
|
||||
case '[':
|
||||
case ':':
|
||||
b.append('_');
|
||||
break;
|
||||
default:
|
||||
b.append(ch);
|
||||
}
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private static Map<String, CellStyle> createStyles(Workbook wb){
|
||||
Map<String, CellStyle> styles = new HashMap<>();
|
||||
|
||||
|
|
|
@ -82,9 +82,9 @@ public class StructureDefinitionSpreadsheetGenerator extends CanonicalSpreadshee
|
|||
"Slicing Discriminator", "Slicing Description", "Slicing Ordered", "Slicing Rules", "Base Path", "Base Min", "Base Max",
|
||||
"Condition(s)", "Constraint(s)"};
|
||||
|
||||
public StructureDefinitionSpreadsheetGenerator(IWorkerContext context, boolean asXml, boolean hideMustSupportFalse) {
|
||||
public StructureDefinitionSpreadsheetGenerator(IWorkerContext context, boolean valuesAsXml, boolean hideMustSupportFalse) {
|
||||
super(context);
|
||||
this.asXml = asXml;
|
||||
this.asXml = valuesAsXml;
|
||||
this.hideMustSupportFalse = hideMustSupportFalse;
|
||||
}
|
||||
|
||||
|
|
|
@ -299,37 +299,40 @@ public class FHIRToolingClient {
|
|||
String ps = "";
|
||||
try {
|
||||
if (!complex)
|
||||
for (ParametersParameterComponent p : params.getParameter())
|
||||
if (p.getValue() instanceof PrimitiveType)
|
||||
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue()) + "&";
|
||||
ResourceRequest<T> result;
|
||||
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
|
||||
if (complex) {
|
||||
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
|
||||
client.getLogger().logRequest("POST", url.toString(), null, body);
|
||||
result = client.issuePostRequest(url, body, getPreferredResourceFormat(),
|
||||
"POST " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
} else {
|
||||
client.getLogger().logRequest("GET", url.toString(), null, null);
|
||||
result = client.issueGetResourceRequest(url, getPreferredResourceFormat(), generateHeaders(), "GET " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
}
|
||||
if (result.isUnsuccessfulRequest()) {
|
||||
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload());
|
||||
}
|
||||
if (result.getPayload() instanceof Parameters) {
|
||||
return (Parameters) result.getPayload();
|
||||
} else {
|
||||
Parameters p_out = new Parameters();
|
||||
p_out.addParameter().setName("return").setResource(result.getPayload());
|
||||
return p_out;
|
||||
}
|
||||
for (ParametersParameterComponent p : params.getParameter())
|
||||
if (p.getValue() instanceof PrimitiveType)
|
||||
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue()) + "&";
|
||||
ResourceRequest<T> result;
|
||||
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
|
||||
if (complex) {
|
||||
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
|
||||
if (client.getLogger() != null) {
|
||||
client.getLogger().logRequest("POST", url.toString(), null, body);
|
||||
}
|
||||
result = client.issuePostRequest(url, body, getPreferredResourceFormat(),
|
||||
"POST " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
} else {
|
||||
if (client.getLogger() != null) {
|
||||
client.getLogger().logRequest("GET", url.toString(), null, null);
|
||||
}
|
||||
result = client.issueGetResourceRequest(url, getPreferredResourceFormat(), generateHeaders(), "GET " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
|
||||
}
|
||||
if (result.isUnsuccessfulRequest()) {
|
||||
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload());
|
||||
}
|
||||
if (result.getPayload() instanceof Parameters) {
|
||||
return (Parameters) result.getPayload();
|
||||
} else {
|
||||
Parameters p_out = new Parameters();
|
||||
p_out.addParameter().setName("return").setResource(result.getPayload());
|
||||
return p_out;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException("Error performing operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
handleException("Error performing tx5 operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public Bundle transaction(Bundle batch) {
|
||||
Bundle transactionResult = null;
|
||||
try {
|
||||
|
|
|
@ -1098,7 +1098,17 @@ public class Utilities {
|
|||
if (ref != null && ref.contains(":")) {
|
||||
String scheme = ref.substring(0, ref.indexOf(":"));
|
||||
String details = ref.substring(ref.indexOf(":")+1);
|
||||
return (existsInList(scheme, "http", "https", "urn") || isToken(scheme) || Utilities.startsWithInList(ref, "urn:iso:", "urn:iso-iec:", "urn:iso-cie:", "urn:iso-astm:", "urn:iso-ieee:", "urn:iec:"))
|
||||
return (existsInList(scheme, "http", "https", "urn") || (isToken(scheme) && scheme.equals(scheme.toLowerCase())) || Utilities.startsWithInList(ref, "urn:iso:", "urn:iso-iec:", "urn:iso-cie:", "urn:iso-astm:", "urn:iso-ieee:", "urn:iec:"))
|
||||
&& details != null && details.length() > 0 && !details.contains(" "); // rfc5141
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isAbsoluteUrlLinkable(String ref) {
|
||||
if (ref != null && ref.contains(":")) {
|
||||
String scheme = ref.substring(0, ref.indexOf(":"));
|
||||
String details = ref.substring(ref.indexOf(":")+1);
|
||||
return (existsInList(scheme, "http", "https", "ftp"))
|
||||
&& details != null && details.length() > 0 && !details.contains(" "); // rfc5141
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -135,7 +135,7 @@ public class VersionUtilities {
|
|||
}
|
||||
|
||||
public static boolean isR5Ver(String ver) {
|
||||
return ver != null && ver.startsWith(CURRENT_VERSION);
|
||||
return ver != null && (ver.startsWith(CURRENT_VERSION) || ver.equals("current"));
|
||||
}
|
||||
|
||||
public static boolean isR4BVer(String ver) {
|
||||
|
|
|
@ -84,7 +84,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
|||
public static final String SECONDARY_SERVER = "https://packages2.fhir.org/packages";
|
||||
// private static final String SECONDARY_SERVER = "http://local.fhir.org:960/packages";
|
||||
public static final String PACKAGE_REGEX = "^[a-zA-Z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+$";
|
||||
public static final String PACKAGE_VERSION_REGEX = "^[A-Za-z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+\\#[A-Za-z0-9\\-\\_]+(\\.[A-Za-z0-9\\-\\_]+)*$";
|
||||
public static final String PACKAGE_VERSION_REGEX = "^[A-Za-z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+\\#[A-Za-z0-9\\-\\_\\$]+(\\.[A-Za-z0-9\\-\\_\\$]+)*$";
|
||||
public static final String PACKAGE_VERSION_REGEX_OPT = "^[A-Za-z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+(\\#[A-Za-z0-9\\-\\_]+(\\.[A-Za-z0-9\\-\\_]+)*)?$";
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(FilesystemPackageCacheManager.class);
|
||||
private static final String CACHE_VERSION = "3"; // second version - see wiki page
|
||||
|
@ -195,7 +195,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
|||
throw new FHIRException("Cannot add package " + id + " to the package cache - the version '" + version + "' is illegal in this context");
|
||||
}
|
||||
for (char ch : version.toCharArray()) {
|
||||
if (!Character.isAlphabetic(ch) && !Character.isDigit(ch) && !Utilities.existsInList(ch, '.', '-')) {
|
||||
if (!Character.isAlphabetic(ch) && !Character.isDigit(ch) && !Utilities.existsInList(ch, '.', '-', '$')) {
|
||||
throw new FHIRException("Cannot add package " + id + " to the package cache - the version '" + version + "' is illegal (ch '" + ch + "'");
|
||||
}
|
||||
}
|
||||
|
@ -503,9 +503,9 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
|||
|
||||
// nup, don't have it locally (or it's expired)
|
||||
FilesystemPackageCacheManager.InputStreamWithSrc source;
|
||||
if ("current".equals(version)) {
|
||||
if ("current".equals(version) || version.startsWith("current$")) {
|
||||
// special case - fetch from ci-build server
|
||||
source = loadFromCIBuild(id);
|
||||
source = loadFromCIBuild(id, version.startsWith("current$") ? version.substring(8) : null);
|
||||
} else {
|
||||
source = loadFromPackageServer(id, version);
|
||||
}
|
||||
|
@ -524,15 +524,20 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
|||
if (optional)
|
||||
return null;
|
||||
else
|
||||
throw new FHIRException(e.getMessage(), e);
|
||||
throw new FHIRException("Unable to fetch: "+e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStreamWithSrc loadFromCIBuild(String id) throws IOException {
|
||||
private InputStreamWithSrc loadFromCIBuild(String id, String branch) throws IOException {
|
||||
checkBuildLoaded();
|
||||
if (ciList.containsKey(id)) {
|
||||
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL(ciList.get(id), "package.tgz"), false);
|
||||
return new InputStreamWithSrc(stream, Utilities.pathURL(ciList.get(id), "package.tgz"), "current");
|
||||
if (branch == null) {
|
||||
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL(ciList.get(id), "package.tgz"), false);
|
||||
return new InputStreamWithSrc(stream, Utilities.pathURL(ciList.get(id), "package.tgz"), "current");
|
||||
} else {
|
||||
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL(ciList.get(id), "branches", branch, "package.tgz"), false);
|
||||
return new InputStreamWithSrc(stream, Utilities.pathURL(ciList.get(id), "branches", branch, "package.tgz"), "current$"+branch);
|
||||
}
|
||||
} else if (id.startsWith("hl7.fhir.r5")) {
|
||||
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL("http://build.fhir.org", id + ".tgz"), false);
|
||||
return new InputStreamWithSrc(stream, Utilities.pathURL("http://build.fhir.org", id + ".tgz"), "current");
|
||||
|
|
|
@ -514,6 +514,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
|
|||
public String[] sliceText;
|
||||
private boolean slicingHint;
|
||||
private boolean signpost;
|
||||
private boolean criticalSignpost;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -772,9 +773,10 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
|
|||
return sliceHtml;
|
||||
}
|
||||
|
||||
public void setSliceHtml(String sliceHtml, String[] text) {
|
||||
public ValidationMessage setSliceHtml(String sliceHtml, String[] text) {
|
||||
this.sliceHtml = sliceHtml;
|
||||
this.sliceText = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMessageId() {
|
||||
|
@ -795,5 +797,14 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
|
|||
return this;
|
||||
}
|
||||
|
||||
public boolean isCriticalSignpost() {
|
||||
return criticalSignpost;
|
||||
}
|
||||
|
||||
public ValidationMessage setCriticalSignpost(boolean criticalSignpost) {
|
||||
this.criticalSignpost = criticalSignpost;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -11,6 +11,7 @@ public class ValidationOptions {
|
|||
private boolean useClient = true;
|
||||
private boolean guessSystem = false;
|
||||
private ValueSetMode valueSetMode = ValueSetMode.ALL_CHECKS;
|
||||
private boolean vsAsUrl;
|
||||
|
||||
public ValidationOptions() {
|
||||
super();
|
||||
|
@ -42,7 +43,8 @@ public class ValidationOptions {
|
|||
ValidationOptions n = new ValidationOptions(language);
|
||||
n.useServer = useServer;
|
||||
n.useClient = useClient;
|
||||
n.guessSystem = guessSystem;
|
||||
n.guessSystem = guessSystem;
|
||||
n.vsAsUrl = vsAsUrl;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -96,5 +98,14 @@ public class ValidationOptions {
|
|||
return valueSetMode;
|
||||
}
|
||||
|
||||
public ValidationOptions setVsAsUrl() {
|
||||
vsAsUrl = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean getVsAsUrl() {
|
||||
return vsAsUrl;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -229,7 +229,6 @@ Validation_VAL_Profile_SliceOrder = As specified by profile {0}, Element ''{1}''
|
|||
Validation_VAL_Profile_Unknown = Profile reference ''{0}'' has not been checked because it is unknown
|
||||
VALIDATION_VAL_PROFILE_UNKNOWN_NOT_POLICY = Profile reference ''{0}'' has not been checked because it is unknown, and the validator is set to not fetch unknown profiles
|
||||
VALIDATION_VAL_PROFILE_UNKNOWN_ERROR = Profile reference ''{0}'' has not been checked because it is unknown, and fetching it resulted in the error {1}
|
||||
Validation_VAL_Profile_WrongType = Specified profile type was ''{0}'', but found type ''{1}''
|
||||
Validation_VAL_Unknown_Profile = Unknown profile {0}
|
||||
XHTML_XHTML_Attribute_Illegal = Illegal attribute name in the XHTML (''{0}'' on ''{1}'')
|
||||
XHTML_XHTML_Element_Illegal = Illegal element name in the XHTML (''{0}'')
|
||||
|
@ -503,7 +502,7 @@ TYPE_SPECIFIC_CHECKS_DT_ATT_TOO_LONG = Attachment size is {0} bytes which exceed
|
|||
TYPE_SPECIFIC_CHECKS_DT_ATT_NO_CONTENT = Attachments have data and/or url, or else SHOULD have either contentType and/or language
|
||||
TYPE_SPECIFIC_CHECKS_DT_BASE64_TOO_LONG = Base64 size is {0} bytes which exceeds the stated limit of {1} bytes
|
||||
TYPE_SPECIFIC_CHECKS_DT_DECIMAL_CHARS = Found {0} decimal places which exceeds the stated limit of {1} digits
|
||||
Validation_VAL_Profile_WrongType = Specified profile type was ''{0}'', but found type ''{1}''
|
||||
Validation_VAL_Profile_WrongType = Specified profile type was ''{0}'' in profile ''{2}'', but found type ''{1}''
|
||||
Validation_VAL_Profile_WrongType2 = Type mismatch processing profile {0} at path {1}: The element type is {4}, but the profile {3} is for a different type {2}
|
||||
VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT = Illegal constraint in profile {0} at path {1} - cannot constrain to type {2} from base types {3}
|
||||
EXTENSION_EXT_CONTEXT_WRONG_XVER = The extension {0} from FHIR version {3} is not allowed to be used at this point (allowed = {1}; this element is [{2}; this is a warning since contexts may be renamed between FHIR versions)
|
||||
|
|
|
@ -2,7 +2,11 @@ package org.hl7.fhir.validation;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
/*
|
||||
|
@ -262,9 +266,9 @@ public class BaseValidator {
|
|||
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
|
||||
*/
|
||||
//FIXME: formatMessage should be done here
|
||||
protected boolean slicingHint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, String msg, String html, String[] text) {
|
||||
protected boolean slicingHint(List<ValidationMessage> errors, IssueType type, int line, int col, String path, boolean thePass, boolean isCritical, String msg, String html, String[] text) {
|
||||
if (!thePass) {
|
||||
addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION, null).setSlicingHint(true).setSliceHtml(html, text);
|
||||
addValidationMessage(errors, type, line, col, path, msg, IssueSeverity.INFORMATION, null).setSlicingHint(true).setSliceHtml(html, text).setCriticalSignpost(isCritical);
|
||||
}
|
||||
return thePass;
|
||||
}
|
||||
|
|
|
@ -139,6 +139,8 @@ public class IgLoader {
|
|||
res.cntType = Manager.FhirFormat.XML;
|
||||
else if (t.getKey().endsWith(".ttl"))
|
||||
res.cntType = Manager.FhirFormat.TURTLE;
|
||||
else if (t.getKey().endsWith(".shc"))
|
||||
res.cntType = Manager.FhirFormat.SHC;
|
||||
else if (t.getKey().endsWith(".txt") || t.getKey().endsWith(".map"))
|
||||
res.cntType = Manager.FhirFormat.TEXT;
|
||||
else
|
||||
|
|
|
@ -71,6 +71,8 @@ import org.hl7.fhir.validation.cli.utils.*;
|
|||
import java.io.File;
|
||||
import java.net.Authenticator;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A executable class that will validate one or more FHIR resources against
|
||||
|
@ -101,6 +103,8 @@ public class ValidatorCli {
|
|||
TimeTracker tt = new TimeTracker();
|
||||
TimeTracker.Session tts = tt.start("Loading");
|
||||
|
||||
args = preProcessArgs(args);
|
||||
|
||||
Display.displayVersion();
|
||||
Display.displaySystemInfo();
|
||||
|
||||
|
@ -160,6 +164,35 @@ public class ValidatorCli {
|
|||
}
|
||||
}
|
||||
|
||||
private static String[] preProcessArgs(String[] args) {
|
||||
// ips$branch --> -version 4.0 -ig hl7.fhir.uv.ips#current$connectathon-2 -profile http://hl7.org/fhir/uv/ips/StructureDefinition/Bundle-uv-ips
|
||||
List<String> res = new ArrayList<>();
|
||||
for (String a : args) {
|
||||
if (a.equals("-ips")) {
|
||||
res.add("-version");
|
||||
res.add("4.0");
|
||||
res.add("-ig");
|
||||
res.add("hl7.fhir.uv.ips#current$connectathon-2");
|
||||
res.add("-profile");
|
||||
res.add("http://hl7.org/fhir/uv/ips/StructureDefinition/Bundle-uv-ips");
|
||||
} else if (a.startsWith("-ips$")) {
|
||||
res.add("-version");
|
||||
res.add("4.0");
|
||||
res.add("-ig");
|
||||
res.add("hl7.fhir.uv.ips#current$"+a.substring(5));
|
||||
res.add("-profile");
|
||||
res.add("http://hl7.org/fhir/uv/ips/StructureDefinition/Bundle-uv-ips");
|
||||
} else {
|
||||
res.add(a);
|
||||
}
|
||||
}
|
||||
String[] r = new String[res.size()];
|
||||
for (int i = 0; i < res.size(); i++) {
|
||||
r[i] = res.get(i);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static boolean destinationDirectoryValid(String dest) {
|
||||
if (dest == null) {
|
||||
System.out.println("no -dest parameter provided");
|
||||
|
|
|
@ -293,14 +293,10 @@ public class ValidationService {
|
|||
System.out.println((error == 0 ? "Success" : "*FAILURE*") + ": " + Integer.toString(error) + " errors, " + Integer.toString(warn) + " warnings, " + Integer.toString(info) + " notes");
|
||||
for (OperationOutcome.OperationOutcomeIssueComponent issue : oo.getIssue()) {
|
||||
System.out.println(getIssueSummary(issue));
|
||||
if (crumbs) {
|
||||
ValidationMessage vm = (ValidationMessage) issue.getUserData("source.msg");
|
||||
if (vm != null) {
|
||||
if (vm.sliceText != null) {
|
||||
for (String s : vm.sliceText) {
|
||||
System.out.println(" slice info: "+s);
|
||||
}
|
||||
}
|
||||
ValidationMessage vm = (ValidationMessage) issue.getUserData("source.msg");
|
||||
if (vm != null && vm.sliceText != null && (crumbs || vm.isCriticalSignpost())) {
|
||||
for (String s : vm.sliceText) {
|
||||
System.out.println(" slice info: "+s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType;
|
|||
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.PropertyRepresentation;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
|
||||
|
@ -303,7 +304,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
} else if (item instanceof Element) {
|
||||
Element e = (Element) item;
|
||||
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage));
|
||||
if (e.getName().equals("contained")) {
|
||||
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage));
|
||||
} else {
|
||||
self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage));
|
||||
}
|
||||
} else
|
||||
throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT));
|
||||
boolean ok = true;
|
||||
|
@ -2753,7 +2758,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (!isShowMessagesFromReferences()) {
|
||||
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile()));
|
||||
for (StructureDefinition sd : badProfiles.keySet()) {
|
||||
slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false,
|
||||
slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, false,
|
||||
context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()),
|
||||
errorSummaryForSlicingAsHtml(badProfiles.get(sd)), errorSummaryForSlicingAsText(badProfiles.get(sd)));
|
||||
}
|
||||
|
@ -2771,7 +2776,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (!isShowMessagesFromReferences()) {
|
||||
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet()));
|
||||
for (StructureDefinition sd : badProfiles.keySet()) {
|
||||
slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()),
|
||||
slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, false, context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()),
|
||||
errorSummaryForSlicingAsHtml(badProfiles.get(sd)), errorSummaryForSlicingAsText(badProfiles.get(sd)));
|
||||
}
|
||||
} else {
|
||||
|
@ -2898,6 +2903,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return "<ul>" + b.toString() + "</ul>";
|
||||
}
|
||||
|
||||
private boolean isCritical(List<ValidationMessage> list) {
|
||||
for (ValidationMessage vm : list) {
|
||||
if (vm.isSlicingHint() && vm.isCriticalSignpost()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String[] errorSummaryForSlicingAsText(List<ValidationMessage> list) {
|
||||
List<String> res = new ArrayList<String>();
|
||||
for (ValidationMessage vm : list) {
|
||||
|
@ -3305,7 +3319,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
rr.setExternal(false);
|
||||
rr.setStack(nstack);
|
||||
// rr.getStack().qualifyPath(".ofType("+nstack.getElement().fhirType()+")");
|
||||
System.out.println("-->"+nstack.getLiteralPath());
|
||||
// System.out.println("-->"+nstack.getLiteralPath());
|
||||
return rr;
|
||||
}
|
||||
if (nstack.getElement().getSpecial() == SpecialElement.CONTAINED) {
|
||||
|
@ -3675,9 +3689,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
ValidatorHostContext shc = hostContext.forSlicing();
|
||||
boolean pass = evaluateSlicingExpression(shc, element, path, profile, n);
|
||||
if (!pass) {
|
||||
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, (context.formatMessage(I18nConstants.DOES_NOT_MATCH_SLICE_, ed.getSliceName())), "discriminator = " + Utilities.escapeXml(n.toString()), null);
|
||||
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, isProfile(slicer), (context.formatMessage(I18nConstants.DOES_NOT_MATCH_SLICE_, ed.getSliceName())), "discriminator = " + Utilities.escapeXml(n.toString()), null);
|
||||
for (String url : shc.getSliceRecords().keySet()) {
|
||||
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false,
|
||||
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, isProfile(slicer),
|
||||
context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, stack.getLiteralPath(), url),
|
||||
context.formatMessage(I18nConstants.PROFILE__DOES_NOT_MATCH_FOR__BECAUSE_OF_THE_FOLLOWING_PROFILE_ISSUES__,
|
||||
url,
|
||||
|
@ -3687,6 +3701,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return pass;
|
||||
}
|
||||
|
||||
private boolean isProfile(ElementDefinition slicer) {
|
||||
if (slicer == null || !slicer.hasSlicing()) {
|
||||
return false;
|
||||
}
|
||||
for (ElementDefinitionSlicingDiscriminatorComponent t : slicer.getSlicing().getDiscriminator()) {
|
||||
if (t.getType() == DiscriminatorType.PROFILE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean evaluateSlicingExpression(ValidatorHostContext hostContext, Element element, String path, StructureDefinition profile, ExpressionNode n) throws FHIRException {
|
||||
String msg;
|
||||
boolean ok;
|
||||
|
@ -4898,7 +4924,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (ei.additionalSlice && ei.definition != null) {
|
||||
if (ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPEN) ||
|
||||
ei.definition.getSlicing().getRules().equals(ElementDefinition.SlicingRules.OPENATEND) && true /* TODO: replace "true" with condition to check that this element is at "end" */) {
|
||||
slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false,
|
||||
slicingHint(errors, IssueType.INFORMATIONAL, ei.line(), ei.col(), ei.getPath(), false, isProfile(slicer) || isCritical(ei.sliceInfo),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_,
|
||||
profile == null ? "" : " defined in the profile " + profile.getUrl()),
|
||||
context.formatMessage(I18nConstants.THIS_ELEMENT_DOES_NOT_MATCH_ANY_KNOWN_SLICE_, profile == null ? "" : I18nConstants.DEFINED_IN_THE_PROFILE + profile.getUrl()) + errorSummaryForSlicingAsHtml(ei.sliceInfo),
|
||||
|
@ -4944,6 +4970,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return problematicPaths;
|
||||
}
|
||||
|
||||
|
||||
public List<ElementInfo> listChildren(Element element, NodeStack stack) {
|
||||
// 1. List the children, and remember their exact path (convenience)
|
||||
List<ElementInfo> children = new ArrayList<ElementInfo>();
|
||||
|
@ -5197,7 +5224,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
// todo: validate everything in this bundle.
|
||||
}
|
||||
ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), typeMatchesDefn(resourceName, defn), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE, defn.getName(), resourceName);
|
||||
ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), typeMatchesDefn(resourceName, defn), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE, defn.getType(), resourceName, defn.getName());
|
||||
|
||||
if (ok) {
|
||||
if (idstatus == IdStatus.REQUIRED && (element.getNamedChild(ID) == null)) {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -192,9 +192,13 @@ public class QuestionnaireValidator extends BaseValidator {
|
|||
hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, I18nConstants.QUESTIONNAIRE_QR_Q_NONE);
|
||||
if (ok) {
|
||||
Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire);
|
||||
ok = questionnaireMode == QuestionnaireMode.REQUIRED ?
|
||||
rule(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire) :
|
||||
warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire);
|
||||
if (questionnaireMode == QuestionnaireMode.REQUIRED) {
|
||||
ok = rule(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire);
|
||||
} else if (questionnaire.startsWith("http://example.org")) {
|
||||
ok = hint(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire);
|
||||
} else {
|
||||
ok = warning(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire);
|
||||
}
|
||||
if (ok) {
|
||||
boolean inProgress = "in-progress".equals(element.getNamedChildValue("status"));
|
||||
validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element));
|
||||
|
|
|
@ -15,10 +15,12 @@ import org.hl7.fhir.convertors.factory.*;
|
|||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.conformance.ProfileUtilities;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.ExpressionNode;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
|
@ -35,6 +37,7 @@ import org.hl7.fhir.utilities.i18n.I18nConstants;
|
|||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||
import org.hl7.fhir.validation.BaseValidator;
|
||||
import org.hl7.fhir.validation.TimeTracker;
|
||||
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||
|
@ -209,13 +212,23 @@ public class StructureDefinitionValidator extends BaseValidator {
|
|||
String ref = valueSet.hasPrimitiveValue() ? valueSet.primitiveValue() : valueSet.getNamedChildValue("reference");
|
||||
if (warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || ref != null, I18nConstants.SD_ED_SHOULD_BIND_WITH_VS, path)) {
|
||||
Resource vs = context.fetchResource(Resource.class, ref);
|
||||
if (warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs != null, I18nConstants.SD_ED_BIND_UNKNOWN_VS, path, ref)) {
|
||||
rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType());
|
||||
|
||||
// just because we can't resolve it directly doesn't mean that terminology server can't. Check with it
|
||||
|
||||
if (warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs != null || serverSupportsValueSet(ref), I18nConstants.SD_ED_BIND_UNKNOWN_VS, path, ref)) {
|
||||
if (vs != null) {
|
||||
rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean serverSupportsValueSet(String ref) {
|
||||
ValidationResult vr = context.validateCode(new ValidationOptions().checkValueSetOnly().setVsAsUrl().noClient(), new Coding("http://loinc.org", "5792-7", null), new ValueSet().setUrl(ref));
|
||||
return vr.getErrorClass() == null;
|
||||
}
|
||||
|
||||
private void validateElementType(List<ValidationMessage> errors, Element type, NodeStack stack, StructureDefinition sd, String path) {
|
||||
String code = type.getNamedChildValue("code");
|
||||
if (code == null && path != null) {
|
||||
|
|
|
@ -26,11 +26,20 @@ public class ValidatorHostContext {
|
|||
}
|
||||
|
||||
public ValidatorHostContext(Object appContext, Element element) {
|
||||
this.appContext = appContext;
|
||||
this.resource = element;
|
||||
this.rootResource = element;
|
||||
// no container
|
||||
}
|
||||
this.appContext = appContext;
|
||||
this.resource = element;
|
||||
this.rootResource = element;
|
||||
// no container
|
||||
dump("creating");
|
||||
}
|
||||
|
||||
public ValidatorHostContext(Object appContext, Element element, Element root) {
|
||||
this.appContext = appContext;
|
||||
this.resource = element;
|
||||
this.rootResource = root;
|
||||
// no container
|
||||
dump("creating");
|
||||
}
|
||||
|
||||
public Object getAppContext() {
|
||||
return appContext;
|
||||
|
@ -52,6 +61,7 @@ public class ValidatorHostContext {
|
|||
|
||||
public ValidatorHostContext setRootResource(Element rootResource) {
|
||||
this.rootResource = rootResource;
|
||||
dump("setting root resource");
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -96,6 +106,7 @@ public class ValidatorHostContext {
|
|||
res.rootResource = resource;
|
||||
res.resource = element;
|
||||
res.profile = profile;
|
||||
res.dump("forContained");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -104,6 +115,7 @@ public class ValidatorHostContext {
|
|||
res.rootResource = element;
|
||||
res.resource = element;
|
||||
res.profile = profile;
|
||||
res.dump("forEntry");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -113,6 +125,7 @@ public class ValidatorHostContext {
|
|||
res.rootResource = rootResource;
|
||||
res.profile = profile;
|
||||
res.sliceRecords = sliceRecords != null ? sliceRecords : new HashMap<String, List<ValidationMessage>>();
|
||||
res.dump("forProfile "+profile.getUrl());
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -122,15 +135,24 @@ public class ValidatorHostContext {
|
|||
res.rootResource = resource;
|
||||
res.profile = profile;
|
||||
res.checkSpecials = false;
|
||||
res.dump("forLocalReference "+profile.getUrl());
|
||||
return res;
|
||||
}
|
||||
|
||||
private void dump(String ctxt) {
|
||||
// System.out.println("** app = "+(appContext == null ? "(null)" : appContext.toString())+", res = "+resource.toString()+", root = "+rootResource.toString()+" ("+ctxt+")");
|
||||
// if (rootResource.getName().equals("contained")) {
|
||||
// System.out.println("** something is wrong!");
|
||||
// }
|
||||
}
|
||||
|
||||
public ValidatorHostContext forRemoteReference(StructureDefinition profile, Element resource) {
|
||||
ValidatorHostContext res = new ValidatorHostContext(appContext);
|
||||
res.resource = resource;
|
||||
res.rootResource = resource;
|
||||
res.profile = profile;
|
||||
res.checkSpecials = false;
|
||||
res.dump("forRemoteReference "+profile.getUrl());
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -141,6 +163,7 @@ public class ValidatorHostContext {
|
|||
res.profile = profile;
|
||||
res.checkSpecials = false;
|
||||
res.sliceRecords = new HashMap<String, List<ValidationMessage>>();
|
||||
res.dump("forSlicing");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue