Merge pull request #1399 from hapifhir/2023-08-gg-tx-tests-externals-2

2023 08 gg tx tests externals 2
This commit is contained in:
Grahame Grieve 2023-08-20 05:12:57 +10:00 committed by GitHub
commit 46bae75c40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 457 additions and 211 deletions

View File

@ -5,6 +5,7 @@
* Add checking around internal status consistency and across dependencies (draft/experimental/retired/deprecated)
* Improved error messages on server failure
* Fix bug in warning about No valid Display Names found
* Use Supplements when validating display names
* Fix issue in FHIRPath .combine focus handling
* Check Extension fixed values for URLs - enforce consistency
* Fix R4 FML parser problem

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.convertors.txClient;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.EnumSet;
import java.util.HashMap;
@ -8,10 +9,12 @@ import java.util.Map;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.utils.client.EFhirClientException;
import org.hl7.fhir.r4.utils.client.FHIRToolingClient;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
@ -93,16 +96,38 @@ public class TerminologyClientR4 implements ITerminologyClient {
@Override
public Parameters validateCS(Parameters pin) throws FHIRException {
org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) VersionConvertorFactory_40_50.convertResource(pin);
p2 = client.operateType(org.hl7.fhir.r4.model.CodeSystem.class, "validate-code", p2);
return (Parameters) VersionConvertorFactory_40_50.convertResource(p2);
try {
org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) VersionConvertorFactory_40_50.convertResource(pin);
p2 = client.operateType(org.hl7.fhir.r4.model.CodeSystem.class, "validate-code", p2);
return (Parameters) VersionConvertorFactory_40_50.convertResource(p2);
} catch (EFhirClientException e) {
if (e.getServerErrors().size() == 1) {
OperationOutcome op = (OperationOutcome) VersionConvertorFactory_40_50.convertResource(e.getServerErrors().get(0));
throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getMessage(), op, e);
} else {
throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getMessage(), e);
}
} catch (IOException e) {
throw new FHIRException(e);
}
}
@Override
public Parameters validateVS(Parameters pin) throws FHIRException {
org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) VersionConvertorFactory_40_50.convertResource(pin);
p2 = client.operateType(org.hl7.fhir.r4.model.ValueSet.class, "validate-code", p2);
return (Parameters) VersionConvertorFactory_40_50.convertResource(p2);
try {
org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) VersionConvertorFactory_40_50.convertResource(pin);
p2 = client.operateType(org.hl7.fhir.r4.model.ValueSet.class, "validate-code", p2);
return (Parameters) VersionConvertorFactory_40_50.convertResource(p2);
} catch (EFhirClientException e) {
if (e.getServerErrors().size() == 1) {
OperationOutcome op = (OperationOutcome) VersionConvertorFactory_40_50.convertResource(e.getServerErrors().get(0));
throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getMessage(), op, e);
} else {
throw new org.hl7.fhir.r5.utils.client.EFhirClientException(e.getMessage(), e);
}
} catch (IOException e) {
throw new FHIRException(e);
}
}
@Override

View File

