fix how CodeableConcept is validated, and add Tx interaction logging by validator
This commit is contained in:
parent
46e1e5edd4
commit
ef085a847e
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue