Merge pull request #257 from hapifhir/gg-work

ongoing bug fixes
This commit is contained in:
Grahame Grieve 2020-06-26 09:09:21 +10:00 committed by GitHub
commit 08c4cdac4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 301 additions and 21 deletions

View File

@ -111,6 +111,11 @@ public class TerminologyClientR2 implements TerminologyClient {
public Parameters lookupCode(Map<String, String> params) throws FHIRException {
return (Parameters) VersionConvertor_10_50.convertResource(client.lookupCode(params));
}
@Override
public int getRetryCount() throws FHIRException {
return client.getRetryCount();
}
}

View File

@ -111,5 +111,10 @@ public class TerminologyClientR3 implements TerminologyClient {
public Parameters lookupCode(Map<String, String> params) throws FHIRException {
return (Parameters) VersionConvertor_30_50.convertResource(client.lookupCode(params), false);
}
@Override
public int getRetryCount() throws FHIRException {
return client.getRetryCount();
}
}

View File

@ -112,5 +112,10 @@ public class TerminologyClientR4 implements TerminologyClient {
public Parameters lookupCode(Map<String, String> params) throws FHIRException {
return (Parameters) VersionConvertor_40_50.convertResource(client.lookupCode(params));
}
@Override
public int getRetryCount() throws FHIRException {
return client.getRetryCount();
}
}

View File

@ -105,4 +105,9 @@ public class TerminologyClientR5 implements TerminologyClient {
return this;
}
@Override
public int getRetryCount() throws FHIRException {
return client.getRetryCount();
}
}

View File

@ -1393,12 +1393,24 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
protected String tail(String url) {
if (Utilities.noString(url)) {
return "noname";
if (Utilities.noString(url)) {
return "noname";
}
if (url.contains("/")) {
return url.substring(url.lastIndexOf("/")+1);
}
return url;
}
if (url.contains("/")) {
return url.substring(url.lastIndexOf("/")+1);
public int getClientRetryCount() {
return txClient == null ? 0 : txClient.getRetryCount();
}
return url;
}
public IWorkerContext setClientRetryCount(int value) {
if (txClient != null) {
txClient.setRetryCount(value);
}
return this;
}
}

View File

@ -634,5 +634,6 @@ public interface IWorkerContext {
public boolean hasPackage(String id, String ver);
public int getClientRetryCount();
public IWorkerContext setClientRetryCount(int value);
}

View File

@ -136,8 +136,19 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
}
if (i.hasEnableWhen()) {
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
defn.getPieces().add(gen.new Piece(null, "Enable When: ", null));
defn.getPieces().add(gen.new Piece(null, "todo", null));
Piece p = gen.new Piece(null, "Enable When: ", null);
defn.getPieces().add(p);
if (i.getEnableWhen().size() == 0) {
XhtmlNode x = new XhtmlNode(NodeType.Element, "span");
p.getChildren().add(x);
renderEnableWhen(x, i.getEnableWhenFirstRep());
} else {
XhtmlNode x = new XhtmlNode(NodeType.Element, "ul");
p.getChildren().add(x);
for (QuestionnaireItemEnableWhenComponent qi : i.getEnableWhen()) {
renderEnableWhen(x.li(), qi);
}
}
}
if (i.hasAnswerValueSet()) {
if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
@ -242,7 +253,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
hasExt = renderLogicItem(gen, model.getRows(), q, i) || hasExt;
}
}
XhtmlNode xn = gen.generate(model, context.getDestDir(), 1, null);
XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null);
x.getChildNodes().add(xn);
return hasExt;
}

View File