@ -522,37 +522,31 @@ public class FHIRToolingClient {
complex = complex || !(p.getValue() instanceof PrimitiveType);
Parameters searchResults = null;
String ps = "";
try {
if (!complex)
for (ParametersParameterComponent p : params.getParameter())
if (p.getValue() instanceof PrimitiveType)
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue()) + "&";
ResourceRequest<T> result;
if (complex)
result = utils.issuePostRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps),
utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())),
getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG);
else
result = utils.issueGetResourceRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps),
getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG);
result.addErrorStatus(410);// gone
result.addErrorStatus(404);// unknown
result.addSuccessStatus(200);// Only one for now
if (result.isUnsuccessfulRequest())
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
(OperationOutcome) result.getPayload());
if (result.getPayload() instanceof Parameters)
return (Parameters) result.getPayload();
else {
Parameters p_out = new Parameters();
p_out.addParameter().setName("return").setResource(result.getPayload());
return p_out;
}
} catch (Exception e) {
handleException(
"Error performing tx2 operation '" + name + ": " + e.getMessage() + "' (parameters = \"" + ps + "\")", e);
if (!complex)
for (ParametersParameterComponent p : params.getParameter())
if (p.getValue() instanceof PrimitiveType)
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue()) + "&";
ResourceRequest<T> result;
if (complex)
result = utils.issuePostRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps),
utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())),
getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG);
else
result = utils.issueGetResourceRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps),
getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG);
result.addErrorStatus(410);// gone
result.addErrorStatus(404);// unknown
result.addSuccessStatus(200);// Only one for now
if (result.isUnsuccessfulRequest())
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
(OperationOutcome) result.getPayload());
if (result.getPayload() instanceof Parameters)
return (Parameters) result.getPayload();
else {
Parameters p_out = new Parameters();
p_out.addParameter().setName("return").setResource(result.getPayload());
return p_out;
}
return null;
}
public Bundle transaction(Bundle batch) {

View File

@ -1,5 +1,7 @@
package org.hl7.fhir.r4.terminologies;
import java.io.IOException;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -29,9 +31,11 @@ package org.hl7.fhir.r4.terminologies;
*/
import java.net.URISyntaxException;
import java.util.Map;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.context.HTMLClientLogger;
import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.CodeSystem;
@ -65,12 +69,20 @@ public class TerminologyClientR4 implements TerminologyClient {
@Override
public Parameters validateCS(Parameters pin) {
return client.operateType(CodeSystem.class, "validate-code", pin);
try {
return client.operateType(CodeSystem.class, "validate-code", pin);
} catch (IOException e) {
throw new FHIRException(e);
}
}
@Override
public Parameters validateVS(Parameters pin) {
return client.operateType(ValueSet.class, "validate-code", pin);
try {
return client.operateType(ValueSet.class, "validate-code", pin);
} catch (IOException e) {
throw new FHIRException(e);
}
}
@Override

View File

@ -278,42 +278,36 @@ public class FHIRToolingClient {
return result.getPayload();
}
public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) {
public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) throws IOException {
boolean complex = false;
for (ParametersParameterComponent p : params.getParameter())
complex = complex || !(p.getValue() instanceof PrimitiveType);
String ps = "";
try {
if (!complex)
for (ParametersParameterComponent p : params.getParameter())
if (p.getValue() instanceof PrimitiveType)
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue()) + "&";
ResourceRequest<T> result;
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
if (complex) {
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
result = client.issuePostRequest(url, body, getPreferredResourceFormat(), generateHeaders(),
"POST " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
} else {
result = client.issueGetResourceRequest(url, getPreferredResourceFormat(), generateHeaders(),
"GET " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
}
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
(OperationOutcome) result.getPayload());
}
if (result.getPayload() instanceof Parameters) {
return (Parameters) result.getPayload();
} else {
Parameters p_out = new Parameters();
p_out.addParameter().setName("return").setResource(result.getPayload());
return p_out;
}
} catch (Exception e) {
handleException(
"Error performing tx4 operation '" + name + ": " + e.getMessage() + "' (parameters = \"" + ps + "\")", e);
if (!complex)
for (ParametersParameterComponent p : params.getParameter())
if (p.getValue() instanceof PrimitiveType)
ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue()) + "&";
ResourceRequest<T> result;
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
if (complex) {
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
result = client.issuePostRequest(url, body, getPreferredResourceFormat(), generateHeaders(),
"POST " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
} else {
result = client.issueGetResourceRequest(url, getPreferredResourceFormat(), generateHeaders(),
"GET " + resourceClass.getName() + "/$" + name, TIMEOUT_OPERATION_LONG);
}
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
(OperationOutcome) result.getPayload());
}
if (result.getPayload() instanceof Parameters) {
return (Parameters) result.getPayload();
} else {
Parameters p_out = new Parameters();
p_out.addParameter().setName("return").setResource(result.getPayload());
return p_out;
}
return null;
}
public Bundle transaction(Bundle batch) {
@ -390,16 +384,16 @@ public class FHIRToolingClient {
parameters.put("url", vsUrl);
org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand", parameters),
getPreferredResourceFormat(), generateHeaders(), "ValueSet/$expand?url=" + vsUrl, TIMEOUT_OPERATION_EXPAND);
try {
result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand", parameters),
getPreferredResourceFormat(), generateHeaders(), "ValueSet/$expand?url=" + vsUrl, TIMEOUT_OPERATION_EXPAND);
} catch (IOException e) {
throw new FHIRException(e);
}
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
(OperationOutcome) result.getPayload());
}
} catch (IOException e) {
e.printStackTrace();
}
return result == null ? null : (ValueSet) result.getPayload();
}
@ -416,7 +410,7 @@ public class FHIRToolingClient {
(OperationOutcome) result.getPayload());
}
} catch (IOException e) {
e.printStackTrace();
throw new FHIRException(e);
}
return result == null ? null : (ValueSet) result.getPayload();
}
@ -427,7 +421,7 @@ public class FHIRToolingClient {
result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup", params),
getPreferredResourceFormat(), generateHeaders(), "CodeSystem/$lookup", TIMEOUT_NORMAL);
} catch (IOException e) {
e.printStackTrace();
throw new FHIRException(e);
}
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
@ -455,7 +449,7 @@ public class FHIRToolingClient {
(OperationOutcome) result.getPayload());
}
} catch (IOException e) {
e.printStackTrace();
throw new FHIRException(e);
}
return result == null ? null : (ValueSet) result.getPayload();
}
@ -478,7 +472,7 @@ public class FHIRToolingClient {
(OperationOutcome) result.getPayload());
}
} catch (IOException e) {
e.printStackTrace();
throw new FHIRException(e);
}
return result == null ? null : (ConceptMap) result.getPayload();
}
@ -498,7 +492,7 @@ public class FHIRToolingClient {
(OperationOutcome) result.getPayload());
}
} catch (IOException e) {
e.printStackTrace();
throw new FHIRException(e);
}
return result == null ? null : (ConceptMap) result.getPayload();
}

View File

@ -306,7 +306,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
} catch (Exception e) {
throw new FHIRException(formatMessage(canNoTS
? I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER_USE_PARAMETER_TX_NA_TUN_RUN_WITHOUT_USING_TERMINOLOGY_SERVICES_TO_VALIDATE_LOINC_SNOMED_ICDX_ETC_ERROR__
: I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER, e.getMessage()), e);
: I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER, e.getMessage(), client.getAddress()), e);
}
}

View File

