fix how CodeableConcept is validated, and add Tx interaction logging by validator

This commit is contained in:
Grahame Grieve 2020-01-11 06:22:16 +11:00
parent 46e1e5edd4
commit ef085a847e
15 changed files with 240 additions and 77 deletions

View File

@ -26,7 +26,7 @@ import java.util.Map;
import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient; import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.HTMLClientLogger; import org.hl7.fhir.r5.utils.client.ToolingClientLogger;
import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities; import org.hl7.fhir.r5.model.TerminologyCapabilities;
@ -81,7 +81,7 @@ public class TerminologyClientR2 implements TerminologyClient {
} }
@Override @Override
public void setLogger(HTMLClientLogger txLog) { public void setLogger(ToolingClientLogger txLog) {
// ignored in this version - need to roll R4 internal changes back to R2 if desired // ignored in this version - need to roll R4 internal changes back to R2 if desired
} }

View File

@ -26,7 +26,7 @@ import java.util.Map;
import org.hl7.fhir.dstu3.utils.client.FHIRToolingClient; import org.hl7.fhir.dstu3.utils.client.FHIRToolingClient;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.HTMLClientLogger; import org.hl7.fhir.r5.utils.client.ToolingClientLogger;
import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities; import org.hl7.fhir.r5.model.TerminologyCapabilities;
@ -79,7 +79,7 @@ public class TerminologyClientR3 implements TerminologyClient {
} }
@Override @Override
public void setLogger(HTMLClientLogger txLog) { public void setLogger(ToolingClientLogger txLog) {
// ignored in this version - need to roll R4 internal changes back to R2 if desired // ignored in this version - need to roll R4 internal changes back to R2 if desired
} }

View File

@ -26,7 +26,7 @@ import java.util.Map;
import org.hl7.fhir.r4.utils.client.FHIRToolingClient; import org.hl7.fhir.r4.utils.client.FHIRToolingClient;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.HTMLClientLogger; import org.hl7.fhir.r5.utils.client.ToolingClientLogger;
import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities; import org.hl7.fhir.r5.model.TerminologyCapabilities;
@ -79,7 +79,7 @@ public class TerminologyClientR4 implements TerminologyClient {
} }
@Override @Override
public void setLogger(HTMLClientLogger txLog) { public void setLogger(ToolingClientLogger txLog) {
// ignored in this version - need to roll R4 internal changes back to R2 if desired // ignored in this version - need to roll R4 internal changes back to R2 if desired
} }

View File

@ -79,12 +79,14 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorCla
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple; import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.client.ToolingClientLogger;
import org.hl7.fhir.utilities.OIDUtils; import org.hl7.fhir.utilities.OIDUtils;
import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.utilities.validation.ValidationOptions.ValueSetMode;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
@ -148,7 +150,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
private boolean allowLoadingDuplicates; private boolean allowLoadingDuplicates;
protected TerminologyClient txClient; protected TerminologyClient txClient;
protected HTMLClientLogger txLog; protected ToolingClientLogger txLog;
private TerminologyCapabilities txcaps; private TerminologyCapabilities txcaps;
private boolean canRunWithoutTerminology; private boolean canRunWithoutTerminology;
protected boolean noTerminologyServer; protected boolean noTerminologyServer;
@ -593,8 +595,12 @@ public abstract class BaseWorkerContext implements IWorkerContext {
} }
private void setTerminologyOptions(ValidationOptions options, Parameters pIn) { private void setTerminologyOptions(ValidationOptions options, Parameters pIn) {
if (!Utilities.noString(options.getLanguage())) if (!Utilities.noString(options.getLanguage())) {
pIn.addParameter("displayLanguage", options.getLanguage()); pIn.addParameter("displayLanguage", options.getLanguage());
}
if (options.getValueSetMode() != ValueSetMode.ALL_CHECKS) {
pIn.addParameter("valueSetMode", options.getValueSetMode().toString());
}
} }
@Override @Override

View File

