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

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.convertors.txClient; package org.hl7.fhir.convertors.txClient;
import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; 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.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.model.Resource; 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.r4.utils.client.FHIRToolingClient;
import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CapabilityStatement; 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.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;
@ -93,16 +96,38 @@ public class TerminologyClientR4 implements ITerminologyClient {
@Override @Override
public Parameters validateCS(Parameters pin) throws FHIRException { 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); try {
p2 = client.operateType(org.hl7.fhir.r4.model.CodeSystem.class, "validate-code", p2); org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) VersionConvertorFactory_40_50.convertResource(pin);
return (Parameters) VersionConvertorFactory_40_50.convertResource(p2); 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 @Override
public Parameters validateVS(Parameters pin) throws FHIRException { 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); try {
p2 = client.operateType(org.hl7.fhir.r4.model.ValueSet.class, "validate-code", p2); org.hl7.fhir.r4.model.Parameters p2 = (org.hl7.fhir.r4.model.Parameters) VersionConvertorFactory_40_50.convertResource(pin);
return (Parameters) VersionConvertorFactory_40_50.convertResource(p2); 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 @Override

View File

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

View File

@ -1,5 +1,7 @@
package org.hl7.fhir.r4.terminologies; package org.hl7.fhir.r4.terminologies;
import java.io.IOException;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -29,9 +31,11 @@ package org.hl7.fhir.r4.terminologies;
*/ */
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Map; import java.util.Map;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.context.HTMLClientLogger; import org.hl7.fhir.r4.context.HTMLClientLogger;
import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
@ -65,12 +69,20 @@ public class TerminologyClientR4 implements TerminologyClient {
@Override @Override
public Parameters validateCS(Parameters pin) { 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 @Override
public Parameters validateVS(Parameters pin) { 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 @Override

View File

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

View File

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

View File

@ -124,6 +124,8 @@ public interface IWorkerContext {
private List<OperationOutcomeIssueComponent> issues = new ArrayList<>(); private List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
private CodeableConcept codeableConcept; private CodeableConcept codeableConcept;
private Set<String> unknownSystems; private Set<String> unknownSystems;
private boolean inactive;
private String status;
@Override @Override
public String toString() { 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 { public class CodingValidationRequest {

View File

@ -341,7 +341,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
setTxCaps(capabilityStatement); setTxCaps(capabilityStatement);
return capabilitiesStatementQuick.getSoftware().getVersion(); return capabilitiesStatementQuick.getSoftware().getVersion();
} catch (Exception e) { } 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 { 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()) { for (ConceptPropertyComponent p : def.getProperty()) {
if ("status".equals(p.getCode()) && p.hasValueStringType()) { 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()) { if ("inactive".equals(p.getCode()) && p.hasValueBooleanType()) {
return p.getValueBooleanType().getValue(); return p.getValueBooleanType().getValue();
@ -886,6 +890,10 @@ public class CodeSystemUtilities {
public static DataType getProperty(CodeSystem cs, String code, String property) { public static DataType getProperty(CodeSystem cs, String code, String property) {
ConceptDefinitionComponent def = getCode(cs, code); 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); ConceptPropertyComponent cp = getProperty(def, property);
return cp == null ? null : cp.getValue(); return cp == null ? null : cp.getValue();
} }
@ -905,5 +913,18 @@ public class CodeSystemUtilities {
} }
return false; 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 TerminologyOperationContext {
public class TerminologyServiceProtectionException extends FHIRException { public static class TerminologyServiceProtectionException extends FHIRException {
private TerminologyServiceErrorClass error; private TerminologyServiceErrorClass error;
private IssueType type; private IssueType type;
protected TerminologyServiceProtectionException(String message, TerminologyServiceErrorClass error, IssueType type) { public TerminologyServiceProtectionException(String message, TerminologyServiceErrorClass error, IssueType type) {
super(message); super(message);
this.error = error; this.error = error;
this.type = type; this.type = type;

View File

@ -143,7 +143,7 @@ public class ValueSetProcessBase {
} }
private List<OperationOutcomeIssueComponent> makeStatusIssue(String path, String id, String msg, CanonicalResource resource) { 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 // this is a testing hack - see TerminologyServiceTests
iss.get(0).setUserData("status-msg-name", "warning-"+id); iss.get(0).setUserData("status-msg-name", "warning-"+id);

View File

@ -24,7 +24,7 @@ public class ValidationProcessInfo {
public void setErr(TerminologyServiceErrorClass err) { public void setErr(TerminologyServiceErrorClass err) {
this.err = err; this.err = err;
} }
public List<OperationOutcomeIssueComponent> getIssues() { public List<OperationOutcomeIssueComponent> getIssues() {
return issues; return issues;
} }

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.SpecialCodeSystem;
import org.hl7.fhir.r5.terminologies.providers.URICodeSystem; import org.hl7.fhir.r5.terminologies.providers.URICodeSystem;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext; 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.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.utilities.ValueSetProcessBase; import org.hl7.fhir.r5.terminologies.utilities.ValueSetProcessBase;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -195,6 +196,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
ValidationProcessInfo info = new ValidationProcessInfo(); ValidationProcessInfo info = new ValidationProcessInfo();
CodeableConcept vcc = new CodeableConcept(); CodeableConcept vcc = new CodeableConcept();
if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) { if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) {
int i = 0; int i = 0;
for (Coding c : code.getCoding()) { for (Coding c : code.getCoding()) {
@ -262,6 +264,9 @@ public class ValueSetValidator extends ValueSetProcessBase {
info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, path, msg)); info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, path, msg));
} }
} }
if (vcc.hasCoding() && code.hasText()) {
vcc.setText(code.getText());
}
if (!checkRequiredSupplements(info)) { if (!checkRequiredSupplements(info)) {
return new ValidationResult(IssueSeverity.ERROR, info.getIssues().get(info.getIssues().size()-1).getDetails().getText(), info.getIssues()); return new ValidationResult(IssueSeverity.ERROR, info.getIssues().get(info.getIssues().size()-1).getDetails().getText(), info.getIssues());
} else if (info.hasErrors()) { } else if (info.hasErrors()) {
@ -295,7 +300,8 @@ public class ValueSetValidator extends ValueSetProcessBase {
private boolean checkRequiredSupplements(ValidationProcessInfo info) { private boolean checkRequiredSupplements(ValidationProcessInfo info) {
if (!requiredSupplements.isEmpty()) { 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(); return requiredSupplements.isEmpty();
} }
@ -529,6 +535,8 @@ public class ValueSetValidator extends ValueSetProcessBase {
res.setMessage("Code found in include, however: " + res.getMessage()); res.setMessage("Code found in include, however: " + res.getMessage());
res.getIssues().addAll(makeIssue(IssueSeverity.WARNING, IssueType.EXCEPTION, path, 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())) { } 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()); 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) { if (vcc != null) {
vcc.addCoding(vc); 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;
boolean ws = false;
if (code.getDisplay() == null) { 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 "); CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(", ", " or ");
if (cc.hasDisplay() && isOkLanguage(cs.getLanguage())) { if (cc.hasDisplay() && isOkLanguage(cs.getLanguage())) {
b.append("'"+cc.getDisplay()+"'"); b.append("'"+cc.getDisplay()+"'");
if (code.getDisplay().equalsIgnoreCase(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()))) { } else if (Utilities.normalize(code.getDisplay()).equals(Utilities.normalize(cc.getDisplay()))) {
ws = true; ws = true;
} }
@ -698,7 +707,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (isOkLanguage(ds.getLanguage())) { if (isOkLanguage(ds.getLanguage())) {
b.append("'"+ds.getValue()+"'"); b.append("'"+ds.getValue()+"'");
if (code.getDisplay().equalsIgnoreCase(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()))) { if (Utilities.normalize(code.getDisplay()).equalsIgnoreCase(Utilities.normalize(ds.getValue()))) {
ws = true; ws = true;
@ -712,7 +721,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (vs.getCc().hasDisplay() && isOkLanguage(vs.getValueset().getLanguage())) { if (vs.getCc().hasDisplay() && isOkLanguage(vs.getValueset().getLanguage())) {
b.append("'"+vs.getCc().getDisplay()+"'"); b.append("'"+vs.getCc().getDisplay()+"'");
if (code.getDisplay().equalsIgnoreCase(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()) { for (ConceptReferenceDesignationComponent ds : vs.getCc().getDesignation()) {
@ -720,7 +729,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (isOkLanguage(ds.getLanguage())) { if (isOkLanguage(ds.getLanguage())) {
b.append("'"+ds.getValue()+"'"); b.append("'"+ds.getValue()+"'");
if (code.getDisplay().equalsIgnoreCase(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) { if (b.count() == 0) {
String msg = context.formatMessagePlural(options.getLanguages().size(), I18nConstants.NO_VALID_DISPLAY_FOUND, code.getSystem(), code.getCode(), code.getDisplay(), options.langSummary()); 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 { } 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()); 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 { public class EFhirClientException extends RuntimeException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private List<OperationOutcome> errors = new ArrayList<OperationOutcome>(); private OperationOutcome error = null;
public EFhirClientException(String message) { public EFhirClientException(String message) {
super(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) { public EFhirClientException(Exception cause) {
super(cause); super(cause);
} }
@ -86,12 +79,23 @@ public class EFhirClientException extends RuntimeException {
* @param serverError * @param serverError
*/ */
public EFhirClientException(String message, OperationOutcome serverError) { public EFhirClientException(String message, OperationOutcome serverError) {
super(message); super(message);
if(serverError != null) { error = serverError;
errors.add(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;
}
/** /**
* Generate EFhirClientException indicating the cause of the exception * Generate EFhirClientException indicating the cause of the exception
* along with any OperationOutcome server error the server may have generated. * along with any OperationOutcome server error the server may have generated.
@ -103,19 +107,11 @@ public class EFhirClientException extends RuntimeException {
*/ */
public EFhirClientException(OperationOutcome serverError) { public EFhirClientException(OperationOutcome serverError) {
super("Error on the server: "+serverError.getText().getDiv().allText()+". Refer to e.getServerErrors() for additional details."); super("Error on the server: "+serverError.getText().getDiv().allText()+". Refer to e.getServerErrors() for additional details.");
if(serverError != null) { error = serverError;
errors.add(serverError);
}
} }
/** public OperationOutcome getServerError() {
* Method returns all OperationOutcome server errors that are return error;
* associated with this exception.
*
* @return
*/
public List<OperationOutcome> getServerErrors() {
return errors;
} }
/** /**
@ -123,8 +119,8 @@ public class EFhirClientException extends RuntimeException {
* *
* @return * @return
*/ */
public boolean hasServerErrors() { public boolean hasServerError() {
return errors.size() > 0; return error != null;
} }
} }

View File

@ -421,16 +421,16 @@ public class FHIRToolingClient {
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null; org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try { try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"), result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())), ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
getPreferredResourceFormat(), getPreferredResourceFormat(),
generateHeaders(), generateHeaders(),
"ValueSet/$expand?url=" + source.getUrl(), "ValueSet/$expand?url=" + source.getUrl(),
TIMEOUT_OPERATION_EXPAND); TIMEOUT_OPERATION_EXPAND);
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload());
}
} catch (IOException e) { } 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(); 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_RETIRED_SRC = "MSG_RETIRED_SRC";
public static final String MSG_DRAFT_SRC = "MSG_DRAFT_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 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 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_DEPRECATED = "MSG_DEPENDS_ON_DEPRECATED";
public static final String MSG_DEPENDS_ON_WITHDRAWN = "MSG_DEPENDS_ON_WITHDRAWN"; 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} 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_reading__from_package__ = Error reading {0} from package {1}#{2}: {3}
Error_parsing_ = Error parsing {0}:{1} 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_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_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}'') 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} 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_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 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_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_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} 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_RETIRED_SRC = Reference to retired item {0} from {1}
MSG_EXPERIMENTAL_SRC = Reference to experimental item {0} from {1} MSG_EXPERIMENTAL_SRC = Reference to experimental item {0} from {1}
MSG_DRAFT_SRC = Reference to draft 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_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} 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 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.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; 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.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.XVerExtensionManager; 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 * 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 (standardsStatus == StandardsStatus.DEPRECATED) {
if (!statusWarnings.contains(vurl+":DEPRECATED")) { if (!statusWarnings.contains(vurl+":DEPRECATED")) {
statusWarnings.add(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) { } else if (standardsStatus == StandardsStatus.WITHDRAWN) {
if (!statusWarnings.contains(vurl+":WITHDRAWN")) { if (!statusWarnings.contains(vurl+":WITHDRAWN")) {
statusWarnings.add(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) { } else if (ex.getStatus() == PublicationStatus.RETIRED) {
if (!statusWarnings.contains(vurl+":RETIRED")) { if (!statusWarnings.contains(vurl+":RETIRED")) {
statusWarnings.add(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) { } else if (false && warnOnDraftOrExperimental && source != null) {
// for now, this is disabled; these warnings are just everywhere, and it's an intractible problem. // 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.ImplementationGuide.ImplementationGuideGlobalComponent;
import org.hl7.fhir.r5.model.InstantType; import org.hl7.fhir.r5.model.InstantType;
import org.hl7.fhir.r5.model.IntegerType; 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.Period;
import org.hl7.fhir.r5.model.PrimitiveType; import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Quantity; 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+"'"); timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'");
if (s == null) if (s == null)
return true; 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.isOk()) {
if (s.getMessage() != null) 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); 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; return true;
} }
if (s.getErrorClass() != null && s.getErrorClass().isInfrastructure()) 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.. // ignore this since we can't validate but it doesn't matter..
} else { } else {
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, true); // we're going to validate the codings directly, so only check the valueset 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()) { if (!vr.isOk()) {
bindingsOk = false; bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) { 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) { 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())) { if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem())) {
ValidationResult vr = checkCodeOnServer(stack, valueset, nextCoding, false); 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() != null/* && vr.hasMessage()*/) {
if (vr.getSeverity() == IssueSeverity.INFORMATION) { if (vr.getSeverity() == IssueSeverity.INFORMATION) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); 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.. // ignore this since we can't validate but it doesn't matter..
} else { } else {
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, false); // we're going to validate the codings directly 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()) { if (!vr.isOk()) {
bindingsOk = false; bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
@ -1465,6 +1487,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String nextVersion = nextCoding.getVersion(); String nextVersion = nextCoding.getVersion();
if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) { if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) {
ValidationResult vr = checkCodeOnServer(stack, nextCode, nextSystem, nextVersion, null, false); 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()) { 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); 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) { if (binding.getStrength() == BindingStrength.REQUIRED) {
removeTrackedMessagesForLocation(errors, element, path); 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+"'"); timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'");
if (vr != null && !vr.isOk()) { if (vr != null && !vr.isOk()) {
if (vr.IsNoService()) if (vr.IsNoService())
@ -1671,6 +1702,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try { try {
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, valueset, cc, false); 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()); timeTracker.tx(t, "vc "+cc.toString());
if (!vr.isOk()) { if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1708,6 +1744,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try { try {
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, valueset, c, true); 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()+"'"); timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'");
if (!vr.isOk()) { if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1737,6 +1779,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try { try {
long t = System.nanoTime(); long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, valueset, value, baseOptions.withLanguage(stack.getWorkingLang())); 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); timeTracker.tx(t, "vc "+value);
if (!vr.isOk()) { if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1788,7 +1835,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (theSystem != null && theCode != null && !noTerminologyChecks) { 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; ok = rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_VALUESET2, theSystem) && ok;
try { 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()) { if (theElementCntext != null && theElementCntext.hasBinding()) {
ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); 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)) { 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) { if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = checkCodeOnServer(stack, valueset, c, true); 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()+"'"); timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'");
if (binding.getStrength() == BindingStrength.REQUIRED) { if (binding.getStrength() == BindingStrength.REQUIRED) {
removeTrackedMessagesForLocation(errors, element, path); removeTrackedMessagesForLocation(errors, element, path);
@ -1862,6 +1915,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
} }
} else {
ok = false;
}
} catch (Exception e) { } catch (Exception e) {
if (STACK_TRACE) e.printStackTrace(); 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()); 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); 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+""); timeTracker.tx(t, "vc "+value+"");
if (binding.getStrength() == BindingStrength.REQUIRED) { if (binding.getStrength() == BindingStrength.REQUIRED) {
removeTrackedMessagesForLocation(errors, element, path); 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) { 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? // no delay on this one?
@ -6578,27 +6640,52 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
lang = "en"; // ubiquitious default languauge lang = "en"; // ubiquitious default languauge
} }
codingObserver.seeCode(stack, system, version, code, display); 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) { public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, Coding c, boolean checkMembership) {
codingObserver.seeCode(stack, c); codingObserver.seeCode(stack, c);
if (checkMembership) { if (checkMembership) {
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), c, valueset); return checkForInctive( context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), c, valueset));
} else { } 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) { public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, CodeableConcept cc, boolean vsOnly) {
codingObserver.seeCode(stack, cc); codingObserver.seeCode(stack, cc);
if (vsOnly) { if (vsOnly) {
return context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), cc, valueset); return checkForInctive(context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), cc, valueset));
} else { } 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() { public boolean isSecurityChecks() {
return securityChecks; return securityChecks;
} }

View File

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

View File

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

View File

@ -78,7 +78,7 @@ public class TerminologyServiceTests {
String contents = TestingUtilities.loadTestResource("tx", "test-cases.json"); String contents = TestingUtilities.loadTestResource("tx", "test-cases.json");
String externalSource = TestingUtilities.loadTestResource("tx", "messages-tx.fhir.org.json"); String externalSource = TestingUtilities.loadTestResource("tx", "messages-tx.fhir.org.json");
externals = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(externalSource); externals = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(externalSource);
Map<String, JsonObjectPair> examples = new HashMap<String, JsonObjectPair>(); Map<String, JsonObjectPair> examples = new HashMap<String, JsonObjectPair>();
manifest = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(contents); manifest = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(contents);
for (org.hl7.fhir.utilities.json.model.JsonObject suite : manifest.getJsonObjects("suites")) { for (org.hl7.fhir.utilities.json.model.JsonObject suite : manifest.getJsonObjects("suites")) {
@ -95,7 +95,7 @@ public class TerminologyServiceTests {
List<Object[]> objects = new ArrayList<Object[]>(examples.size()); List<Object[]> objects = new ArrayList<Object[]>(examples.size());
for (String id : names) { for (String id : names) {
objects.add(new Object[]{id, examples.get(id)}); objects.add(new Object[]{id, examples.get(id)});
} }
return objects; return objects;
} }
@ -105,7 +105,7 @@ public class TerminologyServiceTests {
private JsonObjectPair setup; private JsonObjectPair setup;
private String version; private String version;
private String name; private String name;
private static ValidationEngine baseEngine; private static ValidationEngine baseEngine;
@ -114,7 +114,7 @@ public class TerminologyServiceTests {
this.setup = setup; this.setup = setup;
version = "5.0.0"; version = "5.0.0";
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Test @Test
public void test() throws Exception { public void test() throws Exception {
@ -208,8 +208,9 @@ public class TerminologyServiceTests {
} }
e.getDetails().setText(vse.getError()); e.getDetails().setText(vse.getError());
oo.addIssue(e); oo.addIssue(e);
TxTesterSorters.sortOperationOutcome(oo);
TxTesterScrubbers.scrubOO(oo, false); TxTesterScrubbers.scrubOO(oo, false);
String ooj = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(oo); String ooj = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(oo);
String diff = CompareUtilities.checkJsonSrcIsSame(resp, ooj, ext); String diff = CompareUtilities.checkJsonSrcIsSame(resp, ooj, ext);
if (diff != null) { if (diff != null) {
@ -242,7 +243,7 @@ public class TerminologyServiceTests {
options = options.withLanguage(p.getParameterString("displayLanguage")); options = options.withLanguage(p.getParameterString("displayLanguage"));
} }
if (p.hasParameter("valueSetMode") && "CHECK_MEMBERSHIP_ONLY".equals(p.getParameterString("valueSetMode"))) { 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"))) { if (p.hasParameter("mode") && "lenient-display-validation".equals(p.getParameterString("mode"))) {
options = options.setDisplayWarningMode(true); options = options.setDisplayWarningMode(true);
@ -265,51 +266,73 @@ public class TerminologyServiceTests {
} else { } else {
throw new Error("validate not done yet for this steup"); 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.getSeverity() == org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.FATAL) {
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) {
OperationOutcome oo = new OperationOutcome(); OperationOutcome oo = new OperationOutcome();
oo.getIssue().addAll(vm.getIssues()); oo.getIssue().addAll(vm.getIssues());
res.addParameter().setName("issues").setResource(oo); TxTesterSorters.sortOperationOutcome(oo);
TxTesterScrubbers.scrubOO(oo, false);
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);
} }
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);
} }
public Resource loadResource(String filename) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { 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.model.StructureDefinition;
import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.settings.FhirSettings;
import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext; import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.model.FileInfo; import org.hl7.fhir.validation.cli.model.FileInfo;
@ -60,7 +61,7 @@ class ValidationServiceTest {
List<FileInfo> filesToValidate = new ArrayList<>(); List<FileInfo> filesToValidate = new ArrayList<>();
filesToValidate.add(new FileInfo().setFileName("test_resource.json").setFileContent(resource).setFileType(Manager.FhirFormat.JSON.getExtension())); 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 // Validation run 1...nothing cached yet
myService.validateSources(request); myService.validateSources(request);
verify(sessionCache, Mockito.times(1)).cacheSession(ArgumentMatchers.any(ValidationEngine.class)); 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.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.SystemExitManager; import org.hl7.fhir.utilities.SystemExitManager;
import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.settings.FhirSettings;
import org.hl7.fhir.validation.ValidatorCli; import org.hl7.fhir.validation.ValidatorCli;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -16,7 +17,7 @@ public class CDAValidationTest {
String fn = TestingUtilities.tempFile("cda", "cda.xml"); String fn = TestingUtilities.tempFile("cda", "cda.xml");
TextFile.stringToFile(TestingUtilities.loadTestResource("cda/cda-original.xml"), fn); TextFile.stringToFile(TestingUtilities.loadTestResource("cda/cda-original.xml"), fn);
SystemExitManager.setNoExit(true); 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()});
} }
} }