@ -1197,7 +1197,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, e.getType());
iss.getDetails().setText(e.getMessage());
issues.add(iss);
return new ValidationResult(IssueSeverity.ERROR, e.getMessage(), e.getError(), issues);
return new ValidationResult(IssueSeverity.FATAL, e.getMessage(), e.getError(), issues);
} catch (Exception e) {
// e.printStackTrace();
localError = e.getMessage();
@ -1354,6 +1354,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
Set<String> unknownSystems = new HashSet<>();
List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
String localError = null;
String localWarning = null;
if (options.isUseClient()) {
// ok, first we try to validate locally
try {
@ -1365,14 +1370,37 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
}
return res;
} catch (Exception e) {
e.printStackTrace();
if (e instanceof NoTerminologyServiceException) {
return new ValidationResult(IssueSeverity.ERROR, "No Terminology Service", TerminologyServiceErrorClass.NOSERVICE, null);
} catch (VSCheckerException e) {
if (e.isWarning()) {
localWarning = e.getMessage();
} else {
localError = e.getMessage();
}
if (e.getIssues() != null) {
issues.addAll(e.getIssues());
}
} catch (TerminologyServiceProtectionException e) {
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, e.getType());
iss.getDetails().setText(e.getMessage());
issues.add(iss);
return new ValidationResult(IssueSeverity.FATAL, e.getMessage(), e.getError(), issues);
} catch (Exception e) {
// e.printStackTrace();
localError = e.getMessage();
}
}
if (localError != null && tcc.getClient() == null) {
if (unknownSystems.size() > 0) {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems);
} else {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues);
}
}
if (localWarning != null && tcc.getClient() == null) {
return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues);
}
if (!options.isUseServer()) {
return new ValidationResult(IssueSeverity.WARNING, "Unable to validate code without using server", TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, null);
}
@ -1499,6 +1527,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
String system = null;
String code = null;
String version = null;
boolean inactive = false;
String status = null;
List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
TerminologyServiceErrorClass err = TerminologyServiceErrorClass.UNKNOWN;
@ -1516,18 +1546,22 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
version = ((PrimitiveType<?>) p.getValue()).asStringValue();
} else if (p.getName().equals("code")) {
code = ((PrimitiveType<?>) p.getValue()).asStringValue();
} else if (p.getName().equals("inactive")) {
inactive = "true".equals(((PrimitiveType<?>) p.getValue()).asStringValue());
} else if (p.getName().equals("status")) {
status = ((PrimitiveType<?>) p.getValue()).asStringValue();
} else if (p.getName().equals("x-caused-by-unknown-system")) {
err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED;
} else if (p.getName().equals("warning-withdrawn")) {
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.EXPIRED);
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE);
iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_WITHDRAWN : I18nConstants.MSG_WITHDRAWN_SRC, ((PrimitiveType<?>) p.getValue()).asStringValue(), vs));
issues.add(iss);
} else if (p.getName().equals("warning-deprecated")) {
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.EXPIRED);
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE);
iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_DEPRECATED : I18nConstants.MSG_DEPRECATED_SRC, ((PrimitiveType<?>) p.getValue()).asStringValue(), vs));
issues.add(iss);
} else if (p.getName().equals("warning-retired")) {
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.EXPIRED);
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE);
iss.getDetails().setText(formatMessage(vs == null ? I18nConstants.MSG_RETIRED : I18nConstants.MSG_RETIRED_SRC, ((PrimitiveType<?>) p.getValue()).asStringValue(), vs));
issues.add(iss);
} else if (p.getName().equals("warning-experimental")) {
@ -1565,7 +1599,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
} else {
res = new ValidationResult(system, version, new ConceptDefinitionComponent().setCode(code), null).setTxLink(txLog.getLastId());
}
res.setIssues(issues );
res.setIssues(issues);
res.setStatus(inactive, status);
return res;
}

View File

@ -124,6 +124,8 @@ public interface IWorkerContext {
private List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
private CodeableConcept codeableConcept;
private Set<String> unknownSystems;
private boolean inactive;
private String status;
@Override
public String toString() {
@ -339,6 +341,22 @@ public interface IWorkerContext {
}
public boolean isInactive() {
return inactive;
}
public String getStatus() {
return status;
}
public ValidationResult setStatus(boolean inactive, String status) {
this.inactive = inactive;
if (!"inactive".equals(status)) {
this.status = status;
}
return this;
}
}
public class CodingValidationRequest {

View File

@ -341,7 +341,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
setTxCaps(capabilityStatement);
return capabilitiesStatementQuick.getSoftware().getVersion();
} catch (Exception e) {
throw new FHIRException(formatMessage(canNoTS ? I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER_USE_PARAMETER_TX_NA_TUN_RUN_WITHOUT_USING_TERMINOLOGY_SERVICES_TO_VALIDATE_LOINC_SNOMED_ICDX_ETC_ERROR__ : I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER, e.getMessage()), e);
throw new FHIRException(formatMessage(canNoTS ? I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER_USE_PARAMETER_TX_NA_TUN_RUN_WITHOUT_USING_TERMINOLOGY_SERVICES_TO_VALIDATE_LOINC_SNOMED_ICDX_ETC_ERROR__ : I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER, e.getMessage(), client.getAddress()), e);
}
}

View File