@ -241,7 +241,11 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
try { try {
tlog("Connect to "+client.getAddress()); tlog("Connect to "+client.getAddress());
txClient = client; txClient = client;
txLog = new HTMLClientLogger(log); if (log != null && log.endsWith(".txt")) {
txLog = new TextClientLogger(log);
} else {
txLog = new HTMLClientLogger(log);
}
txClient.setLogger(txLog); txClient.setLogger(txLog);
return txClient.getCapabilitiesStatementQuick().getSoftware().getVersion(); return txClient.getCapabilitiesStatementQuick().getSoftware().getVersion();
} catch (Exception e) { } catch (Exception e) {

View File

@ -0,0 +1,92 @@
package org.hl7.fhir.r5.context;
/*-
* #%L
* org.hl7.fhir.r5
* %%
* Copyright (C) 2014 - 2019 Health Level 7
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.hl7.fhir.r5.utils.client.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
public class TextClientLogger implements ToolingClientLogger {
private PrintStream file;
private int id = 0;
private String lastId;
public TextClientLogger(String log) {
if (log != null) {
try {
file = new PrintStream(new FileOutputStream(log));
} catch (FileNotFoundException e) {
}
}
}
@Override
public void logRequest(String method, String url, List<String> headers, byte[] body) {
if (file == null)
return;
id++;
lastId = Integer.toString(id);
file.println("\r\n--- "+lastId+" -----------------\r\nRequest: \r\n");
file.println(method+" "+url+" HTTP/1.0");
for (String s : headers)
file.println(Utilities.escapeXml(s));
if (body != null) {
file.println("");
try {
file.println(Utilities.escapeXml(new String(body, "UTF-8")));
} catch (UnsupportedEncodingException e) {
}
}
}
@Override
public void logResponse(String outcome, List<String> headers, byte[] body) {
if (file == null)
return;
file.println("\r\n\r\nResponse: \r\n");
file.println(outcome);
for (String s : headers)
file.println(Utilities.escapeXml(s));
if (body != null) {
file.println("");
try {
file.println(Utilities.escapeXml(new String(body, "UTF-8")));
} catch (UnsupportedEncodingException e) {
}
}
}
public String getLastId() {
return lastId;
}
public void clearLastId() {
lastId = null;
}
}

View File

@ -29,6 +29,7 @@ import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities; import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.client.ToolingClientLogger;
public interface TerminologyClient { public interface TerminologyClient {
public String getAddress(); public String getAddress();
@ -37,7 +38,7 @@ public interface TerminologyClient {
public Parameters validateCS(Parameters pin) throws FHIRException; public Parameters validateCS(Parameters pin) throws FHIRException;
public Parameters validateVS(Parameters pin) throws FHIRException; public Parameters validateVS(Parameters pin) throws FHIRException;
public void setTimeout(int i) throws FHIRException; public void setTimeout(int i) throws FHIRException;
public void setLogger(HTMLClientLogger txLog) throws FHIRException; public void setLogger(ToolingClientLogger txLog) throws FHIRException;
public CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException; public CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException;
public Parameters lookupCode(Map<String, String> params) throws FHIRException; public Parameters lookupCode(Map<String, String> params) throws FHIRException;
} }

View File

@ -31,6 +31,7 @@ import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities; import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.client.FHIRToolingClient; import org.hl7.fhir.r5.utils.client.FHIRToolingClient;
import org.hl7.fhir.r5.utils.client.ToolingClientLogger;
public class TerminologyClientR5 implements TerminologyClient { public class TerminologyClientR5 implements TerminologyClient {
@ -71,7 +72,7 @@ public class TerminologyClientR5 implements TerminologyClient {
} }
@Override @Override
public void setLogger(HTMLClientLogger txLog) { public void setLogger(ToolingClientLogger txLog) {
client.setLogger(txLog); client.setLogger(txLog);
} }

View File

@ -48,6 +48,7 @@ import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.utilities.validation.ValidationOptions.ValueSetMode;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
public class ValueSetCheckerSimple implements ValueSetChecker { public class ValueSetCheckerSimple implements ValueSetChecker {
@ -67,22 +68,24 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
// first, we validate the codings themselves // first, we validate the codings themselves
List<String> errors = new ArrayList<String>(); List<String> errors = new ArrayList<String>();
List<String> warnings = new ArrayList<String>(); List<String> warnings = new ArrayList<String>();
for (Coding c : code.getCoding()) { if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) {
if (!c.hasSystem()) for (Coding c : code.getCoding()) {
warnings.add("Coding has no system - cannot validate"); if (!c.hasSystem())
CodeSystem cs = context.fetchCodeSystem(c.getSystem()); warnings.add("Coding has no system - cannot validate");
ValidationResult res = null; CodeSystem cs = context.fetchCodeSystem(c.getSystem());
if (cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) { ValidationResult res = null;
res = context.validateCode(options.noClient(), c, null); if (cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) {
} else { res = context.validateCode(options.noClient(), c, null);
res = validateCode(c, cs); } else {
res = validateCode(c, cs);
}
if (!res.isOk())
errors.add(res.getMessage());
else if (res.getMessage() != null)
warnings.add(res.getMessage());
} }
if (!res.isOk())
errors.add(res.getMessage());
else if (res.getMessage() != null)
warnings.add(res.getMessage());
} }
if (valueset != null) { if (valueset != null && options.getValueSetMode() != ValueSetMode.NO_MEMBERSHIP_CHECK) {
boolean ok = false; boolean ok = false;
for (Coding c : code.getCoding()) { for (Coding c : code.getCoding()) {
ok = ok || codeInValueSet(c.getSystem(), c.getCode()); ok = ok || codeInValueSet(c.getSystem(), c.getCode());
@ -101,38 +104,45 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
public ValidationResult validateCode(Coding code) throws FHIRException { public ValidationResult validateCode(Coding code) throws FHIRException {
String warningMessage = null; String warningMessage = null;
// first, we validate the concept itself // first, we validate the concept itself
String system = code.hasSystem() ? code.getSystem() : getValueSetSystem();
if (system == null && !code.hasDisplay()) { // dealing with just a plain code (enum)
system = systemForCodeInValueSet(code.getCode());
}
if (!code.hasSystem())
code.setSystem(system);
boolean inExpansion = checkExpansion(code);
CodeSystem cs = context.fetchCodeSystem(system);
if (cs == null) {
warningMessage = "Unable to resolve system "+system+" - system is not specified or implicit";
if (!inExpansion)
throw new FHIRException(warningMessage);
}
if (cs!=null && cs.getContent() != CodeSystemContentMode.COMPLETE) {
warningMessage = "Unable to resolve system "+system+" - system is not complete";
if (!inExpansion)
throw new FHIRException(warningMessage);
}
ValidationResult res =null; ValidationResult res =null;
if (cs!=null) boolean inExpansion = false;
res = validateCode(code, cs); String system = code.hasSystem() ? code.getSystem() : getValueSetSystem();
if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) {
if (system == null && !code.hasDisplay()) { // dealing with just a plain code (enum)
system = systemForCodeInValueSet(code.getCode());
}
if (!code.hasSystem())
code.setSystem(system);
inExpansion = checkExpansion(code);
CodeSystem cs = context.fetchCodeSystem(system);
if (cs == null) {
warningMessage = "Unable to resolve system "+system+" - system is not specified or implicit";
if (!inExpansion)
throw new FHIRException(warningMessage);
}
if (cs!=null && cs.getContent() != CodeSystemContentMode.COMPLETE) {
warningMessage = "Unable to resolve system "+system+" - system is not complete";
if (!inExpansion)
throw new FHIRException(warningMessage);
}
if (cs!=null)
res = validateCode(code, cs);
} else {
inExpansion = checkExpansion(code);
}
// then, if we have a value set, we check it's in the value set // then, if we have a value set, we check it's in the value set
if ((res==null || res.isOk()) && valueset != null && !codeInValueSet(system, code.getCode())) { if (valueset != null && options.getValueSetMode() != ValueSetMode.NO_MEMBERSHIP_CHECK) {
if (!inExpansion) if ((res==null || res.isOk()) && !codeInValueSet(system, code.getCode())) {
res.setMessage("Not in value set "+valueset.getUrl()).setSeverity(IssueSeverity.ERROR); if (!inExpansion)
else if (warningMessage!=null) res.setMessage("Not in value set "+valueset.getUrl()).setSeverity(IssueSeverity.ERROR);
res = new ValidationResult(IssueSeverity.WARNING, "Code found in expansion, however: " + warningMessage); else if (warningMessage!=null)
else res = new ValidationResult(IssueSeverity.WARNING, "Code found in expansion, however: " + warningMessage);
res.setMessage("Code found in expansion, however: " + res.getMessage()); else
res.setMessage("Code found in expansion, however: " + res.getMessage());
}
} }
return res; return res;
} }

View File

@ -27,4 +27,8 @@ public interface ToolingClientLogger {
public void logRequest(String method, String url, List<String> headers, byte[] body); public void logRequest(String method, String url, List<String> headers, byte[] body);
public void logResponse(String outcome, List<String> headers, byte[] body); public void logResponse(String outcome, List<String> headers, byte[] body);
public String getLastId();
public void clearLastId();
} }

View File

@ -1,10 +1,16 @@
package org.hl7.fhir.utilities.validation; package org.hl7.fhir.utilities.validation;
public class ValidationOptions { public class ValidationOptions {
public enum ValueSetMode {
ALL_CHECKS, CHECK_MEMERSHIP_ONLY, NO_MEMBERSHIP_CHECK
}
private String language; private String language;
private boolean useServer = true; private boolean useServer = true;
private boolean useClient = true; private boolean useClient = true;
private boolean guessSystem = false; private boolean guessSystem = false;
private ValueSetMode valueSetMode = ValueSetMode.ALL_CHECKS;
public ValidationOptions() { public ValidationOptions() {
super(); super();
@ -67,10 +73,28 @@ public class ValidationOptions {
public String toJson() { public String toJson() {
return "\"lang\":\""+language+"\", \"useServer\":\""+Boolean.toString(useServer)+"\", \"useClient\":\""+Boolean.toString(useClient)+"\", \"guessSystem\":\""+Boolean.toString(guessSystem)+"\""; return "\"lang\":\""+language+"\", \"useServer\":\""+Boolean.toString(useServer)+"\", \"useClient\":\""+Boolean.toString(useClient)+"\", \"guessSystem\":\""+Boolean.toString(guessSystem)+"\", \"valueSetMode\":\""+valueSetMode.toString()+"\"";
} }
public static ValidationOptions defaults() { public static ValidationOptions defaults() {
return new ValidationOptions("en-US"); return new ValidationOptions("en-US");
} }
public ValidationOptions checkValueSetOnly() {
ValidationOptions n = this.copy();
n.valueSetMode = ValueSetMode.CHECK_MEMERSHIP_ONLY;
return n;
}
public ValidationOptions noCheckValueSetMembership() {
ValidationOptions n = this.copy();
n.valueSetMode = ValueSetMode.NO_MEMBERSHIP_CHECK;
return n;
}
public ValueSetMode getValueSetMode() {
return valueSetMode;
}
} }

View File

@ -530,7 +530,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
public void addAncestorProfiles(StructureDefinition sd) { public void addAncestorProfiles(StructureDefinition sd) {
if (sd.hasDerivation() && sd.getDerivation().equals(StructureDefinition.TypeDerivationRule.CONSTRAINT)) { if (sd.hasDerivation() && sd.getDerivation() == StructureDefinition.TypeDerivationRule.CONSTRAINT) {
StructureDefinition parentSd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); StructureDefinition parentSd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
if (parentSd != null && !profiles.containsKey(parentSd)) { if (parentSd != null && !profiles.containsKey(parentSd)) {
ProfileUsage pu = new ProfileUsage(parentSd); ProfileUsage pu = new ProfileUsage(parentSd);
@ -944,7 +944,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return true; // we don't validate these return true; // we don't validate these
else { else {
CodeSystem cs = getCodeSystem(system); CodeSystem cs = getCodeSystem(system);
if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null, "Unknown Code System " + system)) { if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs != null, "Unknown Code System '" + system+"'")) {
ConceptDefinitionComponent def = getCodeDefinition(cs, code); ConceptDefinitionComponent def = getCodeDefinition(cs, code);
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null, "Unknown Code (" + system + "#" + code + ")")) if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, def != null, "Unknown Code (" + system + "#" + code + ")"))
return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()), "Display should be '" + def.getDisplay() + "'"); return warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, display == null || display.equals(def.getDisplay()), "Display should be '" + def.getDisplay() + "'");
@ -962,6 +962,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: "+system+" - cannot use a value set URI as a system"); rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, "Invalid System URI: "+system+" - cannot use a value set URI as a system");
// Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back. // Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back.
} }
hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false, "Code System URI '"+system+"' is unknown so the code cannot be validated");
return true; return true;
} }
catch (Exception e) { catch (Exception e) {
@ -1076,7 +1077,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) {
// ignore this since we can't validate but it doesn't matter.. // ignore this since we can't validate but it doesn't matter..
} else { } else {
ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), cc, valueset); // we're going to validate the codings directly ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).checkValueSetOnly(), cc, valueset); // we're going to validate the codings directly, so only check the valueset
if (!vr.isOk()) { if (!vr.isOk()) {
bindingsOk = false; bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
@ -1112,12 +1113,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// to validate, we'll validate that the codes actually exist // to validate, we'll validate that the codes actually exist
if (bindingsOk) { if (bindingsOk) {
for (Coding nextCoding : cc.getCoding()) { for (Coding nextCoding : cc.getCoding()) {
String nextCode = nextCoding.getCode(); if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem())) {
String nextSystem = nextCoding.getSystem(); ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang).noCheckValueSetMembership(), nextCoding, valueset);
if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { if (vr.getSeverity() != null) {
ValidationResult vr = context.validateCode(new ValidationOptions(stack.workingLang), nextSystem, nextCode, null); if (vr.getSeverity() == IssueSeverity.INFORMATION) {
if (!vr.isOk()) { txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem); } else if (vr.getSeverity() == IssueSeverity.WARNING) {
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
} else {
txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
}
} }
} }
} }
@ -4335,9 +4340,6 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, String extensionUrl) throws FHIRException, FHIRException, IOException { Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, String extensionUrl) throws FHIRException, FHIRException, IOException {
// element.markValidation(profile, definition); // element.markValidation(profile, definition);
if (debug) {
System.out.println(" "+stack.getLiteralPath());
}
// time = System.nanoTime(); // time = System.nanoTime();
// check type invariants // check type invariants
checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false); checkInvariants(hostContext, errors, profile, definition, resource, element, stack, false);
@ -4376,6 +4378,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
public void checkChild(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, public void checkChild(ValidatorHostContext hostContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition,
Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl) Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept, boolean checkDisplayInContext, ElementInfo ei, String extensionUrl)
throws FHIRException, IOException, DefinitionException { throws FHIRException, IOException, DefinitionException {
List<String> profiles = new ArrayList<String>(); List<String> profiles = new ArrayList<String>();
if (ei.definition != null) { if (ei.definition != null) {
String type = null; String type = null;
@ -4446,6 +4449,9 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
} }
} }
NodeStack localStack = stack.push(ei.element, ei.count, ei.definition, type == null ? typeDefn : resolveType(type, ei.definition.getType())); NodeStack localStack = stack.push(ei.element, ei.count, ei.definition, type == null ? typeDefn : resolveType(type, ei.definition.getType()));
if (debug) {
System.out.println(" "+localStack.getLiteralPath());
}
String localStackLiterapPath = localStack.getLiteralPath(); String localStackLiterapPath = localStack.getLiteralPath();
String eiPath = ei.path; String eiPath = ei.path;
assert(eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.path + " - localStack.getLiteralPath: " + localStackLiterapPath; assert(eiPath.equals(localStackLiterapPath)) : "ei.path: " + ei.path + " - localStack.getLiteralPath: " + localStackLiterapPath;

View File

@ -276,6 +276,11 @@ public class Validator {
System.out.println("Specified destination (-dest parameter) is not valid: \""+dest+"\")"); System.out.println("Specified destination (-dest parameter) is not valid: \""+dest+"\")");
else { else {
// first, prepare the context // first, prepare the context
String txLog = null;
if (hasParam(args, "-txLog")) {
txLog = getParam(args, "-txLog");
new File(txLog).delete();
}
String v = getParam(args, "-version"); String v = getParam(args, "-version");
if (v == null) { if (v == null) {
v = "current"; v = "current";
@ -314,7 +319,7 @@ public class Validator {
} }
String definitions = VersionUtilities.packageForVersion(v)+"#"+v; String definitions = VersionUtilities.packageForVersion(v)+"#"+v;
System.out.println("Loading (v = "+v+", tx server http://tx.fhir.org)"); System.out.println("Loading (v = "+v+", tx server http://tx.fhir.org)");
ValidationEngine validator = new ValidationEngine(definitions, "http://tx.fhir.org", null, FhirPublication.fromCode(v)); ValidationEngine validator = new ValidationEngine(definitions, "http://tx.fhir.org", txLog, FhirPublication.fromCode(v));
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
if ("-ig".equals(args[i])) { if ("-ig".equals(args[i])) {
if (i+1 == args.length) if (i+1 == args.length)

View File

@ -129,7 +129,7 @@ public class ValidationEngineTests {
int h = hints(op); int h = hints(op);
Assert.assertTrue(e == 1); Assert.assertTrue(e == 1);
Assert.assertTrue(w == 0); Assert.assertTrue(w == 0);
Assert.assertTrue(h == 0); Assert.assertTrue(h == 1);
if (!TestUtilities.silent) if (!TestUtilities.silent)
System.out.println(" .. done: "+Integer.toString(e)+" errors, "+Integer.toString(w)+" warnings, "+Integer.toString(h)+" information messages"); System.out.println(" .. done: "+Integer.toString(e)+" errors, "+Integer.toString(w)+" warnings, "+Integer.toString(h)+" information messages");
} }

View File

@ -93,23 +93,28 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
@Test @Test
public void test() throws Exception { public void test() throws Exception {
System.out.println("Name: " + name+" - base"); System.out.println("Name: " + name+" - base");
String txLog = null;
if (content.has("txLog")) {
txLog = content.get("txLog").getAsString();
}
String v = "5.0"; String v = "5.0";
List<ValidationMessage> messages = new ArrayList<ValidationMessage>(); List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (content.has("version")) if (content.has("version")) {
v = content.get("version").getAsString(); v = content.get("version").getAsString();
}
v = VersionUtilities.getMajMin(v); v = VersionUtilities.getMajMin(v);
if (!ve.containsKey(v)) { if (!ve.containsKey(v)) {
if (v.startsWith("5.0")) if (v.startsWith("5.0"))
ve.put(v, new ValidationEngine("hl7.fhir.r5.core#current", DEF_TX, null, FhirPublication.R5, true)); ve.put(v, new ValidationEngine("hl7.fhir.r5.core#current", DEF_TX, txLog, FhirPublication.R5, true));
else if (v.startsWith("3.0")) else if (v.startsWith("3.0"))
ve.put(v, new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3, true)); ve.put(v, new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, txLog, FhirPublication.STU3, true));
else if (v.startsWith("4.0")) else if (v.startsWith("4.0"))
ve.put(v, new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, null, FhirPublication.R4, true)); ve.put(v, new ValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, txLog, FhirPublication.R4, true));
else if (v.startsWith("1.0")) else if (v.startsWith("1.0"))
ve.put(v, new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, null, FhirPublication.DSTU2, true)); ve.put(v, new ValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, txLog, FhirPublication.DSTU2, true));
else if (v.startsWith("1.4")) else if (v.startsWith("1.4"))
ve.put(v, new ValidationEngine("hl7.fhir.r2b.core#1.4.0", DEF_TX, null, FhirPublication.DSTU2016May, true)); ve.put(v, new ValidationEngine("hl7.fhir.r2b.core#1.4.0", DEF_TX, txLog, FhirPublication.DSTU2016May, true));
else else
throw new Exception("unknown version "+v); throw new Exception("unknown version "+v);
} }
@ -155,6 +160,11 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
} }
} }
List<ValidationMessage> errors = new ArrayList<ValidationMessage>(); List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
if (content.getAsJsonObject("java").has("debug")) {
val.setDebug(content.getAsJsonObject("java").get("debug").getAsBoolean());
} else {
val.setDebug(false);
}
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
@ -277,7 +287,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
} }
if (vm.getLevel() == IssueSeverity.INFORMATION) { if (vm.getLevel() == IssueSeverity.INFORMATION) {
hc++; hc++;
if (java.has("infoCount")) { if (java.has("infoCount") || java.has("debug")) {
System.out.println("hint: "+vm.getDisplay()); System.out.println("hint: "+vm.getDisplay());
} }
} }