@ -122,6 +122,10 @@ public abstract class ResourceRenderer extends DataRenderer {
}
public void renderReference(ResourceWrapper rw, XhtmlNode x, Reference r, boolean allowLinks) throws UnsupportedEncodingException, IOException {
if (r == null) {
x.tx("null!");
return;
}
XhtmlNode c = null;
ResourceWithReference tr = null;
if (r.hasReferenceElement() && allowLinks) {
@ -132,6 +136,8 @@ public abstract class ResourceRenderer extends DataRenderer {
c = x.ah(tr.getReference());
else
c = x.ah(r.getReference());
} else {
c = x.ah(r.getReference());
}
} else {
c = x.span(null, null);

View File

@ -48,6 +48,7 @@ public interface TerminologyClient {
public Parameters validateVS(Parameters pin) throws FHIRException;
public TerminologyClient setTimeout(int i) throws FHIRException;
public TerminologyClient setLogger(ToolingClientLogger txLog) throws FHIRException;
public int getRetryCount() throws FHIRException;
public TerminologyClient setRetryCount(int retryCount) throws FHIRException;
public CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException;
public Parameters lookupCode(Map<String, String> params) throws FHIRException;

View File

@ -0,0 +1,32 @@
package org.hl7.fhir.r5.utils;
import java.util.Comparator;
import org.hl7.fhir.r5.model.CanonicalResource;
public class ResourceSorters {
public static class CanonicalResourceSortByUrl implements Comparator<CanonicalResource> {
@Override
public int compare(CanonicalResource arg0, CanonicalResource arg1) {
return arg0.getUrl().compareTo(arg1.getUrl());
}
}
public static class CanonicalResourceSortByTypeId implements Comparator<CanonicalResource> {
@Override
public int compare(CanonicalResource arg0, CanonicalResource arg1) {
int ret = arg0.fhirType().compareTo(arg1.fhirType());
if (ret == 0) {
ret = arg0.getId().compareTo(arg1.getId());
}
return ret;
}
}
}

View File

@ -468,6 +468,9 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
} else {
source = loadFromPackageServer(id, version);
}
if (source == null) {
throw new FHIRException("Unable to find package "+id+"#"+version);
}
return addPackageToCache(id, version == null ? source.version : version, source.stream, source.url);
}

View File

@ -14,6 +14,7 @@ public abstract class I18nBase {
private Locale locale;
private ResourceBundle i18nMessages;
private boolean warnAboutMissingMessages = true;
public Locale getLocale() {
if (Objects.nonNull(locale)) {
@ -47,8 +48,10 @@ public abstract class I18nBase {
private boolean messageExistsForLocale(String message) {
checkResourceBundleIsLoaded();
if (!i18nMessages.containsKey(message)) {
System.out.println("Attempting to localize message " + message + ", but no such equivalent message exists for" +
" the local " + getLocale());
if (warnAboutMissingMessages ) {
System.out.println("Attempting to localize message " + message + ", but no such equivalent message exists for" +
" the local " + getLocale());
}
}
return i18nMessages.containsKey(message);
}
@ -78,4 +81,14 @@ public abstract class I18nBase {
public void setValidationMessageLanguage(Locale locale) {
i18nMessages = ResourceBundle.getBundle("Messages", locale);
}
public boolean isWarnAboutMissingMessages() {
return warnAboutMissingMessages;
}
public void setWarnAboutMissingMessages(boolean warnAboutMissingMessages) {
this.warnAboutMissingMessages = warnAboutMissingMessages;
}
}

View File

@ -502,4 +502,13 @@ public class I18nConstants {
public static final String _HAS_CHILDREN__FOR_TYPE__IN_PROFILE__BUT_CANT_FIND_TYPE = "_has_children__for_type__in_profile__but_cant_find_type";
public static final String _HAS_NO_CHILDREN__AND_NO_TYPES_IN_PROFILE_ = "_has_no_children__and_no_types_in_profile_";
public static final String ALL_OK = "ALL_OK";
public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND";
public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG";
public static final String SEARCHPARAMETER_TYPE_WRONG = "SEARCHPARAMETER_TYPE_WRONG";
public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG";
public static final String VALUESET_NO_SYSTEM_WARNING = "VALUESET_NO_SYSTEM_WARNING";
public static final String VALUESET_UNC_SYSTEM_WARNING = "VALUESET_UNC_SYSTEM_WARNING";
public static final String VALUESET_UNC_SYSTEM_WARNING_VER = "VALUESET_UNC_SYSTEM_WARNING_VER";
public static final String VALUESET_INCLUDE_INVALID_CONCEPT_CODE = "VALUESET_INCLUDE_INVALID_CONCEPT_CODE";
public static final String VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER = "VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER";
}

View File

@ -502,4 +502,13 @@ VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT = Illegal constraint in profile {0} at pa
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)
SECURITY_STRING_CONTENT_ERROR = The string value contains embedded HTML tags, which are not allowed for security reasons in this context
SECURITY_STRING_CONTENT_WARNING = The string value contains embedded HTML tags. Note that all inputs should be escaped when rendered to HTML as a matter of course
ALL_OK = All OK
ALL_OK = All OK
SEARCHPARAMETER_NOTFOUND = Unable to find the base Search Parameter {0} so can't check that this SearchParameter is a proper derivation from it
SEARCHPARAMETER_BASE_WRONG = The base {1} is not listed as a base in the derivedFrom SearchParameter
SEARCHPARAMETER_TYPE_WRONG = The type {1} is different to the type {0} in the derivedFrom SearchParameter
SEARCHPARAMETER_TYPE_WRONG = The expression "{1}" is different to the expression "{0}" in the derivedFrom SearchParameter, and this likely indicates that the derivation relationship is not valid
VALUESET_NO_SYSTEM_WARNING = No System specified, so Concepts and Filters can't be checked
VALUESET_INCLUDE_INVALID_CONCEPT_CODE = The code {1} is not valid in the system {0}
VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER = The code {2} is not valid in the system {0} version {1}
VALUESET_UNC_SYSTEM_WARNING = Unknown System specified, so Concepts and Filters can't be checked
VALUESET_UNC_SYSTEM_WARNING_VER = Unknown System/Version specified, so Concepts and Filters can't be checked

View File

@ -459,9 +459,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 warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
protected boolean warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, Object... theMessageArguments) {
if (!thePass) {
addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.WARNING, null);
String message = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.WARNING, null);
}
return thePass;
}
@ -473,7 +474,7 @@ 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 warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
protected boolean warningHtml(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html) {
if (!thePass) {
addValidationMessage(errors, type, path, msg, html, IssueSeverity.WARNING, null);
}
@ -487,7 +488,7 @@ 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 warning(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
protected boolean warningHtml(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg, String html, Object... theMessageArguments) {
if (!thePass) {
String nmsg = context.formatMessage(msg, theMessageArguments);
addValidationMessage(errors, type, path, nmsg, html, IssueSeverity.WARNING, msg);

View File

@ -140,6 +140,8 @@ import org.hl7.fhir.validation.instance.type.BundleValidator;
import org.hl7.fhir.validation.instance.type.CodeSystemValidator;
import org.hl7.fhir.validation.instance.type.MeasureValidator;
import org.hl7.fhir.validation.instance.type.QuestionnaireValidator;
import org.hl7.fhir.validation.instance.type.SearchParameterValidator;
import org.hl7.fhir.validation.instance.type.ValueSetValidator;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.Utilities.DecimalStatus;
@ -3464,6 +3466,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
validateCapabilityStatement(errors, element, stack);
} else if (element.getType().equals("CodeSystem")) {
new CodeSystemValidator(context, timeTracker).validateCodeSystem(errors, element, stack);
} else if (element.getType().equals("SearchParameter")) {
new SearchParameterValidator(context, timeTracker).validateSearchParameter(errors, element, stack);
} else if (element.getType().equals("ValueSet")) {
new ValueSetValidator(context, timeTracker).validateValueSet(errors, element, stack);
}
}

View File

@ -0,0 +1,46 @@
package org.hl7.fhir.validation.instance.type;
import java.util.List;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.SearchParameter;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.utilities.Utilities;
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.validation.BaseValidator;
import org.hl7.fhir.validation.TimeTracker;
import org.hl7.fhir.validation.instance.utils.NodeStack;
public class SearchParameterValidator extends BaseValidator {
public SearchParameterValidator(IWorkerContext context, TimeTracker timeTracker) {
super(context);
source = Source.InstanceValidator;
this.timeTracker = timeTracker;
}
public void validateSearchParameter(List<ValidationMessage> errors, Element cs, NodeStack stack) {
String url = cs.getNamedChildValue("url");
String master = cs.getNamedChildValue("derivedFrom");
if (!Utilities.noString(master)) {
SearchParameter sp = context.fetchResource(SearchParameter.class, master);
if (warning(errors, IssueType.BUSINESSRULE,stack.getLiteralPath(), sp != null, I18nConstants.SEARCHPARAMETER_NOTFOUND, master)) {
// base must be in the master list of base
List<Element> bl = cs.getChildren("base");
for (Element b : bl) {
rule(errors, IssueType.BUSINESSRULE,stack.getLiteralPath(), sp.hasBase(b.primitiveValue()), I18nConstants.SEARCHPARAMETER_BASE_WRONG, master, b.primitiveValue());
}
rule(errors, IssueType.BUSINESSRULE,stack.getLiteralPath(), !cs.hasChild("type") || sp.getType().toCode().equals(cs.getNamedChildValue("type")), I18nConstants.SEARCHPARAMETER_TYPE_WRONG, master, sp.getType().toCode(), cs.getNamedChildValue("type"));
warning(errors, IssueType.BUSINESSRULE,stack.getLiteralPath(), !cs.hasChild("expression") || sp.getExpression().equals(cs.getNamedChildValue("expression")), I18nConstants.SEARCHPARAMETER_EXP_WRONG, master, sp.getExpression(), cs.getNamedChildValue("expression"));
// todo: cjeck compositions
}
}
}
}

View File

@ -0,0 +1,107 @@
package org.hl7.fhir.validation.instance.type;
import java.util.List;
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.model.Coding;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
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;
public class ValueSetValidator extends BaseValidator {
public ValueSetValidator(IWorkerContext context, TimeTracker timeTracker) {
super(context);
source = Source.InstanceValidator;
this.timeTracker = timeTracker;
}
public void validateValueSet(List<ValidationMessage> errors, Element vs, NodeStack stack) {
if (!VersionUtilities.isR2Ver(context.getVersion())) {
List<Element> composes = vs.getChildrenByName("compose");
int cc = 0;
for (Element compose : composes) {
validateValueSetCompose(errors, compose, stack.push(compose, cc, null, null));
cc++;
}
}
}
private void validateValueSetCompose(List<ValidationMessage> errors, Element compose, NodeStack stack) {
List<Element> includes = compose.getChildrenByName("include");
int ci = 0;
for (Element include : includes) {
validateValueSetInclude(errors, include, stack.push(include, ci, null, null));
ci++;
}
List<Element> excludes = compose.getChildrenByName("exclude");
int ce = 0;
for (Element exclude : excludes) {
validateValueSetInclude(errors, exclude, stack.push(exclude, ce, null, null));
ce++;
}
}
private void validateValueSetInclude(List<ValidationMessage> errors, Element include, NodeStack stack) {
String system = include.getChildValue("system");
String version = include.getChildValue("version");
boolean systemOk = true;
List<Element> concepts = include.getChildrenByName("concept");
List<Element> filters = include.getChildrenByName("filter");
if (!Utilities.noString(system)) {
int cc = 0;
for (Element concept : concepts) {
if (systemOk && !validateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version)) {
systemOk = false;
}
cc++;
}
int cf = 0;
for (Element filter : filters) {
if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version)) {
systemOk = false;
}
cf++;
}
warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), systemOk, version == null ? I18nConstants.VALUESET_UNC_SYSTEM_WARNING : I18nConstants.VALUESET_UNC_SYSTEM_WARNING_VER);
} else {
warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), filters.size() == 0 && concepts.size() == 0, I18nConstants.VALUESET_NO_SYSTEM_WARNING);
}
}
private boolean validateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String system, String version) {
String code = concept.getChildValue("code");
if (version == null) {
ValidationResult vv = context.validateCode(ValidationOptions.defaults(), new Coding(system, code, null), null);
if (vv.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
return false;
} else {
boolean ok = vv.isOk();
warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), ok, I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE, system, code);
}
} else {
ValidationResult vv = context.validateCode(ValidationOptions.defaults(), new Coding(system, code, null).setVersion(version), null);
if (vv.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
return false;
} else {
boolean ok = vv.isOk();
warning(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), ok, I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER, system, version, code);
}
}
return true;
}
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element include, NodeStack push, String system, String version) {
return true;
}
}