@ -363,9 +363,13 @@ public class CodeSystemUtilities {
}
public static boolean isInactive(CodeSystem cs, ConceptDefinitionComponent def) throws FHIRException {
StandardsStatus ss = ToolingExtensions.getStandardsStatus(def);
if (ss == StandardsStatus.DEPRECATED || ss == StandardsStatus.WITHDRAWN) {
return true;
}
for (ConceptPropertyComponent p : def.getProperty()) {
if ("status".equals(p.getCode()) && p.hasValueStringType()) {
return "inactive".equals(p.getValueStringType().primitiveValue()) || "retired".equals(p.getValueStringType().primitiveValue());
return "inactive".equals(p.getValueStringType().primitiveValue()) || "retired".equals(p.getValueStringType().primitiveValue()) || "deprecated".equals(p.getValueStringType().primitiveValue());
}
if ("inactive".equals(p.getCode()) && p.hasValueBooleanType()) {
return p.getValueBooleanType().getValue();
@ -886,6 +890,10 @@ public class CodeSystemUtilities {
public static DataType getProperty(CodeSystem cs, String code, String property) {
ConceptDefinitionComponent def = getCode(cs, code);
return getProperty(cs, def, property);
}
public static DataType getProperty(CodeSystem cs, ConceptDefinitionComponent def, String property) {
ConceptPropertyComponent cp = getProperty(def, property);
return cp == null ? null : cp.getValue();
}
@ -905,5 +913,18 @@ public class CodeSystemUtilities {
}
return false;
}
public static String getStatus(CodeSystem cs, ConceptDefinitionComponent cc) {
StandardsStatus ss = ToolingExtensions.getStandardsStatus(cc);
if (ss == StandardsStatus.DEPRECATED || ss == StandardsStatus.WITHDRAWN) {
return ss.toCode();
}
DataType v = getProperty(cs, cc, "status");
if (v == null || !v.isPrimitive()) {
return null;
} else {
return v.primitiveValue();
}
}
}

View File

@ -13,12 +13,12 @@ import java.util.ArrayList;
public class TerminologyOperationContext {
public class TerminologyServiceProtectionException extends FHIRException {
public static class TerminologyServiceProtectionException extends FHIRException {
private TerminologyServiceErrorClass error;
private IssueType type;
protected TerminologyServiceProtectionException(String message, TerminologyServiceErrorClass error, IssueType type) {
public TerminologyServiceProtectionException(String message, TerminologyServiceErrorClass error, IssueType type) {
super(message);
this.error = error;
this.type = type;

View File

@ -143,7 +143,7 @@ public class ValueSetProcessBase {
}
private List<OperationOutcomeIssueComponent> makeStatusIssue(String path, String id, String msg, CanonicalResource resource) {
List<OperationOutcomeIssueComponent> iss = makeIssue(IssueSeverity.INFORMATION, IssueType.EXPIRED, path, context.formatMessage(msg, resource.getVersionedUrl()));
List<OperationOutcomeIssueComponent> iss = makeIssue(IssueSeverity.INFORMATION, IssueType.BUSINESSRULE, path, context.formatMessage(msg, resource.getVersionedUrl()));
// this is a testing hack - see TerminologyServiceTests
iss.get(0).setUserData("status-msg-name", "warning-"+id);

View File

@ -81,6 +81,7 @@ import org.hl7.fhir.r5.terminologies.providers.CodeSystemProvider;
import org.hl7.fhir.r5.terminologies.providers.SpecialCodeSystem;
import org.hl7.fhir.r5.terminologies.providers.URICodeSystem;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext.TerminologyServiceProtectionException;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.utilities.ValueSetProcessBase;
import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -195,6 +196,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
ValidationProcessInfo info = new ValidationProcessInfo();
CodeableConcept vcc = new CodeableConcept();
if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) {
int i = 0;
for (Coding c : code.getCoding()) {
@ -262,6 +264,9 @@ public class ValueSetValidator extends ValueSetProcessBase {
info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, path, msg));
}
}
if (vcc.hasCoding() && code.hasText()) {
vcc.setText(code.getText());
}
if (!checkRequiredSupplements(info)) {
return new ValidationResult(IssueSeverity.ERROR, info.getIssues().get(info.getIssues().size()-1).getDetails().getText(), info.getIssues());
} else if (info.hasErrors()) {
@ -295,7 +300,8 @@ public class ValueSetValidator extends ValueSetProcessBase {
private boolean checkRequiredSupplements(ValidationProcessInfo info) {
if (!requiredSupplements.isEmpty()) {
info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, null, context.formatMessagePlural(requiredSupplements.size(), I18nConstants.VALUESET_SUPPLEMENT_MISSING, CommaSeparatedStringBuilder.build(requiredSupplements))));
String msg= context.formatMessagePlural(requiredSupplements.size(), I18nConstants.VALUESET_SUPPLEMENT_MISSING, CommaSeparatedStringBuilder.build(requiredSupplements));
throw new TerminologyServiceProtectionException(msg, TerminologyServiceErrorClass.BUSINESS_RULE, IssueType.NOTFOUND);
}
return requiredSupplements.isEmpty();
}
@ -529,6 +535,8 @@ public class ValueSetValidator extends ValueSetProcessBase {
res.setMessage("Code found in include, however: " + res.getMessage());
res.getIssues().addAll(makeIssue(IssueSeverity.WARNING, IssueType.EXCEPTION, path, res.getMessage()));
}
} else if (res == null) {
res = new ValidationResult(system, wv, null, null);
}
} else if ((res != null && !res.isOk())) {
String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), code.toString());
@ -676,18 +684,19 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (vcc != null) {
vcc.addCoding(vc);
}
if (CodeSystemUtilities.isInactive(cs, cc)) {
info.addIssue(makeIssue(IssueSeverity.WARNING, IssueType.EXPIRED, path, context.formatMessage(I18nConstants.INACTIVE_CODE_WARNING, cc.getCode())));
}
boolean inactive = (CodeSystemUtilities.isInactive(cs, cc));
String status = inactive ? (CodeSystemUtilities.getStatus(cs, cc)) : null;
boolean ws = false;
if (code.getDisplay() == null) {
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, vc.getDisplay());
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, vc.getDisplay()).setStatus(inactive, status);
}
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(", ", " or ");
if (cc.hasDisplay() && isOkLanguage(cs.getLanguage())) {
b.append("'"+cc.getDisplay()+"'");
if (code.getDisplay().equalsIgnoreCase(cc.getDisplay())) {
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs));
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs)).setStatus(inactive, status);
} else if (Utilities.normalize(code.getDisplay()).equals(Utilities.normalize(cc.getDisplay()))) {
ws = true;
}
@ -698,7 +707,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (isOkLanguage(ds.getLanguage())) {
b.append("'"+ds.getValue()+"'");
if (code.getDisplay().equalsIgnoreCase(ds.getValue())) {
return new ValidationResult(code.getSystem(),cs.getVersion(), cc, getPreferredDisplay(cc, cs));
return new ValidationResult(code.getSystem(),cs.getVersion(), cc, getPreferredDisplay(cc, cs)).setStatus(inactive, status);
}
if (Utilities.normalize(code.getDisplay()).equalsIgnoreCase(Utilities.normalize(ds.getValue()))) {
ws = true;
@ -712,7 +721,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (vs.getCc().hasDisplay() && isOkLanguage(vs.getValueset().getLanguage())) {
b.append("'"+vs.getCc().getDisplay()+"'");
if (code.getDisplay().equalsIgnoreCase(vs.getCc().getDisplay())) {
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs));
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs)).setStatus(inactive, status);
}
}
for (ConceptReferenceDesignationComponent ds : vs.getCc().getDesignation()) {
@ -720,7 +729,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (isOkLanguage(ds.getLanguage())) {
b.append("'"+ds.getValue()+"'");
if (code.getDisplay().equalsIgnoreCase(ds.getValue())) {
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs));
return new ValidationResult(code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs)).setStatus(inactive, status);
}
}
}
@ -728,10 +737,10 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
if (b.count() == 0) {
String msg = context.formatMessagePlural(options.getLanguages().size(), I18nConstants.NO_VALID_DISPLAY_FOUND, code.getSystem(), code.getCode(), code.getDisplay(), options.langSummary());
return new ValidationResult(IssueSeverity.WARNING, msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(IssueSeverity.WARNING, IssueType.INVALID, path+".display", msg));
return new ValidationResult(IssueSeverity.WARNING, msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(IssueSeverity.WARNING, IssueType.INVALID, path+".display", msg)).setStatus(inactive, status);
} else {
String msg = context.formatMessagePlural(b.count(), ws ? I18nConstants.DISPLAY_NAME_WS_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF : I18nConstants.DISPLAY_NAME_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF, code.getSystem(), code.getCode(), b.toString(), code.getDisplay(), options.langSummary());
return new ValidationResult(dispWarningStatus(), msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(dispWarning(), IssueType.INVALID, path+".display", msg));
return new ValidationResult(dispWarningStatus(), msg, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(dispWarning(), IssueType.INVALID, path+".display", msg)).setStatus(inactive, status);
}
}

View File

@ -57,19 +57,12 @@ import org.hl7.fhir.r5.model.OperationOutcome;
*/
public class EFhirClientException extends RuntimeException {
private static final long serialVersionUID = 1L;
private List<OperationOutcome> errors = new ArrayList<OperationOutcome>();
private OperationOutcome error = null;
public EFhirClientException(String message) {
super(message);
}
public EFhirClientException(String message, List<OperationOutcome> serverErrors) {
super(message);
if(serverErrors != null && serverErrors.size() > 0) {
errors.addAll(serverErrors);
}
}
public EFhirClientException(Exception cause) {
super(cause);
}
@ -86,10 +79,21 @@ public class EFhirClientException extends RuntimeException {
* @param serverError
*/
public EFhirClientException(String message, OperationOutcome serverError) {
super(message);
if(serverError != null) {
errors.add(serverError);
}
super(message);
error = serverError;
}
/**
* Generate EFhirClientException which include a message indicating the cause of the exception
* along with any OperationOutcome server error that may have resulted.
*
* @param message
* @param serverError
*/
public EFhirClientException(String message, OperationOutcome serverError, Exception cause) {
super(message, cause);
error = serverError;
}
/**
@ -103,19 +107,11 @@ public class EFhirClientException extends RuntimeException {
*/
public EFhirClientException(OperationOutcome serverError) {
super("Error on the server: "+serverError.getText().getDiv().allText()+". Refer to e.getServerErrors() for additional details.");
if(serverError != null) {
errors.add(serverError);
}
error = serverError;
}
/**
* Method returns all OperationOutcome server errors that are
* associated with this exception.
*
* @return
*/
public List<OperationOutcome> getServerErrors() {
return errors;
public OperationOutcome getServerError() {
return error;
}
/**
@ -123,8 +119,8 @@ public class EFhirClientException extends RuntimeException {
*
* @return
*/
public boolean hasServerErrors() {
return errors.size() > 0;
public boolean hasServerError() {
return error != null;
}
}

View File

@ -421,16 +421,16 @@ public class FHIRToolingClient {
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
getPreferredResourceFormat(),
generateHeaders(),
"ValueSet/$expand?url=" + source.getUrl(),
TIMEOUT_OPERATION_EXPAND);
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload());
}
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
getPreferredResourceFormat(),
generateHeaders(),
"ValueSet/$expand?url=" + source.getUrl(),
TIMEOUT_OPERATION_EXPAND);
} catch (IOException e) {
e.printStackTrace();
throw new FHIRException(e);
}
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload());
}
return result == null ? null : (ValueSet) result.getPayload();
}

View File