View File

@ -92,6 +92,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
private static JsonObject manifest;
private JsonObject content;
private String version;
private String name;
private static final String DEF_TX = "http://tx.fhir.org";
private static Map<String, ValidationEngine> ve = new HashMap<>();
@ -102,6 +103,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
public void test(String name, JsonObject content) throws Exception {
long setup = System.nanoTime();
this.content = content;
this.name = name;
System.out.println("---- " + name + " ----------------------------------------------------------------");
System.out.println("** Core: ");
String txLog = null;
@ -142,6 +144,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
String testCaseContent = TestingUtilities.loadTestResource("validator", name);
InstanceValidator val = vCurr.getValidator();
val.getContext().setClientRetryCount(4);
val.setDebug(false);
if (content.has("allowed-extension-domain"))
val.getExtensionDomains().add(content.get("allowed-extension-domain").getAsString());
@ -337,11 +340,11 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
}
}
if (!TestingUtilities.context(version).isNoTerminologyServer() || !focus.has("tx-dependent")) {
Assertions.assertEquals(java.get("errorCount").getAsInt(), ec, "Expected " + Integer.toString(java.get("errorCount").getAsInt()) + " errors, but found " + Integer.toString(ec) + ".");
Assertions.assertEquals(java.get("errorCount").getAsInt(), ec, "Test "+name+": Expected " + Integer.toString(java.get("errorCount").getAsInt()) + " errors, but found " + Integer.toString(ec) + ".");
if (java.has("warningCount"))
Assertions.assertEquals(java.get("warningCount").getAsInt(), wc, "Expected " + Integer.toString(java.get("warningCount").getAsInt()) + " warnings, but found " + Integer.toString(wc) + ".");
Assertions.assertEquals(java.get("warningCount").getAsInt(), wc, "Test "+name+": Expected " + Integer.toString(java.get("warningCount").getAsInt()) + " warnings, but found " + Integer.toString(wc) + ".");
if (java.has("infoCount"))
Assertions.assertEquals(java.get("infoCount").getAsInt(), hc, "Expected " + Integer.toString(java.get("infoCount").getAsInt()) + " hints, but found " + Integer.toString(hc) + ".");
Assertions.assertEquals(java.get("infoCount").getAsInt(), hc, "Test "+name+": Expected " + Integer.toString(java.get("infoCount").getAsInt()) + " hints, but found " + Integer.toString(hc) + ".");
}
if (java.has("error-locations")) {
JsonArray el = java.getAsJsonArray("error-locations");

View File

@ -17,7 +17,7 @@
<properties>
<hapi_fhir_version>5.0.0</hapi_fhir_version>
<validator_test_case_version>1.1.21</validator_test_case_version>
<validator_test_case_version>1.1.22-SNAPSHOT</validator_test_case_version>
<junit_jupiter_version>5.6.2</junit_jupiter_version>
<maven_surefire_version>3.0.0-M4</maven_surefire_version>
<jacoco_version>0.8.5</jacoco_version>