@ -962,7 +962,10 @@ public class I18nConstants {
public static final String MSG_RETIRED_SRC = "MSG_RETIRED_SRC";
public static final String MSG_DRAFT_SRC = "MSG_DRAFT_SRC";
public static final String MSG_EXPERIMENTAL_SRC = "MSG_EXPERIMENTAL_SRC";
public static final String INACTIVE_CODE_WARNING = "INACTIVE_CODE_WARNING";
public static final String STATUS_CODE_WARNING = "STATUS_CODE_WARNING";
public static final String STATUS_CODE_HINT = "STATUS_CODE_HINT";
public static final String STATUS_CODE_WARNING_CODE = "STATUS_CODE_WARNING_CODE";
public static final String STATUS_CODE_HINT_CODE = "STATUS_CODE_HINT_CODE";
public static final String SD_EXTENSION_URL_MISSING = "SD_EXTENSION_URL_MISSING";
public static final String MSG_DEPENDS_ON_DEPRECATED = "MSG_DEPENDS_ON_DEPRECATED";
public static final String MSG_DEPENDS_ON_WITHDRAWN = "MSG_DEPENDS_ON_WITHDRAWN";

View File

@ -465,7 +465,7 @@ Parser_Type__not_supported = Parser Type {0} not supported
Version_mismatch_The_context_has_version__loaded_and_the_new_content_being_loaded_is_version_ = Version mismatch. The context has version {0} loaded, and the new content being loaded is version {1}
Error_reading__from_package__ = Error reading {0} from package {1}#{2}: {3}
Error_parsing_ = Error parsing {0}:{1}
Unable_to_connect_to_terminology_server_Use_parameter_tx_na_tun_run_without_using_terminology_services_to_validate_LOINC_SNOMED_ICDX_etc_Error__ = Unable to connect to terminology server. Use parameter ''-tx n/a'' to run without using terminology services to validate LOINC, SNOMED, ICD-X etc. Error = {0}
Unable_to_connect_to_terminology_server_Use_parameter_tx_na_tun_run_without_using_terminology_services_to_validate_LOINC_SNOMED_ICDX_etc_Error__ = Unable to connect to terminology server at {1}. Use parameter ''-tx n/a'' to run without using terminology services to validate LOINC, SNOMED, ICD-X etc. Error = {0}
Display_Name_for__should_be_one_of__instead_of_one = Wrong Display Name ''{4}'' for {1}#{2} - should be {3} (for the language(s) ''{5}'')
Display_Name_for__should_be_one_of__instead_of_other = Wrong Display Name ''{4}'' for {1}#{2} - should be one of {0} choices: {3} (for the language(s) ''{5}'')
Display_Name_WS_for__should_be_one_of__instead_of_one = Wrong whitespace in Display Name ''{4}'' for {1}#{2} - should be {3} (for the language(s) ''{5}'')
@ -697,7 +697,7 @@ UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLIC
UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported: no properties with values found on type {2} for pattern for discriminator ({0}) for slice {1}
SD_NESTED_MUST_SUPPORT_DIFF = The element {0} has types/profiles/targets that are marked as must support, but the element itself is not marked as must-support. The inner must-supports will be ignored unless the element inherits must-support = true
SD_NESTED_MUST_SUPPORT_SNAPSHOT = The element {0} has types/profiles/targets that are marked as must support, but the element itself is not marked as must-support
Unable_to_connect_to_terminology_server = Unable to connect to terminology server. Error = {0}
Unable_to_connect_to_terminology_server = Unable to connect to terminology server at {1}. Error = {0}
SD_ED_TYPE_PROFILE_UNKNOWN = Unable to resolve profile {0}
SD_ED_TYPE_PROFILE_NOTYPE = Found profile {0}, but unable to determine the type it applies to
SD_ED_TYPE_PROFILE_WRONG = Profile {0} is for type {1}, but the {3} element has type {2}
@ -1020,7 +1020,10 @@ MSG_WITHDRAWN_SRC = Reference to withdrawn item {0} from {1}
MSG_RETIRED_SRC = Reference to retired item {0} from {1}
MSG_EXPERIMENTAL_SRC = Reference to experimental item {0} from {1}
MSG_DRAFT_SRC = Reference to draft item {0} from {1}
INACTIVE_CODE_WARNING = The code ''{0}'' is valid but is not active
STATUS_CODE_WARNING = The code is valid but is {0}
STATUS_CODE_HINT = The code is {0}
STATUS_CODE_WARNING_CODE = The code ''{1}'' is valid but is {0}
STATUS_CODE_HINT_CODE = The code ''{1}'' is {0}
SD_ED_TYPE_PROFILE_WRONG_TYPE_one = The type {0} is not in the list of allowed type {1} in the profile {2}
SD_ED_TYPE_PROFILE_WRONG_TYPE_other = The type {0} is not in the list of allowed types {1} in the profile {2}
MSG_DEPENDS_ON_DEPRECATED = The {0} {1} is deprecated

View File

@ -60,6 +60,7 @@ import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
@ -634,6 +635,24 @@ public class BaseValidator implements IValidationContextResourceLoader {
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails
*
* @param thePass
* 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 void txIssue(List<ValidationMessage> errors, String ruleDate, String txLink, int line, int col, String path, OperationOutcomeIssueComponent issue) {
IssueType code = IssueType.fromCode(issue.getCode().toCode());
IssueSeverity severity = IssueSeverity.fromCode(issue.getSeverity().toCode());
ValidationMessage vmsg = new ValidationMessage(Source.TerminologyEngine, code, line, col, path, issue.getDetails().getText(), severity).setTxLink(txLink);
// if (checkMsgId(msg, vmsg)) {
errors.add(vmsg);
// }
// }
// return thePass;
}
/**
* Test a rule and add a {@link IssueSeverity#WARNING} validation message if the validation fails. Also, keep track of it later in case we want to remove it if we find a required binding for this element later
*
@ -1312,17 +1331,17 @@ public class BaseValidator implements IValidationContextResourceLoader {
if (standardsStatus == StandardsStatus.DEPRECATED) {
if (!statusWarnings.contains(vurl+":DEPRECATED")) {
statusWarnings.add(vurl+":DEPRECATED");
hint(errors, "2023-08-10", IssueType.EXPIRED, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_DEPRECATED, type, vurl);
hint(errors, "2023-08-10", IssueType.BUSINESSRULE, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_DEPRECATED, type, vurl);
}
} else if (standardsStatus == StandardsStatus.WITHDRAWN) {
if (!statusWarnings.contains(vurl+":WITHDRAWN")) {
statusWarnings.add(vurl+":WITHDRAWN");
hint(errors, "2023-08-10", IssueType.EXPIRED, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_WITHDRAWN, type, vurl);
hint(errors, "2023-08-10", IssueType.BUSINESSRULE, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_WITHDRAWN, type, vurl);
}
} else if (ex.getStatus() == PublicationStatus.RETIRED) {
if (!statusWarnings.contains(vurl+":RETIRED")) {
statusWarnings.add(vurl+":RETIRED");
hint(errors, "2023-08-10", IssueType.EXPIRED, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_RETIRED, type, vurl);
hint(errors, "2023-08-10", IssueType.BUSINESSRULE, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_RETIRED, type, vurl);
}
} else if (false && warnOnDraftOrExperimental && source != null) {
// for now, this is disabled; these warnings are just everywhere, and it's an intractible problem.

View File

@ -125,6 +125,7 @@ import org.hl7.fhir.r5.model.ImplementationGuide;
import org.hl7.fhir.r5.model.ImplementationGuide.ImplementationGuideGlobalComponent;
import org.hl7.fhir.r5.model.InstantType;
import org.hl7.fhir.r5.model.IntegerType;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r5.model.Period;
import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Quantity;
@ -1024,9 +1025,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'");
if (s == null)
return true;
if (s != null && s.isOk()) {
for (OperationOutcomeIssueComponent iss : s.getIssues()) {
txIssue(errors, "2023-08-19", s.getTxLink(), element.line(), element.col(), path, iss);
}
}
if (s.isOk()) {
if (s.getMessage() != null)
txWarning(errors, NO_RULE_DATE, s.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, s == null, I18nConstants.TERMINOLOGY_PASSTHROUGH_TX_MESSAGE, s.getMessage(), system, code);
return true;
}
if (s.getErrorClass() != null && s.getErrorClass().isInfrastructure())
@ -1279,6 +1286,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// ignore this since we can't validate but it doesn't matter..
} else {
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, true); // we're going to validate the codings directly, so only check the valueset
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
if (!vr.isOk()) {
bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) {
@ -1356,6 +1368,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
public void checkBindings(List<ValidationMessage> errors, String path, Element element, NodeStack stack, ValueSet valueset, Coding nextCoding) {
if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem())) {
ValidationResult vr = checkCodeOnServer(stack, valueset, nextCoding, false);
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
if (vr.getSeverity() != null/* && vr.hasMessage()*/) {
if (vr.getSeverity() == IssueSeverity.INFORMATION) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
@ -1416,6 +1433,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// ignore this since we can't validate but it doesn't matter..
} else {
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, false); // we're going to validate the codings directly
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
if (!vr.isOk()) {
bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
@ -1465,6 +1487,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String nextVersion = nextCoding.getVersion();
if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) {
ValidationResult vr = checkCodeOnServer(stack, nextCode, nextSystem, nextVersion, null, false);
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
if (!vr.isOk()) {
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_NOTVALID, nextCode, nextSystem);
}
@ -1528,7 +1555,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (binding.getStrength() == BindingStrength.REQUIRED) {
removeTrackedMessagesForLocation(errors, element, path);
}
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'");
if (vr != null && !vr.isOk()) {
if (vr.IsNoService())
@ -1671,6 +1702,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try {
long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, false);
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
timeTracker.tx(t, "vc "+cc.toString());
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1708,6 +1744,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try {
long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, valueset, c, true);
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'");
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1737,6 +1779,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try {
long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, valueset, value, baseOptions.withLanguage(stack.getWorkingLang()));
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
timeTracker.tx(t, "vc "+value);
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1788,7 +1835,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (theSystem != null && theCode != null && !noTerminologyChecks) {
ok = rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_VALUESET2, theSystem) && ok;
try {
if (checkCode(errors, element, path, theCode, theSystem, theVersion, theDisplay, checkDisplay, stack))
if (checkCode(errors, element, path, theCode, theSystem, theVersion, theDisplay, checkDisplay, stack)) {
if (theElementCntext != null && theElementCntext.hasBinding()) {
ElementDefinitionBindingComponent binding = theElementCntext.getBinding();
if (warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING2, path)) {
@ -1809,6 +1856,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = checkCodeOnServer(stack, valueset, c, true);
}
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'");
if (binding.getStrength() == BindingStrength.REQUIRED) {
removeTrackedMessagesForLocation(errors, element, path);
@ -1862,6 +1915,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
}
} else {
ok = false;
}
} catch (Exception e) {
if (STACK_TRACE) e.printStackTrace();
rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING2, e.getMessage(), e.toString());
@ -3120,6 +3176,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
vr = checkCodeOnServer(stack, vs, value, options);
}
if (vr != null && vr.isOk()) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
txIssue(errors, "2023-08-19", vr.getTxLink(), element.line(), element.col(), path, iss);
}
}
timeTracker.tx(t, "vc "+value+"");
if (binding.getStrength() == BindingStrength.REQUIRED) {
removeTrackedMessagesForLocation(errors, element, path);
@ -6565,7 +6627,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet vs, String value, ValidationOptions options) {
return context.validateCode(options, value, vs);
return checkForInctive(context.validateCode(options, value, vs));
}
// no delay on this one?
@ -6578,27 +6640,52 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
lang = "en"; // ubiquitious default languauge
}
codingObserver.seeCode(stack, system, version, code, display);
return context.validateCode(baseOptions.withLanguage(lang), system, version, code, checkDisplay ? display : null);
return checkForInctive(context.validateCode(baseOptions.withLanguage(lang), system, version, code, checkDisplay ? display : null));
}
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, Coding c, boolean checkMembership) {
codingObserver.seeCode(stack, c);
if (checkMembership) {
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), c, valueset);
return checkForInctive( context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), c, valueset));
} else {
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withNoCheckValueSetMembership(), c, valueset);
return checkForInctive(context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withNoCheckValueSetMembership(), c, valueset));
}
}
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, CodeableConcept cc, boolean vsOnly) {
codingObserver.seeCode(stack, cc);
if (vsOnly) {
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), cc, valueset);
return checkForInctive(context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), cc, valueset));
} else {
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()), cc, valueset);
return checkForInctive(context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()), cc, valueset));
}
}
private ValidationResult checkForInctive(ValidationResult res) {
if (res == null) {
return null;
}
if (!res.isInactive()) {
return res;
}
org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity lvl = org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION;
var status = "not active";
if (res.getStatus() != null) {
status = res.getStatus();
}
String code = res.getCode();
var op = new OperationOutcomeIssueComponent(lvl, org.hl7.fhir.r5.model.OperationOutcome.IssueType.INVALID);
String msgId = null;
if (code != null) {
msgId = res.isOk() ? I18nConstants.STATUS_CODE_WARNING_CODE : I18nConstants.STATUS_CODE_HINT_CODE;
} else {
msgId = res.isOk() ? I18nConstants.STATUS_CODE_WARNING : I18nConstants.STATUS_CODE_HINT;
}
op.getDetails().setText(context.formatMessage(msgId, status, code));
res.getIssues().add(op);
return res;
}
public boolean isSecurityChecks() {
return securityChecks;
}

View File

@ -255,7 +255,7 @@ public class TxTester {
TxTesterSorters.sortValueSet(vs);
vsj = new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(vs);
} catch (EFhirClientException e) {
OperationOutcome oo = e.getServerErrors().get(0);
OperationOutcome oo = e.getServerError();
TxTesterScrubbers.scrubOO(oo, tight);
vsj = new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(oo);
}
@ -279,7 +279,7 @@ public class TxTester {
TxTesterSorters.sortParameters(po);
pj = new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(po);
} catch (EFhirClientException e) {
OperationOutcome oo = e.getServerErrors().get(0);
OperationOutcome oo = e.getServerError();
oo.setText(null);
pj = new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(oo);
}

View File

@ -31,6 +31,10 @@ public class TxTesterSorters {
}
public static void sortOperationOutcome(OperationOutcome oo) {
Collections.sort(oo.getIssue(), new TxTesterSorters.OperationIssueSorter());
}
public static void sortValueSet(ValueSet vs) {
Collections.sort(vs.getExtension(), new TxTesterSorters.ExtensionSorter());
if (vs.hasExpansion()) {

View File

@ -95,7 +95,7 @@ public class TerminologyServiceTests {
List<Object[]> objects = new ArrayList<Object[]>(examples.size());
for (String id : names) {
objects.add(new Object[]{id, examples.get(id)});
objects.add(new Object[]{id, examples.get(id)});
}
return objects;
}
@ -208,6 +208,7 @@ public class TerminologyServiceTests {
}
e.getDetails().setText(vse.getError());
oo.addIssue(e);
TxTesterSorters.sortOperationOutcome(oo);
TxTesterScrubbers.scrubOO(oo, false);
String ooj = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(oo);
@ -242,7 +243,7 @@ public class TerminologyServiceTests {
options = options.withLanguage(p.getParameterString("displayLanguage"));
}
if (p.hasParameter("valueSetMode") && "CHECK_MEMBERSHIP_ONLY".equals(p.getParameterString("valueSetMode"))) {
options = options.withCheckValueSetOnly();
options = options.withCheckValueSetOnly();
}
if (p.hasParameter("mode") && "lenient-display-validation".equals(p.getParameterString("mode"))) {
options = options.setDisplayWarningMode(true);
@ -265,51 +266,73 @@ public class TerminologyServiceTests {
} else {
throw new Error("validate not done yet for this steup");
}
org.hl7.fhir.r5.model.Parameters res = new org.hl7.fhir.r5.model.Parameters();
if (vm.getSystem() != null) {
res.addParameter("system", new UriType(vm.getSystem()));
}
if (vm.getCode() != null) {
res.addParameter("code", new CodeType(vm.getCode()));
}
if (vm.getSeverity() == org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.ERROR) {
res.addParameter("result", false);
} else {
res.addParameter("result", true);
}
if (vm.getMessage() != null) {
res.addParameter("message", vm.getMessage());
}
if (vm.getVersion() != null) {
res.addParameter("version", vm.getVersion());
}
if (vm.getDisplay() != null) {
res.addParameter("display", vm.getDisplay());
}
if (vm.getCodeableConcept() != null) {
res.addParameter("codeableConcept", vm.getCodeableConcept());
}
if (vm.getUnknownSystems() != null) {
for (String s : vm.getUnknownSystems()) {
res.addParameter("x-caused-by-unknown-system", new CanonicalType(s));
}
}
if (vm.getIssues().size() > 0) {
if (vm.getSeverity() == org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.FATAL) {
OperationOutcome oo = new OperationOutcome();
oo.getIssue().addAll(vm.getIssues());
res.addParameter().setName("issues").setResource(oo);
}
TxTesterSorters.sortParameters(res);
TxTesterScrubbers.scrubParams(res);
TxTesterSorters.sortOperationOutcome(oo);
TxTesterScrubbers.scrubOO(oo, false);
String pj = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(res);
String diff = CompareUtilities.checkJsonSrcIsSame(resp, pj, ext);
if (diff != null) {
Utilities.createDirectory(Utilities.getDirectoryForFile(fp));
TextFile.stringToFile(pj, fp);
System.out.println("Test "+name+"failed: "+diff);
String pj = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(oo);
String diff = CompareUtilities.checkJsonSrcIsSame(resp, pj, ext);
if (diff != null) {
Utilities.createDirectory(Utilities.getDirectoryForFile(fp));
TextFile.stringToFile(pj, fp);
System.out.println("Test "+name+"failed: "+diff);
}
Assertions.assertTrue(diff == null, diff);
} else {
org.hl7.fhir.r5.model.Parameters res = new org.hl7.fhir.r5.model.Parameters();
if (vm.getSystem() != null) {
res.addParameter("system", new UriType(vm.getSystem()));
}
if (vm.getCode() != null) {
res.addParameter("code", new CodeType(vm.getCode()));
}
if (vm.getSeverity() == org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.ERROR) {
res.addParameter("result", false);
} else {
res.addParameter("result", true);
}
if (vm.getMessage() != null) {
res.addParameter("message", vm.getMessage());
}
if (vm.getVersion() != null) {
res.addParameter("version", vm.getVersion());
}
if (vm.getDisplay() != null) {
res.addParameter("display", vm.getDisplay());
}
if (vm.getCodeableConcept() != null) {
res.addParameter("codeableConcept", vm.getCodeableConcept());
}
if (vm.isInactive()) {
res.addParameter("inactive", true);
}
if (vm.getStatus() != null) {
res.addParameter("status", vm.getStatus());
}
if (vm.getUnknownSystems() != null) {
for (String s : vm.getUnknownSystems()) {
res.addParameter("x-caused-by-unknown-system", new CanonicalType(s));
}
}
if (vm.getIssues().size() > 0) {
OperationOutcome oo = new OperationOutcome();
oo.getIssue().addAll(vm.getIssues());
res.addParameter().setName("issues").setResource(oo);
}
TxTesterSorters.sortParameters(res);
TxTesterScrubbers.scrubParams(res);
String pj = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(res);
String diff = CompareUtilities.checkJsonSrcIsSame(resp, pj, ext);
if (diff != null) {
Utilities.createDirectory(Utilities.getDirectoryForFile(fp));
TextFile.stringToFile(pj, fp);
System.out.println("Test "+name+"failed: "+diff);
}
Assertions.assertTrue(diff == null, diff);
}
Assertions.assertTrue(diff == null, diff);
}
public Resource loadResource(String filename) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {

View File

@ -30,6 +30,7 @@ import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.settings.FhirSettings;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.model.FileInfo;
@ -60,7 +61,7 @@ class ValidationServiceTest {
List<FileInfo> filesToValidate = new ArrayList<>();
filesToValidate.add(new FileInfo().setFileName("test_resource.json").setFileContent(resource).setFileType(Manager.FhirFormat.JSON.getExtension()));
ValidationRequest request = new ValidationRequest().setCliContext(new CliContext().setTxCache(getTerminologyCacheDirectory("validationService"))).setFilesToValidate(filesToValidate);
ValidationRequest request = new ValidationRequest().setCliContext(new CliContext().setTxServer(FhirSettings.getTxFhirDevelopment()).setTxCache(getTerminologyCacheDirectory("validationService"))).setFilesToValidate(filesToValidate);
// Validation run 1...nothing cached yet
myService.validateSources(request);
verify(sessionCache, Mockito.times(1)).cacheSession(ArgumentMatchers.any(ValidationEngine.class));

View File

@ -4,6 +4,7 @@ import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.SystemExitManager;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.settings.FhirSettings;
import org.hl7.fhir.validation.ValidatorCli;
import org.junit.jupiter.api.Test;
@ -16,7 +17,7 @@ public class CDAValidationTest {
String fn = TestingUtilities.tempFile("cda", "cda.xml");
TextFile.stringToFile(TestingUtilities.loadTestResource("cda/cda-original.xml"), fn);
SystemExitManager.setNoExit(true);
ValidatorCli.main(new String[] {fn, "-ig", "hl7.cda.uv.core#current"});
ValidatorCli.main(new String[] {fn, "-ig", "hl7.cda.uv.core#current", "-tx", FhirSettings.getTxFhirDevelopment()});
}
}