Revert "Fixes for the translation of parameter issues as part of the output f…" (#6441)

This reverts commit fca3ea4445.
This commit is contained in:
Tadgh 2024-11-05 09:14:18 -08:00 committed by GitHub
parent fca3ea4445
commit 9365ad9509
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 774 additions and 2299 deletions

View File

@ -440,259 +440,74 @@ public interface IValidationSupport {
return "Unknown " + getFhirContext().getVersion().getVersion() + " Validation Support"; return "Unknown " + getFhirContext().getVersion().getVersion() + " Validation Support";
} }
/**
* Defines codes in system <a href="http://hl7.org/fhir/issue-severity">http://hl7.org/fhir/issue-severity</a>.
*/
/* this enum would not be needed if we design/refactor to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult */
enum IssueSeverity { enum IssueSeverity {
/** /**
* The issue caused the action to fail, and no further checking could be performed. * The issue caused the action to fail, and no further checking could be performed.
*/ */
FATAL("fatal"), FATAL,
/** /**
* The issue is sufficiently important to cause the action to fail. * The issue is sufficiently important to cause the action to fail.
*/ */
ERROR("error"), ERROR,
/** /**
* The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired. * The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired.
*/ */
WARNING("warning"), WARNING,
/** /**
* The issue has no relation to the degree of success of the action. * The issue has no relation to the degree of success of the action.
*/ */
INFORMATION("information"), INFORMATION
/**
* The operation was successful.
*/
SUCCESS("success");
// the spec for OperationOutcome mentions that a code from http://hl7.org/fhir/issue-severity is required
private final String myCode;
IssueSeverity(String theCode) {
myCode = theCode;
}
/**
* Provide mapping to a code in system <a href="http://hl7.org/fhir/issue-severity">http://hl7.org/fhir/issue-severity</a>.
* @return the code
*/
public String getCode() {
return myCode;
}
/**
* Creates a {@link IssueSeverity} object from the given code.
* @return the {@link IssueSeverity}
*/
public static IssueSeverity fromCode(String theCode) {
switch (theCode) {
case "fatal":
return FATAL;
case "error":
return ERROR;
case "warning":
return WARNING;
case "information":
return INFORMATION;
case "success":
return SUCCESS;
default:
return null;
}
}
} }
/** enum CodeValidationIssueCode {
* Defines codes in system <a href="http://hl7.org/fhir/issue-type">http://hl7.org/fhir/issue-type</a>. NOT_FOUND,
* The binding is enforced as a part of validation logic in the FHIR Core Validation library where an exception is thrown. CODE_INVALID,
* Only a sub-set of these codes are defined as constants because they relate to validation, INVALID,
* If there are additional ones that come up, for Remote Terminology they are currently supported via OTHER
* {@link IValidationSupport.CodeValidationIssue#CodeValidationIssue(String, IssueSeverity, String)}
* while for internal validators, more constants can be added to make things easier and consistent.
* This maps to resource OperationOutcome.issue.code.
*/
/* this enum would not be needed if we design/refactor to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult */
class CodeValidationIssueCode {
public static final CodeValidationIssueCode NOT_FOUND = new CodeValidationIssueCode("not-found");
public static final CodeValidationIssueCode CODE_INVALID = new CodeValidationIssueCode("code-invalid");
public static final CodeValidationIssueCode INVALID = new CodeValidationIssueCode("invalid");
private final String myCode;
// this is intentionally not exposed
CodeValidationIssueCode(String theCode) {
myCode = theCode;
} }
/** enum CodeValidationIssueCoding {
* Retrieve the corresponding code from system <a href="http://hl7.org/fhir/issue-type">http://hl7.org/fhir/issue-type</a>. VS_INVALID,
* @return the code NOT_FOUND,
*/ NOT_IN_VS,
public String getCode() {
return myCode; INVALID_CODE,
} INVALID_DISPLAY,
OTHER
} }
/**
* Holds information about the details of a {@link CodeValidationIssue}.
* This maps to resource OperationOutcome.issue.details.
*/
/* this enum would not be needed if we design/refactor to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult */
class CodeValidationIssueDetails {
private final String myText;
private List<CodeValidationIssueCoding> myCodings;
public CodeValidationIssueDetails(String theText) {
myText = theText;
}
// intentionally not exposed
void addCoding(CodeValidationIssueCoding theCoding) {
getCodings().add(theCoding);
}
public CodeValidationIssueDetails addCoding(String theSystem, String theCode) {
if (myCodings == null) {
myCodings = new ArrayList<>();
}
myCodings.add(new CodeValidationIssueCoding(theSystem, theCode));
return this;
}
public String getText() {
return myText;
}
public List<CodeValidationIssueCoding> getCodings() {
if (myCodings == null) {
myCodings = new ArrayList<>();
}
return myCodings;
}
}
/**
* Defines codes that can be part of the details of an issue.
* There are some constants available (pre-defined) for codes for system <a href="http://hl7.org/fhir/tools/CodeSystem/tx-issue-type">http://hl7.org/fhir/tools/CodeSystem/tx-issue-type</a>.
* This maps to resource OperationOutcome.issue.details.coding[0].code.
*/
class CodeValidationIssueCoding {
public static String TX_ISSUE_SYSTEM = "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type";
public static CodeValidationIssueCoding VS_INVALID =
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "vs-invalid");
public static final CodeValidationIssueCoding NOT_FOUND =
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "not-found");
public static final CodeValidationIssueCoding NOT_IN_VS =
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "not-in-vs");
public static final CodeValidationIssueCoding INVALID_CODE =
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "invalid-code");
public static final CodeValidationIssueCoding INVALID_DISPLAY =
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "vs-display");
private final String mySystem, myCode;
// this is intentionally not exposed
CodeValidationIssueCoding(String theSystem, String theCode) {
mySystem = theSystem;
myCode = theCode;
}
/**
* Retrieve the corresponding code for the details of a validation issue.
* @return the code
*/
public String getCode() {
return myCode;
}
/**
* Retrieve the system for the details of a validation issue.
* @return the system
*/
public String getSystem() {
return mySystem;
}
}
/**
* This is a hapi-fhir internal version agnostic object holding information about a validation issue.
* An alternative (which requires significant refactoring) would be to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult instead.
*/
class CodeValidationIssue { class CodeValidationIssue {
private final String myDiagnostics;
private final String myMessage;
private final IssueSeverity mySeverity; private final IssueSeverity mySeverity;
private final CodeValidationIssueCode myCode; private final CodeValidationIssueCode myCode;
private CodeValidationIssueDetails myDetails; private final CodeValidationIssueCoding myCoding;
public CodeValidationIssue( public CodeValidationIssue(
String theDiagnostics, IssueSeverity theSeverity, CodeValidationIssueCode theTypeCode) { String theMessage,
this(theDiagnostics, theSeverity, theTypeCode, null); IssueSeverity mySeverity,
CodeValidationIssueCode theCode,
CodeValidationIssueCoding theCoding) {
this.myMessage = theMessage;
this.mySeverity = mySeverity;
this.myCode = theCode;
this.myCoding = theCoding;
} }
public CodeValidationIssue(String theDiagnostics, IssueSeverity theSeverity, String theTypeCode) {
this(theDiagnostics, theSeverity, new CodeValidationIssueCode(theTypeCode), null);
}
public CodeValidationIssue(
String theDiagnostics,
IssueSeverity theSeverity,
CodeValidationIssueCode theType,
CodeValidationIssueCoding theDetailsCoding) {
myDiagnostics = theDiagnostics;
mySeverity = theSeverity;
myCode = theType;
// reuse the diagnostics message as a detail text message
myDetails = new CodeValidationIssueDetails(theDiagnostics);
myDetails.addCoding(theDetailsCoding);
}
/**
* @deprecated Please use {@link #getDiagnostics()} instead.
*/
@Deprecated(since = "7.4.6")
public String getMessage() { public String getMessage() {
return getDiagnostics(); return myMessage;
}
public String getDiagnostics() {
return myDiagnostics;
} }
public IssueSeverity getSeverity() { public IssueSeverity getSeverity() {
return mySeverity; return mySeverity;
} }
/**
* @deprecated Please use {@link #getType()} instead.
*/
@Deprecated(since = "7.4.6")
public CodeValidationIssueCode getCode() { public CodeValidationIssueCode getCode() {
return getType();
}
public CodeValidationIssueCode getType() {
return myCode; return myCode;
} }
/**
* @deprecated Please use {@link #getDetails()} instead. That has support for multiple codings.
*/
@Deprecated(since = "7.4.6")
public CodeValidationIssueCoding getCoding() { public CodeValidationIssueCoding getCoding() {
return myDetails != null return myCoding;
? myDetails.getCodings().stream().findFirst().orElse(null)
: null;
}
public void setDetails(CodeValidationIssueDetails theDetails) {
this.myDetails = theDetails;
}
public CodeValidationIssueDetails getDetails() {
return myDetails;
}
public boolean hasIssueDetailCode(@Nonnull String theCode) {
// this method is system agnostic at the moment but it can be restricted if needed
return myDetails.getCodings().stream().anyMatch(coding -> theCode.equals(coding.getCode()));
} }
} }
@ -856,10 +671,6 @@ public interface IValidationSupport {
} }
} }
/**
* This is a hapi-fhir internal version agnostic object holding information about the validation result.
* An alternative (which requires significant refactoring) would be to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult.
*/
class CodeValidationResult { class CodeValidationResult {
public static final String SOURCE_DETAILS = "sourceDetails"; public static final String SOURCE_DETAILS = "sourceDetails";
public static final String RESULT = "result"; public static final String RESULT = "result";
@ -875,7 +686,7 @@ public interface IValidationSupport {
private String myDisplay; private String myDisplay;
private String mySourceDetails; private String mySourceDetails;
private List<CodeValidationIssue> myIssues; private List<CodeValidationIssue> myCodeValidationIssues;
public CodeValidationResult() { public CodeValidationResult() {
super(); super();
@ -960,48 +771,23 @@ public interface IValidationSupport {
return this; return this;
} }
/**
* @deprecated Please use method {@link #getIssues()} instead.
*/
@Deprecated(since = "7.4.6")
public List<CodeValidationIssue> getCodeValidationIssues() { public List<CodeValidationIssue> getCodeValidationIssues() {
return getIssues(); if (myCodeValidationIssues == null) {
myCodeValidationIssues = new ArrayList<>();
}
return myCodeValidationIssues;
} }
/**
* @deprecated Please use method {@link #setIssues(List)} instead.
*/
@Deprecated(since = "7.4.6")
public CodeValidationResult setCodeValidationIssues(List<CodeValidationIssue> theCodeValidationIssues) { public CodeValidationResult setCodeValidationIssues(List<CodeValidationIssue> theCodeValidationIssues) {
return setIssues(theCodeValidationIssues); myCodeValidationIssues = new ArrayList<>(theCodeValidationIssues);
return this;
} }
/**
* @deprecated Please use method {@link #addIssue(CodeValidationIssue)} instead.
*/
@Deprecated(since = "7.4.6")
public CodeValidationResult addCodeValidationIssue(CodeValidationIssue theCodeValidationIssue) { public CodeValidationResult addCodeValidationIssue(CodeValidationIssue theCodeValidationIssue) {
getCodeValidationIssues().add(theCodeValidationIssue); getCodeValidationIssues().add(theCodeValidationIssue);
return this; return this;
} }
public List<CodeValidationIssue> getIssues() {
if (myIssues == null) {
myIssues = new ArrayList<>();
}
return myIssues;
}
public CodeValidationResult setIssues(List<CodeValidationIssue> theIssues) {
myIssues = new ArrayList<>(theIssues);
return this;
}
public CodeValidationResult addIssue(CodeValidationIssue theCodeValidationIssue) {
getIssues().add(theCodeValidationIssue);
return this;
}
public boolean isOk() { public boolean isOk() {
return isNotBlank(myCode); return isNotBlank(myCode);
} }
@ -1025,19 +811,17 @@ public interface IValidationSupport {
public String getSeverityCode() { public String getSeverityCode() {
String retVal = null; String retVal = null;
if (getSeverity() != null) { if (getSeverity() != null) {
retVal = getSeverity().getCode(); retVal = getSeverity().name().toLowerCase();
} }
return retVal; return retVal;
} }
/** /**
* Sets an issue severity using a severity code. Please use method {@link #setSeverity(IssueSeverity)} instead. * Sets an issue severity as a string code. Value must be the name of
* @param theSeverityCode the code * one of the enum values in {@link IssueSeverity}. Value is case-insensitive.
* @return the current {@link CodeValidationResult} instance
*/ */
@Deprecated(since = "7.4.6") public CodeValidationResult setSeverityCode(@Nonnull String theIssueSeverity) {
public CodeValidationResult setSeverityCode(@Nonnull String theSeverityCode) { setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase()));
setSeverity(IssueSeverity.fromCode(theSeverityCode));
return this; return this;
} }
@ -1054,11 +838,6 @@ public interface IValidationSupport {
if (isNotBlank(getSourceDetails())) { if (isNotBlank(getSourceDetails())) {
ParametersUtil.addParameterToParametersString(theContext, retVal, SOURCE_DETAILS, getSourceDetails()); ParametersUtil.addParameterToParametersString(theContext, retVal, SOURCE_DETAILS, getSourceDetails());
} }
/*
should translate issues as well, except that is version specific code, so it requires more refactoring
or replace the current class with org.hl7.fhir.r5.terminologies.utilities.ValidationResult
@see VersionSpecificWorkerContextWrapper#getIssuesForCodeValidation
*/
return retVal; return retVal;
} }

View File

@ -1,7 +0,0 @@
---
type: fix
issue: 6422
title: "Previously, since 7.4.4 the validation issue detail codes were not translated correctly for Remote Terminology
validateCode calls. The detail code used was `invalid-code` for all use-cases which resulted in profile binding strength
not being applied to the issue severity as expected when validating resources against a profile.
This has been fixed and issue detail codes are translated correctly."

View File

@ -1044,8 +1044,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
if (theExpansionOptions != null if (theExpansionOptions != null
&& !theExpansionOptions.isFailOnMissingCodeSystem() && !theExpansionOptions.isFailOnMissingCodeSystem()
// Code system is unknown, therefore NOT_FOUND // Code system is unknown, therefore NOT_FOUND
&& e.getCodeValidationIssue() && e.getCodeValidationIssue().getCoding() == CodeValidationIssueCoding.NOT_FOUND) {
.hasIssueDetailCode(CodeValidationIssueCoding.NOT_FOUND.getCode())) {
return; return;
} }
throw new InternalErrorException(Msg.code(888) + e); throw new InternalErrorException(Msg.code(888) + e);
@ -2191,7 +2190,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
.setSeverity(IssueSeverity.ERROR) .setSeverity(IssueSeverity.ERROR)
.setCodeSystemVersion(theCodeSystemVersion) .setCodeSystemVersion(theCodeSystemVersion)
.setMessage(theMessage) .setMessage(theMessage)
.addIssue(new CodeValidationIssue( .addCodeValidationIssue(new CodeValidationIssue(
theMessage, theMessage,
IssueSeverity.ERROR, IssueSeverity.ERROR,
CodeValidationIssueCode.CODE_INVALID, CodeValidationIssueCode.CODE_INVALID,

View File

@ -1,21 +1,29 @@
package ca.uhn.fhir.jpa.validation; package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.JpaConfig; import ca.uhn.fhir.jpa.config.JpaConfig;
import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.test.utilities.validation.IValidationProviders; import jakarta.servlet.http.HttpServletRequest;
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -25,7 +33,9 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE; import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ -33,15 +43,15 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
/** /*
* This set of integration tests that instantiates and injects an instance of * This set of integration tests that instantiates and injects an instance of
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport} * {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
* into the ValidationSupportChain, which tests the logic of dynamically selecting the correct Remote Terminology * into the ValidationSupportChain, which tests the logic of dynamically selecting the correct Remote Terminology
* implementation. It also exercises the validateCode output translation code found in * implementation. It also exercises the code found in
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport} * {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
*/ */
public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test { public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeWithRemoteTerminologyR4Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeOperationWithRemoteTerminologyR4Test.class);
private static final String DISPLAY = "DISPLAY"; private static final String DISPLAY = "DISPLAY";
private static final String DISPLAY_BODY_MASS_INDEX = "Body mass index (BMI) [Ratio]"; private static final String DISPLAY_BODY_MASS_INDEX = "Body mass index (BMI) [Ratio]";
private static final String CODE_BODY_MASS_INDEX = "39156-5"; private static final String CODE_BODY_MASS_INDEX = "39156-5";
@ -54,8 +64,8 @@ public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProvide
protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx); protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private RemoteTerminologyServiceValidationSupport mySvc; private RemoteTerminologyServiceValidationSupport mySvc;
private IValidationProviders.MyValidationProvider<CodeSystem> myCodeSystemProvider; private MyCodeSystemProvider myCodeSystemProvider;
private IValidationProviders.MyValidationProvider<ValueSet> myValueSetProvider; private MyValueSetProvider myValueSetProvider;
@Autowired @Autowired
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN) @Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
@ -66,8 +76,8 @@ public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProvide
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort(); String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl); mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
myValidationSupportChain.addValidationSupport(0, mySvc); myValidationSupportChain.addValidationSupport(0, mySvc);
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4(); myCodeSystemProvider = new MyCodeSystemProvider();
myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4(); myValueSetProvider = new MyValueSetProvider();
ourRestfulServerExtension.registerProvider(myCodeSystemProvider); ourRestfulServerExtension.registerProvider(myCodeSystemProvider);
ourRestfulServerExtension.registerProvider(myValueSetProvider); ourRestfulServerExtension.registerProvider(myValueSetProvider);
} }
@ -93,11 +103,11 @@ public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProvide
@Test @Test
public void validateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() { public void validateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() {
final String code = "P"; myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
final String system = CODE_SYSTEM_V2_0247_URI;; myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
myCodeSystemProvider.myReturnParams = new Parameters();
Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY); myCodeSystemProvider.myReturnParams.addParameter("result", true);
setupCodeSystemValidateCode(system, code, params); myCodeSystemProvider.myReturnParams.addParameter("display", DISPLAY);
logAllConcepts(); logAllConcepts();
@ -105,8 +115,8 @@ public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProvide
.operation() .operation()
.onType(CodeSystem.class) .onType(CodeSystem.class)
.named(JpaConstants.OPERATION_VALIDATE_CODE) .named(JpaConstants.OPERATION_VALIDATE_CODE)
.withParameter(Parameters.class, "coding", new Coding().setSystem(system).setCode(code)) .withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
.andParameter("url", new UriType(system)) .andParameter("url", new UriType(CODE_SYSTEM_V2_0247_URI))
.execute(); .execute();
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -118,7 +128,7 @@ public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProvide
@Test @Test
public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() { public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() {
myCodeSystemProvider.setShouldThrowExceptionForResourceNotFound(false); myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
Parameters respParam = myClient Parameters respParam = myClient
.operation() .operation()
@ -156,21 +166,21 @@ public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProvide
@Test @Test
public void validateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() { public void validateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() {
final String code = "alerts"; myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
final String system = "http://terminology.hl7.org/CodeSystem/list-example-use-codes"; myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
final String valueSetUrl = "http://hl7.org/fhir/ValueSet/list-example-codes"; myValueSetProvider.myReturnValueSets = new ArrayList<>();
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY); myValueSetProvider.myReturnParams = new Parameters();
setupValueSetValidateCode(valueSetUrl, system, code, params); myValueSetProvider.myReturnParams.addParameter("result", true);
setupCodeSystemValidateCode(system, code, params); myValueSetProvider.myReturnParams.addParameter("display", DISPLAY);
Parameters respParam = myClient Parameters respParam = myClient
.operation() .operation()
.onType(ValueSet.class) .onType(ValueSet.class)
.named(JpaConstants.OPERATION_VALIDATE_CODE) .named(JpaConstants.OPERATION_VALIDATE_CODE)
.withParameter(Parameters.class, "code", new CodeType(code)) .withParameter(Parameters.class, "code", new CodeType("alerts"))
.andParameter("system", new UriType(system)) .andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/list-example-use-codes"))
.andParameter("url", new UriType(valueSetUrl)) .andParameter("url", new UriType("http://hl7.org/fhir/ValueSet/list-example-codes"))
.useHttpGet() .useHttpGet()
.execute(); .execute();
@ -183,20 +193,21 @@ public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProvide
@Test @Test
public void validateCodeOperationOnValueSet_byUrlSystemAndCode() { public void validateCodeOperationOnValueSet_byUrlSystemAndCode() {
final String code = CODE_BODY_MASS_INDEX; myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
final String system = "http://terminology.hl7.org/CodeSystem/list-example-use-codes"; myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
final String valueSetUrl = "http://hl7.org/fhir/ValueSet/list-example-codes"; myValueSetProvider.myReturnValueSets = new ArrayList<>();
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY_BODY_MASS_INDEX); myValueSetProvider.myReturnParams = new Parameters();
setupValueSetValidateCode(valueSetUrl, system, code, params); myValueSetProvider.myReturnParams.addParameter("result", true);
myValueSetProvider.myReturnParams.addParameter("display", DISPLAY_BODY_MASS_INDEX);
Parameters respParam = myClient Parameters respParam = myClient
.operation() .operation()
.onType(ValueSet.class) .onType(ValueSet.class)
.named(JpaConstants.OPERATION_VALIDATE_CODE) .named(JpaConstants.OPERATION_VALIDATE_CODE)
.withParameter(Parameters.class, "code", new CodeType(code)) .withParameter(Parameters.class, "code", new CodeType(CODE_BODY_MASS_INDEX))
.andParameter("url", new UriType(valueSetUrl)) .andParameter("url", new UriType("https://loinc.org"))
.andParameter("system", new UriType(system)) .andParameter("system", new UriType("http://loinc.org"))
.execute(); .execute();
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
@ -208,7 +219,7 @@ public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProvide
@Test @Test
public void validateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() { public void validateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() {
myValueSetProvider.setShouldThrowExceptionForResourceNotFound(false); myValueSetProvider.myReturnValueSets = new ArrayList<>();
Parameters respParam = myClient Parameters respParam = myClient
.operation() .operation()
@ -227,18 +238,70 @@ public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProvide
" - Unknown or unusable ValueSet[" + UNKNOWN_VALUE_SYSTEM_URI + "]"); " - Unknown or unusable ValueSet[" + UNKNOWN_VALUE_SYSTEM_URI + "]");
} }
private void setupValueSetValidateCode(String theUrl, String theSystem, String theCode, IBaseParameters theResponseParams) { @SuppressWarnings("unused")
ValueSet valueSet = myValueSetProvider.addTerminologyResource(theUrl); private static class MyCodeSystemProvider implements IResourceProvider {
myValueSetProvider.addTerminologyResource(theSystem); private List<CodeSystem> myReturnCodeSystems;
myValueSetProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, valueSet.getUrl(), theCode, theResponseParams); private Parameters myReturnParams;
// we currently do this because VersionSpecificWorkerContextWrapper has logic to infer the system when missing @Operation(name = "validate-code", idempotent = true, returnParameters = {
// based on the ValueSet by calling ValidationSupportUtils#extractCodeSystemForCode. @OperationParam(name = "result", type = BooleanType.class, min = 1),
valueSet.getCompose().addInclude().setSystem(theSystem); @OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class)
})
public Parameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
) {
return myReturnParams;
}
@Search
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
assert myReturnCodeSystems != null;
return myReturnCodeSystems;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
}
@SuppressWarnings("unused")
private static class MyValueSetProvider implements IResourceProvider {
private Parameters myReturnParams;
private List<ValueSet> myReturnValueSets;
@Operation(name = "validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class)
})
public Parameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
@OperationParam(name = "valueSet") ValueSet theValueSet
) {
return myReturnParams;
}
@Search
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam) {
assert myReturnValueSets != null;
return myReturnValueSets;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return ValueSet.class;
} }
private void setupCodeSystemValidateCode(String theUrl, String theCode, IBaseParameters theResponseParams) {
CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl);
myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, theResponseParams);
} }
} }

View File

@ -1,261 +0,0 @@
package ca.uhn.fhir.jpa.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.JpaConfig;
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
import ca.uhn.fhir.util.ClasspathUtil;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Procedure;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import java.util.List;
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests resource validation with Remote Terminology bindings.
* To create a new test, you need to do 3 things:
* (1) the resource profile, if any custom one is needed should be stored in the FHIR repository
* (2) all the CodeSystem and ValueSet terminology resources need to be added to the corresponding resource provider.
* At the moment only placeholder CodeSystem/ValueSet resources are returned with id and url populated. For the moment
* there was no need to load the full resource, but that can be done if there is logic run which requires it.
* This is a minimal setup.
* (3) the Remote Terminology operation responses that are needed for the test need to be added to the corresponding
* resource provider. The intention is to record and use the responses of an actual terminology server
* e.g. <a href="https://r4.ontoserver.csiro.au/fhir/">OntoServer</a>.
* This is done as a result of the fact that unit test cannot always catch bugs which are introduced as a result of
* changes in the OntoServer or FHIR Validator library, or both.
* @see #setupValueSetValidateCode
* @see #setupCodeSystemValidateCode
* The responses are in Parameters resource format where issues is an OperationOutcome resource.
*/
public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Test {
private static final FhirContext ourCtx = FhirContext.forR4Cached();
@RegisterExtension
protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private RemoteTerminologyServiceValidationSupport mySvc;
@Autowired
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
private ValidationSupportChain myValidationSupportChain;
private IValidationProviders.MyValidationProvider<CodeSystem> myCodeSystemProvider;
private IValidationProviders.MyValidationProvider<ValueSet> myValueSetProvider;
@BeforeEach
public void before() {
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
myValidationSupportChain.addValidationSupport(0, mySvc);
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4();
ourRestfulServerExtension.registerProvider(myCodeSystemProvider);
ourRestfulServerExtension.registerProvider(myValueSetProvider);
}
@AfterEach
public void after() {
myValidationSupportChain.removeValidationSupport(mySvc);
ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
ourRestfulServerExtension.unregisterProvider(myCodeSystemProvider);
ourRestfulServerExtension.unregisterProvider(myValueSetProvider);
}
@Test
public void validate_withProfileWithValidCodesFromAllBindingTypes_returnsNoErrors() {
// setup
final StructureDefinition profileEncounter = ClasspathUtil.loadResource(ourCtx, StructureDefinition.class, "validation/encounter/profile-encounter-custom.json");
myClient.update().resource(profileEncounter).execute();
final String statusCode = "planned";
final String classCode = "IMP";
final String identifierTypeCode = "VN";
final String statusSystem = "http://hl7.org/fhir/encounter-status"; // implied system
final String classSystem = "http://terminology.hl7.org/CodeSystem/v3-ActCode";
final String identifierTypeSystem = "http://terminology.hl7.org/CodeSystem/v2-0203";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/encounter-status", "http://hl7.org/fhir/encounter-status", statusCode, "validation/encounter/validateCode-ValueSet-encounter-status.json");
setupValueSetValidateCode("http://terminology.hl7.org/ValueSet/v3-ActEncounterCode", "http://terminology.hl7.org/CodeSystem/v3-ActCode", classCode, "validation/encounter/validateCode-ValueSet-v3-ActEncounterCode.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/identifier-type", "http://hl7.org/fhir/identifier-type", identifierTypeCode, "validation/encounter/validateCode-ValueSet-identifier-type.json");
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/encounter/validateCode-CodeSystem-encounter-status.json");
setupCodeSystemValidateCode(classSystem, classCode, "validation/encounter/validateCode-CodeSystem-v3-ActCode.json");
setupCodeSystemValidateCode(identifierTypeSystem, identifierTypeCode, "validation/encounter/validateCode-CodeSystem-v2-0203.json");
Encounter encounter = new Encounter();
encounter.getMeta().addProfile("http://example.ca/fhir/StructureDefinition/profile-encounter");
// required binding
encounter.setStatus(Encounter.EncounterStatus.fromCode(statusCode));
// preferred binding
encounter.getClass_()
.setSystem(classSystem)
.setCode(classCode)
.setDisplay("inpatient encounter");
// extensible binding
encounter.addIdentifier()
.getType().addCoding()
.setSystem(identifierTypeSystem)
.setCode(identifierTypeCode)
.setDisplay("Visit number");
// execute
List<String> errors = getValidationErrors(encounter);
// verify
assertThat(errors).isEmpty();
}
@Test
public void validate_withInvalidCode_returnsErrors() {
// setup
final String statusCode = "final";
final String code = "10xx";
final String statusSystem = "http://hl7.org/fhir/observation-status";
final String loincSystem = "http://loinc.org";
final String system = "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-status", statusSystem, statusCode, "validation/observation/validateCode-ValueSet-observation-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-codes", loincSystem, statusCode, "validation/observation/validateCode-ValueSet-codes.json");
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/observation/validateCode-CodeSystem-observation-status.json");
setupCodeSystemValidateCode(system, code, "validation/observation/validateCode-CodeSystem-ICD9CM.json");
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.fromCode(statusCode));
obs.getCode().addCoding().setCode(code).setSystem(system);
// execute
List<String> errors = getValidationErrors(obs);
assertThat(errors).hasSize(1);
// verify
assertThat(errors.get(0))
.contains("Unknown code '10xx' in the CodeSystem 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM");
}
@Test
public void validate_withProfileWithInvalidCode_returnsErrors() {
// setup
String profile = "http://example.ca/fhir/StructureDefinition/profile-procedure";
StructureDefinition profileProcedure = ClasspathUtil.loadResource(myFhirContext, StructureDefinition.class, "validation/procedure/profile-procedure.json");
myClient.update().resource(profileProcedure).execute();
final String statusCode = "completed";
final String procedureCode1 = "417005";
final String procedureCode2 = "xx417005";
final String statusSystem = "http://hl7.org/fhir/event-status";
final String snomedSystem = "http://snomed.info/sct";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode1, "validation/procedure/validateCode-ValueSet-procedure-code-valid.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode2, "validation/procedure/validateCode-ValueSet-procedure-code-invalid.json");
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/procedure/validateCode-CodeSystem-event-status.json");
setupCodeSystemValidateCode(snomedSystem, procedureCode1, "validation/procedure/validateCode-CodeSystem-snomed-valid.json");
setupCodeSystemValidateCode(snomedSystem, procedureCode2, "validation/procedure/validateCode-CodeSystem-snomed-invalid.json");
Procedure procedure = new Procedure();
procedure.setSubject(new Reference("Patient/P1"));
procedure.setStatus(Procedure.ProcedureStatus.fromCode(statusCode));
procedure.getCode().addCoding().setSystem(snomedSystem).setCode(procedureCode1);
procedure.getCode().addCoding().setSystem(snomedSystem).setCode(procedureCode2);
procedure.getMeta().addProfile(profile);
// execute
List<String> errors = getValidationErrors(procedure);
// TODO: there is currently some duplication in the errors returned. This needs to be investigated and fixed.
// assertThat(errors).hasSize(1);
// verify
// note that we're not selecting an explicit versions (using latest) so the message verification does not include it.
assertThat(StringUtils.join("", errors))
.contains("Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct'")
.doesNotContain("The provided code 'http://snomed.info/sct#xx417005' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code")
.doesNotContain("http://snomed.info/sct#417005");
}
@Test
public void validate_withProfileWithSlicingWithValidCode_returnsNoErrors() {
// setup
String profile = "http://example.ca/fhir/StructureDefinition/profile-procedure-with-slicing";
StructureDefinition profileProcedure = ClasspathUtil.loadResource(myFhirContext, StructureDefinition.class, "validation/procedure/profile-procedure-slicing.json");
myClient.update().resource(profileProcedure).execute();
final String statusCode = "completed";
final String procedureCode = "no-procedure-info";
final String statusSystem = "http://hl7.org/fhir/event-status";
final String snomedSystem = "http://snomed.info/sct";
final String absentUnknownSystem = "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode, "validation/procedure/validateCode-ValueSet-procedure-code-invalid-slice.json");
setupValueSetValidateCode("http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips", absentUnknownSystem, procedureCode, "validation/procedure/validateCode-ValueSet-absent-or-unknown-procedure.json");
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/procedure/validateCode-CodeSystem-event-status.json");
setupCodeSystemValidateCode(absentUnknownSystem, procedureCode, "validation/procedure/validateCode-CodeSystem-absent-or-unknown.json");
Procedure procedure = new Procedure();
procedure.setSubject(new Reference("Patient/P1"));
procedure.setStatus(Procedure.ProcedureStatus.fromCode(statusCode));
procedure.getCode().addCoding().setSystem(absentUnknownSystem).setCode(procedureCode);
procedure.getMeta().addProfile(profile);
// execute
List<String> errors = getValidationErrors(procedure);
assertThat(errors).hasSize(0);
}
private void setupValueSetValidateCode(String theUrl, String theSystem, String theCode, String theTerminologyResponseFile) {
ValueSet valueSet = myValueSetProvider.addTerminologyResource(theUrl);
myCodeSystemProvider.addTerminologyResource(theSystem);
myValueSetProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, valueSet.getUrl(), theCode, ourCtx, theTerminologyResponseFile);
// we currently do this because VersionSpecificWorkerContextWrapper has logic to infer the system when missing
// based on the ValueSet by calling ValidationSupportUtils#extractCodeSystemForCode.
valueSet.getCompose().addInclude().setSystem(theSystem);
// you will notice each of these calls require also a call to setupCodeSystemValidateCode
// that is necessary because VersionSpecificWorkerContextWrapper#validateCodeInValueSet
// which also attempts a validateCode against the CodeSystem after the validateCode against the ValueSet
}
private void setupCodeSystemValidateCode(String theUrl, String theCode, String theTerminologyResponseFile) {
CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl);
myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, ourCtx, theTerminologyResponseFile);
}
private List<String> getValidationErrors(IBaseResource theResource) {
MethodOutcome resultProcedure = myClient.validate().resource(theResource).execute();
OperationOutcome operationOutcome = (OperationOutcome) resultProcedure.getOperationOutcome();
return operationOutcome.getIssue().stream()
.filter(issue -> issue.getSeverity() == OperationOutcome.IssueSeverity.ERROR)
.map(OperationOutcome.OperationOutcomeIssueComponent::getDiagnostics)
.toList();
}
}

View File

@ -1,49 +0,0 @@
{
"resourceType": "StructureDefinition",
"id": "profile-encounter",
"url": "http://example.ca/fhir/StructureDefinition/profile-encounter",
"version": "0.11.0",
"name": "EncounterProfile",
"title": "Encounter Profile",
"status": "active",
"date": "2022-10-15T12:00:00+00:00",
"publisher": "Example Organization",
"fhirVersion": "4.0.1",
"kind": "resource",
"abstract": false,
"type": "Encounter",
"baseDefinition": "http://hl7.org/fhir/StructureDefinition/Encounter",
"derivation": "constraint",
"differential": {
"element": [
{
"id": "Encounter.identifier.type.coding",
"path": "Encounter.identifier.type.coding",
"min": 1,
"max": "1",
"mustSupport": true
},
{
"id": "Encounter.identifier.type.coding.system",
"path": "Encounter.identifier.type.coding.system",
"min": 1,
"fixedUri": "http://terminology.hl7.org/CodeSystem/v2-0203",
"mustSupport": true
},
{
"id": "Encounter.identifier.type.coding.code",
"path": "Encounter.identifier.type.coding.code",
"min": 1,
"fixedCode": "VN",
"mustSupport": true
},
{
"id": "Encounter.identifier.type.coding.display",
"path": "Encounter.identifier.type.coding.display",
"min": 1,
"fixedString": "Visit number",
"mustSupport": true
}
]
}
}

View File

@ -1,59 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "planned"
},
{
"name": "system",
"valueUri": "http://hl7.org/fhir/encounter-status"
},
{
"name": "version",
"valueString": "5.0.0-ballot"
},
{
"name": "display",
"valueString": "Planned"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
}
},
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to draft CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
}
}
]
}
}
]
}

View File

@ -1,25 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "VN"
},
{
"name": "system",
"valueUri": "http://terminology.hl7.org/CodeSystem/v2-0203"
},
{
"name": "version",
"valueString": "3.0.0"
},
{
"name": "display",
"valueString": "Visit number"
}
]
}

View File

@ -1,46 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "IMP"
},
{
"name": "system",
"valueUri": "http://terminology.hl7.org/CodeSystem/v3-ActCode"
},
{
"name": "version",
"valueString": "2018-08-12"
},
{
"name": "display",
"valueString": "inpatient encounter"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to draft CodeSystem http://terminology.hl7.org/CodeSystem/v3-ActCode|2018-08-12"
}
}
]
}
}
]
}

View File

@ -1,59 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "planned"
},
{
"name": "system",
"valueUri": "http://hl7.org/fhir/encounter-status"
},
{
"name": "version",
"valueString": "5.0.0-ballot"
},
{
"name": "display",
"valueString": "Planned"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
}
},
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to draft CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
}
}
]
}
}
]
}

View File

@ -1,52 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": false
},
{
"name": "code",
"valueCode": "VN"
},
{
"name": "system",
"valueUri": "http://terminology.hl7.org/CodeSystem/v2-0203"
},
{
"name": "version",
"valueString": "3.0.0"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "error",
"code": "code-invalid",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "not-in-vs"
}
],
"text": "The provided code 'http://terminology.hl7.org/CodeSystem/v2-0203#VN' was not found in the value set 'http://hl7.org/fhir/ValueSet/identifier-type|5.0.0-ballot'"
},
"location": [
"code"
],
"expression": [
"code"
]
}
]
}
},
{
"name": "message",
"valueString": "The provided code 'http://terminology.hl7.org/CodeSystem/v2-0203#VN' was not found in the value set 'http://hl7.org/fhir/ValueSet/identifier-type|5.0.0-ballot'"
}
]
}

View File

@ -1,59 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "IMP"
},
{
"name": "system",
"valueUri": "http://terminology.hl7.org/CodeSystem/v3-ActCode"
},
{
"name": "version",
"valueString": "2018-08-12"
},
{
"name": "display",
"valueString": "inpatient encounter"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to trial-use ValueSet http://terminology.hl7.org/ValueSet/v3-ActEncounterCode|2014-03-26"
}
},
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to draft CodeSystem http://terminology.hl7.org/CodeSystem/v3-ActCode|2018-08-12"
}
}
]
}
}
]
}

View File

@ -1,48 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": false
},
{
"name": "code",
"valueCode": "10xx"
},
{
"name": "system",
"valueUri": "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "error",
"code": "code-invalid",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "invalid-code"
}
],
"text": "Unknown code '10xx' in the CodeSystem 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM' version '0.1.0'"
},
"location": [
"code"
],
"expression": [
"code"
]
}
]
}
},
{
"name": "message",
"valueString": "Unknown code '10xx' in the CodeSystem 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM' version '0.1.0'"
}
]
}

View File

@ -1,25 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "final"
},
{
"name": "system",
"valueUri": "http://hl7.org/fhir/observation-status"
},
{
"name": "version",
"valueString": "5.0.0-ballot"
},
{
"name": "display",
"valueString": "Final"
}
]
}

View File

@ -1,48 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": false
},
{
"name": "code",
"valueCode": "10xx"
},
{
"name": "system",
"valueUri": "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "error",
"code": "code-invalid",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "not-in-vs"
}
],
"text": "The provided code 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM#10xx' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-codes|5.0.0-ballot'"
},
"location": [
"code"
],
"expression": [
"code"
]
}
]
}
},
{
"name": "message",
"valueString": "The provided code 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM#10xx' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-codes|5.0.0-ballot'"
}
]
}

View File

@ -1,25 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "final"
},
{
"name": "system",
"valueUri": "http://hl7.org/fhir/observation-status"
},
{
"name": "version",
"valueString": "5.0.0-ballot"
},
{
"name": "display",
"valueString": "Final"
}
]
}

View File

@ -1,79 +0,0 @@
{
"resourceType": "StructureDefinition",
"id": "profile-procedure-with-slicing",
"url": "http://example.ca/fhir/StructureDefinition/profile-procedure-with-slicing",
"version": "0.11.0",
"name": "ProcedureProfile",
"title": "Procedure Profile",
"status": "active",
"date": "2022-10-15T12:00:00+00:00",
"publisher": "Example Organization",
"fhirVersion": "4.0.1",
"kind": "resource",
"abstract": false,
"type": "Procedure",
"baseDefinition": "http://hl7.org/fhir/StructureDefinition/Procedure",
"derivation": "constraint",
"differential": {
"element": [
{
"id": "Procedure.code.coding",
"path": "Procedure.code.coding",
"slicing": {
"discriminator": [
{
"type": "pattern",
"path": "$this"
}
],
"description": "Discriminated by the bound value set",
"rules": "open"
},
"mustSupport": true,
"binding": {
"strength": "preferred",
"valueSet": "http://hl7.org/fhir/ValueSet/procedure-code"
}
},
{
"id": "Procedure.code.coding.display.extension:translation",
"path": "Procedure.code.coding.display.extension",
"sliceName": "translation"
},
{
"id": "Procedure.code.coding.display.extension:translation.extension",
"path": "Procedure.code.coding.display.extension.extension",
"min": 2
},
{
"id": "Procedure.code.coding:absentOrUnknownProcedure",
"path": "Procedure.code.coding",
"sliceName": "absentOrUnknownProcedure",
"short": "Optional slice for representing a code for absent problem or for unknown procedure",
"definition": "Code representing the statement \"absent problem\" or the statement \"procedures unknown\"",
"mustSupport": true,
"binding": {
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName",
"valueString": "absentOrUnknownProcedure"
}
],
"strength": "required",
"description": "A code to identify absent or unknown procedures",
"valueSet": "http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips"
}
},
{
"id": "Procedure.code.coding:absentOrUnknownProcedure.display.extension:translation",
"path": "Procedure.code.coding.display.extension",
"sliceName": "translation"
},
{
"id": "Procedure.code.coding:absentOrUnknownProcedure.display.extension:translation.extension",
"path": "Procedure.code.coding.display.extension.extension",
"min": 2
}
]
}
}

View File

@ -1,50 +0,0 @@
{
"resourceType": "StructureDefinition",
"id": "profile-procedure",
"url": "http://example.ca/fhir/StructureDefinition/profile-procedure",
"version": "0.11.0",
"name": "ProcedureProfile",
"title": "Procedure Profile",
"status": "active",
"date": "2022-10-15T12:00:00+00:00",
"publisher": "Example Organization",
"fhirVersion": "4.0.1",
"kind": "resource",
"abstract": false,
"type": "Procedure",
"baseDefinition": "http://hl7.org/fhir/StructureDefinition/Procedure",
"derivation": "constraint",
"differential": {
"element": [
{
"id": "Procedure.code.coding",
"path": "Procedure.code.coding",
"slicing": {
"discriminator": [
{
"type": "pattern",
"path": "$this"
}
],
"description": "Discriminated by the bound value set",
"rules": "open"
},
"mustSupport": true,
"binding": {
"strength": "preferred",
"valueSet": "http://hl7.org/fhir/ValueSet/procedure-code"
}
},
{
"id": "Procedure.code.coding.display.extension:translation",
"path": "Procedure.code.coding.display.extension",
"sliceName": "translation"
},
{
"id": "Procedure.code.coding.display.extension:translation.extension",
"path": "Procedure.code.coding.display.extension.extension",
"min": 2
}
]
}
}

View File

@ -1,46 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "no-procedure-info"
},
{
"name": "system",
"valueUri": "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"
},
{
"name": "version",
"valueString": "1.1.0"
},
{
"name": "display",
"valueString": "No information about past history of procedures"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips|1.1.0"
}
}
]
}
}
]
}

View File

@ -1,59 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "completed"
},
{
"name": "system",
"valueUri": "http://hl7.org/fhir/event-status"
},
{
"name": "version",
"valueString": "5.0.0-ballot"
},
{
"name": "display",
"valueString": "Completed"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/event-status|5.0.0-ballot"
}
},
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to experimental CodeSystem http://hl7.org/fhir/event-status|5.0.0-ballot"
}
}
]
}
}
]
}

View File

@ -1,48 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": false
},
{
"name": "code",
"valueCode": "xx417005"
},
{
"name": "system",
"valueUri": "http://snomed.info/sct"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "error",
"code": "code-invalid",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "invalid-code"
}
],
"text": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'"
},
"location": [
"code"
],
"expression": [
"code"
]
}
]
}
},
{
"name": "message",
"valueString": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'"
}
]
}

View File

@ -1,25 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "417005"
},
{
"name": "system",
"valueUri": "http://snomed.info/sct"
},
{
"name": "version",
"valueString": "http://snomed.info/sct/32506021000036107/version/20241031"
},
{
"name": "display",
"valueString": "Hospital re-admission"
}
]
}

View File

@ -1,59 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "no-procedure-info"
},
{
"name": "system",
"valueUri": "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"
},
{
"name": "version",
"valueString": "1.1.0"
},
{
"name": "display",
"valueString": "No information about past history of procedures"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips|1.1.0"
}
},
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to trial-use ValueSet http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips|1.1.0"
}
}
]
}
}
]
}

View File

@ -1,25 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "final"
},
{
"name": "system",
"valueUri": "http://hl7.org/fhir/procedure-status"
},
{
"name": "version",
"valueString": "5.0.0-ballot"
},
{
"name": "display",
"valueString": "Final"
}
]
}

View File

@ -1,48 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": false
},
{
"name": "code",
"valueCode": "no-procedure-info"
},
{
"name": "system",
"valueUri": "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "error",
"code": "code-invalid",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "not-in-vs"
}
],
"text": "The provided code 'http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips#no-procedure-info' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
},
"location": [
"code"
],
"expression": [
"code"
]
}
]
}
},
{
"name": "message",
"valueString": "The provided code 'http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips#no-procedure-info' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
}
]
}

View File

@ -1,67 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": false
},
{
"name": "code",
"valueCode": "xx417005"
},
{
"name": "system",
"valueUri": "http://snomed.info/sct"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "error",
"code": "code-invalid",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "not-in-vs"
}
],
"text": "The provided code 'http://snomed.info/sct#xx417005' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
},
"location": [
"code"
],
"expression": [
"code"
]
},
{
"severity": "error",
"code": "code-invalid",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "invalid-code"
}
],
"text": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'"
},
"location": [
"code"
],
"expression": [
"code"
]
}
]
}
},
{
"name": "message",
"valueString": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'; The provided code 'http://snomed.info/sct#xx417005' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
}
]
}

View File

@ -1,59 +0,0 @@
{
"resourceType": "Parameters",
"parameter": [
{
"name": "result",
"valueBoolean": true
},
{
"name": "code",
"valueCode": "417005"
},
{
"name": "system",
"valueUri": "http://snomed.info/sct"
},
{
"name": "version",
"valueString": "http://snomed.info/sct/32506021000036107/version/20241031"
},
{
"name": "display",
"valueString": "Hospital re-admission"
},
{
"name": "issues",
"resource": {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to draft ValueSet http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot"
}
},
{
"severity": "information",
"code": "business-rule",
"details": {
"coding": [
{
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code": "status-check"
}
],
"text": "Reference to experimental ValueSet http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot"
}
}
]
}
}
]
}

View File

@ -1,104 +0,0 @@
package ca.uhn.fhir.test.utilities.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.util.ClasspathUtil;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IDomainResource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public interface IValidationProviders {
String CODE_SYSTEM = "http://code.system/url";
String CODE_SYSTEM_VERSION = "1.0.0";
String CODE_SYSTEM_NAME = "Test Code System";
String CODE = "CODE";
String VALUE_SET_URL = "http://value.set/url";
String DISPLAY = "Explanation for code TestCode.";
String LANGUAGE = "en";
String ERROR_MESSAGE = "This is an error message";
interface IMyValidationProvider extends IResourceProvider {
void addException(String theOperation, String theUrl, String theCode, Exception theException);
<P extends IBaseParameters> void addTerminologyResponse(String theOperation, String theUrl, String theCode, P theReturnParams);
IBaseParameters addTerminologyResponse(String theOperation, String theUrl, String theCode, FhirContext theFhirContext, String theTerminologyResponseFile);
}
abstract class MyValidationProvider<T extends IDomainResource> implements IMyValidationProvider {
private final Map<String, Exception> myExceptionMap = new HashMap<>();
private boolean myShouldThrowExceptionForResourceNotFound = true;
private final Map<String, IBaseParameters> myTerminologyResponseMap = new HashMap<>();
private final Map<String, T> myTerminologyResourceMap = new HashMap<>();
static String getInputKey(String theOperation, String theUrl, String theCode) {
return theOperation + "-" + theUrl + "#" + theCode;
}
public void setShouldThrowExceptionForResourceNotFound(boolean theShouldThrowExceptionForResourceNotFound) {
myShouldThrowExceptionForResourceNotFound = theShouldThrowExceptionForResourceNotFound;
}
public void addException(String theOperation, String theUrl, String theCode, Exception theException) {
String inputKey = getInputKey(theOperation, theUrl, theCode);
myExceptionMap.put(inputKey, theException);
}
abstract Class<? extends IBaseParameters> getParameterType();
@Override
public <P extends IBaseParameters> void addTerminologyResponse(String theOperation, String theUrl, String theCode, P theReturnParams) {
myTerminologyResponseMap.put(getInputKey(theOperation, theUrl, theCode), theReturnParams);
}
public IBaseParameters addTerminologyResponse(String theOperation, String theUrl, String theCode, FhirContext theFhirContext, String theTerminologyResponseFile) {
IBaseParameters responseParams = ClasspathUtil.loadResource(theFhirContext, getParameterType(), theTerminologyResponseFile);
addTerminologyResponse(theOperation, theUrl, theCode, responseParams);
return responseParams;
}
protected void addTerminologyResource(String theUrl, T theResource) {
myTerminologyResourceMap.put(theUrl, theResource);
}
public abstract T addTerminologyResource(String theUrl);
protected IBaseParameters getTerminologyResponse(String theOperation, String theUrl, String theCode) throws Exception {
String inputKey = getInputKey(theOperation, theUrl, theCode);
if (myExceptionMap.containsKey(inputKey)) {
throw myExceptionMap.get(inputKey);
}
IBaseParameters params = myTerminologyResponseMap.get(inputKey);
if (params == null) {
throw new IllegalStateException("Test setup incomplete. Missing return params for " + inputKey);
}
return params;
}
protected T getTerminologyResource(UriParam theUrlParam) {
if (theUrlParam.isEmpty()) {
throw new IllegalStateException("CodeSystem url should not be null.");
}
String urlValue = theUrlParam.getValue();
if (!myTerminologyResourceMap.containsKey(urlValue) && myShouldThrowExceptionForResourceNotFound) {
throw new IllegalStateException("Test setup incomplete. CodeSystem not found " + urlValue);
}
return myTerminologyResourceMap.get(urlValue);
}
@Search
public List<T> find(@RequiredParam(name = "url") UriParam theUrlParam) {
T resource = getTerminologyResource(theUrlParam);
return resource != null ? List.of(resource) : List.of();
}
}
interface IMyLookupCodeProvider extends IResourceProvider {
void setLookupCodeResult(IValidationSupport.LookupCodeResult theLookupCodeResult);
}
}

View File

@ -1,118 +0,0 @@
package ca.uhn.fhir.test.utilities.validation;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import jakarta.servlet.http.HttpServletRequest;
import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.List;
public interface IValidationProvidersDstu3 {
@SuppressWarnings("unused")
class MyCodeSystemProviderDstu3 extends IValidationProviders.MyValidationProvider<CodeSystem> {
@Operation(name = "$validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class)
})
public IBaseParameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
) throws Exception {
String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null;
String code = theCode != null ? theCode.getValue() : null;
return getTerminologyResponse("$validate-code", url, code);
}
@Operation(name = "$lookup", idempotent = true, returnParameters= {
@OperationParam(name = "name", type = StringType.class, min = 1),
@OperationParam(name = "version", type = StringType.class),
@OperationParam(name = "display", type = StringType.class, min = 1),
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
@OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
})
public IBaseParameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name = "code", max = 1) CodeType theCode,
@OperationParam(name = "system",max = 1) UriType theSystem,
@OperationParam(name = "coding", max = 1) Coding theCoding,
@OperationParam(name = "version", max = 1) StringType theVersion,
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
RequestDetails theRequestDetails
) throws Exception {
String url = theSystem != null ? theSystem.getValue() : null;
String code = theCode != null ? theCode.getValue() : null;
return getTerminologyResponse("$lookup", url, code);
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
@Override
Class<Parameters> getParameterType() {
return Parameters.class;
}
@Override
public CodeSystem addTerminologyResource(String theUrl) {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
codeSystem.setUrl(theUrl);
addTerminologyResource(theUrl, codeSystem);
return codeSystem;
}
}
@SuppressWarnings("unused")
class MyValueSetProviderDstu3 extends IValidationProviders.MyValidationProvider<ValueSet> {
@Operation(name = "$validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class)
})
public IBaseParameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
@OperationParam(name = "valueSet") ValueSet theValueSet
) throws Exception {
String url = theValueSetUrl != null ? theValueSetUrl.getValue() : null;
String code = theCode != null ? theCode.getValue() : null;
return getTerminologyResponse("$validate-code", url, code);
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return ValueSet.class;
}
@Override
Class<Parameters> getParameterType() {
return Parameters.class;
}
@Override
public ValueSet addTerminologyResource(String theUrl) {
ValueSet valueSet = new ValueSet();
valueSet.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
valueSet.setUrl(theUrl);
addTerminologyResource(theUrl, valueSet);
return valueSet;
}
}
}

View File

@ -190,7 +190,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
return new CodeValidationResult() return new CodeValidationResult()
.setSeverity(IssueSeverity.ERROR) .setSeverity(IssueSeverity.ERROR)
.setMessage(theMessage) .setMessage(theMessage)
.setIssues(Collections.singletonList(new CodeValidationIssue( .setCodeValidationIssues(Collections.singletonList(new CodeValidationIssue(
theMessage, theMessage,
IssueSeverity.ERROR, IssueSeverity.ERROR,
CodeValidationIssueCode.INVALID, CodeValidationIssueCode.INVALID,

View File

@ -28,6 +28,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Enumerations; import org.hl7.fhir.r5.model.Enumerations;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -257,7 +258,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode); theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode);
} catch (ExpansionCouldNotBeCompletedInternallyException e) { } catch (ExpansionCouldNotBeCompletedInternallyException e) {
CodeValidationResult codeValidationResult = new CodeValidationResult(); CodeValidationResult codeValidationResult = new CodeValidationResult();
codeValidationResult.setSeverity(IssueSeverity.ERROR); codeValidationResult.setSeverityCode("error");
String msg = "Failed to expand ValueSet '" + vsUrl + "' (in-memory). Could not validate code " String msg = "Failed to expand ValueSet '" + vsUrl + "' (in-memory). Could not validate code "
+ theCodeSystemUrlAndVersion + "#" + theCode; + theCodeSystemUrlAndVersion + "#" + theCode;
@ -266,7 +267,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
} }
codeValidationResult.setMessage(msg); codeValidationResult.setMessage(msg);
codeValidationResult.addIssue(e.getCodeValidationIssue()); codeValidationResult.addCodeValidationIssue(e.getCodeValidationIssue());
return codeValidationResult; return codeValidationResult;
} }
@ -550,18 +551,18 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
if (valueSetResult != null) { if (valueSetResult != null) {
codeValidationResult = valueSetResult; codeValidationResult = valueSetResult;
} else { } else {
IValidationSupport.IssueSeverity severity; ValidationMessage.IssueSeverity severity;
String message; String message;
CodeValidationIssueCode issueCode = CodeValidationIssueCode.CODE_INVALID; CodeValidationIssueCode issueCode = CodeValidationIssueCode.CODE_INVALID;
CodeValidationIssueCoding issueCoding = CodeValidationIssueCoding.INVALID_CODE; CodeValidationIssueCoding issueCoding = CodeValidationIssueCoding.INVALID_CODE;
if ("fragment".equals(codeSystemResourceContentMode)) { if ("fragment".equals(codeSystemResourceContentMode)) {
severity = IValidationSupport.IssueSeverity.WARNING; severity = ValidationMessage.IssueSeverity.WARNING;
message = "Unknown code in fragment CodeSystem '" message = "Unknown code in fragment CodeSystem '"
+ getFormattedCodeSystemAndCodeForMessage( + getFormattedCodeSystemAndCodeForMessage(
theCodeSystemUrlAndVersionToValidate, theCodeToValidate) theCodeSystemUrlAndVersionToValidate, theCodeToValidate)
+ "'"; + "'";
} else { } else {
severity = IValidationSupport.IssueSeverity.ERROR; severity = ValidationMessage.IssueSeverity.ERROR;
message = "Unknown code '" message = "Unknown code '"
+ getFormattedCodeSystemAndCodeForMessage( + getFormattedCodeSystemAndCodeForMessage(
theCodeSystemUrlAndVersionToValidate, theCodeToValidate) theCodeSystemUrlAndVersionToValidate, theCodeToValidate)
@ -573,9 +574,10 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
} }
codeValidationResult = new CodeValidationResult() codeValidationResult = new CodeValidationResult()
.setSeverity(severity) .setSeverityCode(severity.toCode())
.setMessage(message) .setMessage(message)
.addIssue(new CodeValidationIssue(message, severity, issueCode, issueCoding)); .addCodeValidationIssue(new CodeValidationIssue(
message, getIssueSeverityFromCodeValidationIssue(severity), issueCode, issueCoding));
} }
return codeValidationResult; return codeValidationResult;
@ -587,6 +589,19 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
+ theCodeToValidate; + theCodeToValidate;
} }
private IValidationSupport.IssueSeverity getIssueSeverityFromCodeValidationIssue(
ValidationMessage.IssueSeverity theSeverity) {
switch (theSeverity) {
case ERROR:
return IValidationSupport.IssueSeverity.ERROR;
case WARNING:
return IValidationSupport.IssueSeverity.WARNING;
case INFORMATION:
return IValidationSupport.IssueSeverity.INFORMATION;
}
return null;
}
private CodeValidationResult findCodeInExpansion( private CodeValidationResult findCodeInExpansion(
String theCodeToValidate, String theCodeToValidate,
String theDisplayToValidate, String theDisplayToValidate,
@ -1108,8 +1123,8 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
new CodeValidationIssue( new CodeValidationIssue(
theMessage, theMessage,
IssueSeverity.ERROR, IssueSeverity.ERROR,
CodeValidationIssueCode.INVALID, CodeValidationIssueCode.OTHER,
CodeValidationIssueCoding.VS_INVALID)); CodeValidationIssueCoding.OTHER));
} }
for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next :
subExpansion.getExpansion().getContains()) { subExpansion.getExpansion().getContains()) {
@ -1361,7 +1376,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
.setCodeSystemVersion(theCodeSystemVersion) .setCodeSystemVersion(theCodeSystemVersion)
.setDisplay(theExpectedDisplay); .setDisplay(theExpectedDisplay);
if (issueSeverity != null) { if (issueSeverity != null) {
codeValidationResult.setIssues(Collections.singletonList(new CodeValidationIssue( codeValidationResult.setCodeValidationIssues(Collections.singletonList(new CodeValidationIssue(
message, message,
theIssueSeverityForCodeDisplayMismatch, theIssueSeverityForCodeDisplayMismatch,
CodeValidationIssueCode.INVALID, CodeValidationIssueCode.INVALID,

View File

@ -28,7 +28,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Base; import org.hl7.fhir.r4.model.Base;
import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Parameters;
@ -632,7 +631,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
return new CodeValidationResult() return new CodeValidationResult()
.setSeverity(severity) .setSeverity(severity)
.setMessage(theMessage) .setMessage(theMessage)
.addIssue(new CodeValidationIssue( .addCodeValidationIssue(new CodeValidationIssue(
theMessage, severity, theIssueCode, CodeValidationIssueCoding.INVALID_CODE)); theMessage, severity, theIssueCode, CodeValidationIssueCoding.INVALID_CODE));
} }
@ -681,13 +680,13 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
createCodeValidationIssues( createCodeValidationIssues(
(IBaseOperationOutcome) issuesValue.get(), (IBaseOperationOutcome) issuesValue.get(),
fhirContext.getVersion().getVersion()) fhirContext.getVersion().getVersion())
.ifPresent(i -> i.forEach(result::addIssue)); .ifPresent(i -> i.forEach(result::addCodeValidationIssue));
} else { } else {
// create a validation issue out of the message // create a validation issue out of the message
// this is a workaround to overcome an issue in the FHIR Validator library // this is a workaround to overcome an issue in the FHIR Validator library
// where ValueSet bindings are only reading issues but not messages // where ValueSet bindings are only reading issues but not messages
// @see https://github.com/hapifhir/org.hl7.fhir.core/issues/1766 // @see https://github.com/hapifhir/org.hl7.fhir.core/issues/1766
result.addIssue(createCodeValidationIssue(result.getMessage())); result.addCodeValidationIssue(createCodeValidationIssue(result.getMessage()));
} }
return result; return result;
} }
@ -718,42 +717,23 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
private static Collection<CodeValidationIssue> createCodeValidationIssuesR4(OperationOutcome theOperationOutcome) { private static Collection<CodeValidationIssue> createCodeValidationIssuesR4(OperationOutcome theOperationOutcome) {
return theOperationOutcome.getIssue().stream() return theOperationOutcome.getIssue().stream()
.map(issueComponent -> { .map(issueComponent ->
String diagnostics = issueComponent.getDiagnostics(); createCodeValidationIssue(issueComponent.getDetails().getText()))
IssueSeverity issueSeverity =
IssueSeverity.fromCode(issueComponent.getSeverity().toCode());
String issueTypeCode = issueComponent.getCode().toCode();
CodeableConcept details = issueComponent.getDetails();
CodeValidationIssue issue = new CodeValidationIssue(diagnostics, issueSeverity, issueTypeCode);
CodeValidationIssueDetails issueDetails = new CodeValidationIssueDetails(details.getText());
details.getCoding().forEach(coding -> issueDetails.addCoding(coding.getSystem(), coding.getCode()));
issue.setDetails(issueDetails);
return issue;
})
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private static Collection<CodeValidationIssue> createCodeValidationIssuesDstu3( private static Collection<CodeValidationIssue> createCodeValidationIssuesDstu3(
org.hl7.fhir.dstu3.model.OperationOutcome theOperationOutcome) { org.hl7.fhir.dstu3.model.OperationOutcome theOperationOutcome) {
return theOperationOutcome.getIssue().stream() return theOperationOutcome.getIssue().stream()
.map(issueComponent -> { .map(issueComponent ->
String diagnostics = issueComponent.getDiagnostics(); createCodeValidationIssue(issueComponent.getDetails().getText()))
IssueSeverity issueSeverity =
IssueSeverity.fromCode(issueComponent.getSeverity().toCode());
String issueTypeCode = issueComponent.getCode().toCode();
org.hl7.fhir.dstu3.model.CodeableConcept details = issueComponent.getDetails();
CodeValidationIssue issue = new CodeValidationIssue(diagnostics, issueSeverity, issueTypeCode);
CodeValidationIssueDetails issueDetails = new CodeValidationIssueDetails(details.getText());
details.getCoding().forEach(coding -> issueDetails.addCoding(coding.getSystem(), coding.getCode()));
issue.setDetails(issueDetails);
return issue;
})
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private static CodeValidationIssue createCodeValidationIssue(String theMessage) { private static CodeValidationIssue createCodeValidationIssue(String theMessage) {
return new CodeValidationIssue( return new CodeValidationIssue(
theMessage, theMessage,
// assume issue type is OperationOutcome.IssueType#CODEINVALID as it is the only match
IssueSeverity.ERROR, IssueSeverity.ERROR,
CodeValidationIssueCode.INVALID, CodeValidationIssueCode.INVALID,
CodeValidationIssueCoding.INVALID_CODE); CodeValidationIssueCoding.INVALID_CODE);

View File

@ -87,7 +87,7 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup
result.setSeverity(null); result.setSeverity(null);
result.setMessage(null); result.setMessage(null);
} else { } else {
result.addIssue(new CodeValidationIssue( result.addCodeValidationIssue(new CodeValidationIssue(
theMessage, theMessage,
myNonExistentCodeSystemSeverity, myNonExistentCodeSystemSeverity,
CodeValidationIssueCode.NOT_FOUND, CodeValidationIssueCode.NOT_FOUND,

View File

@ -11,17 +11,6 @@ public final class ValidationSupportUtils {
private ValidationSupportUtils() {} private ValidationSupportUtils() {}
/**
* This method extracts a code system that can be (potentially) associated with a code when
* performing validation against a ValueSet. This method was created for internal purposes.
* Please use this method with care because it will only cover some
* use-cases (e.g. standard bindings) while for others it may not return correct results or return null.
* An incorrect result could be considered if the resource declares a code with a system, and you're calling
* this method to check a binding against a ValueSet that has nothing to do with that system.
* @param theValueSet the valueSet
* @param theCode the code
* @return the system which can be associated with the code
*/
public static String extractCodeSystemForCode(IBaseResource theValueSet, String theCode) { public static String extractCodeSystemForCode(IBaseResource theValueSet, String theCode) {
if (theValueSet instanceof org.hl7.fhir.dstu3.model.ValueSet) { if (theValueSet instanceof org.hl7.fhir.dstu3.model.ValueSet) {
return extractCodeSystemForCodeDSTU3((org.hl7.fhir.dstu3.model.ValueSet) theValueSet, theCode); return extractCodeSystemForCodeDSTU3((org.hl7.fhir.dstu3.model.ValueSet) theValueSet, theCode);

View File

@ -62,7 +62,6 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import static ca.uhn.fhir.context.support.IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY;
import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toSet; import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
@ -297,7 +296,7 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
theResult.getCodeSystemVersion(), theResult.getCodeSystemVersion(),
conceptDefinitionComponent, conceptDefinitionComponent,
display, display,
getIssuesForCodeValidation(theResult.getIssues())); getIssuesForCodeValidation(theResult.getCodeValidationIssues()));
} }
if (retVal == null) { if (retVal == null) {
@ -308,36 +307,73 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
} }
private List<OperationOutcome.OperationOutcomeIssueComponent> getIssuesForCodeValidation( private List<OperationOutcome.OperationOutcomeIssueComponent> getIssuesForCodeValidation(
List<IValidationSupport.CodeValidationIssue> theIssues) { List<IValidationSupport.CodeValidationIssue> codeValidationIssues) {
List<OperationOutcome.OperationOutcomeIssueComponent> issueComponents = new ArrayList<>(); List<OperationOutcome.OperationOutcomeIssueComponent> issues = new ArrayList<>();
for (IValidationSupport.CodeValidationIssue issue : theIssues) { for (IValidationSupport.CodeValidationIssue codeValidationIssue : codeValidationIssues) {
OperationOutcome.IssueSeverity severity =
OperationOutcome.IssueSeverity.fromCode(issue.getSeverity().getCode());
OperationOutcome.IssueType issueType =
OperationOutcome.IssueType.fromCode(issue.getType().getCode());
String diagnostics = issue.getDiagnostics();
IValidationSupport.CodeValidationIssueDetails details = issue.getDetails(); CodeableConcept codeableConcept = new CodeableConcept().setText(codeValidationIssue.getMessage());
CodeableConcept codeableConcept = new CodeableConcept().setText(details.getText()); codeableConcept.addCoding(
details.getCodings().forEach(detailCoding -> codeableConcept "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
.addCoding() getIssueCodingFromCodeValidationIssue(codeValidationIssue),
.setSystem(detailCoding.getSystem()) null);
.setCode(detailCoding.getCode()));
OperationOutcome.OperationOutcomeIssueComponent issueComponent = OperationOutcome.OperationOutcomeIssueComponent issue =
new OperationOutcome.OperationOutcomeIssueComponent() new OperationOutcome.OperationOutcomeIssueComponent()
.setSeverity(severity) .setSeverity(getIssueSeverityFromCodeValidationIssue(codeValidationIssue))
.setCode(issueType) .setCode(getIssueTypeFromCodeValidationIssue(codeValidationIssue))
.setDetails(codeableConcept) .setDetails(codeableConcept);
.setDiagnostics(diagnostics); issue.getDetails().setText(codeValidationIssue.getMessage());
issueComponent issue.addExtension()
.addExtension()
.setUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id") .setUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id")
.setValue(new StringType("Terminology_PassThrough_TX_Message")); .setValue(new StringType("Terminology_PassThrough_TX_Message"));
issueComponents.add(issueComponent); issues.add(issue);
} }
return issueComponents; return issues;
}
private String getIssueCodingFromCodeValidationIssue(IValidationSupport.CodeValidationIssue codeValidationIssue) {
switch (codeValidationIssue.getCoding()) {
case VS_INVALID:
return "vs-invalid";
case NOT_FOUND:
return "not-found";
case NOT_IN_VS:
return "not-in-vs";
case INVALID_CODE:
return "invalid-code";
case INVALID_DISPLAY:
return "invalid-display";
}
return null;
}
private OperationOutcome.IssueType getIssueTypeFromCodeValidationIssue(
IValidationSupport.CodeValidationIssue codeValidationIssue) {
switch (codeValidationIssue.getCode()) {
case NOT_FOUND:
return OperationOutcome.IssueType.NOTFOUND;
case CODE_INVALID:
return OperationOutcome.IssueType.CODEINVALID;
case INVALID:
return OperationOutcome.IssueType.INVALID;
}
return null;
}
private OperationOutcome.IssueSeverity getIssueSeverityFromCodeValidationIssue(
IValidationSupport.CodeValidationIssue codeValidationIssue) {
switch (codeValidationIssue.getSeverity()) {
case FATAL:
return OperationOutcome.IssueSeverity.FATAL;
case ERROR:
return OperationOutcome.IssueSeverity.ERROR;
case WARNING:
return OperationOutcome.IssueSeverity.WARNING;
case INFORMATION:
return OperationOutcome.IssueSeverity.INFORMATION;
}
return null;
} }
@Override @Override
@ -815,22 +851,25 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
.getRootValidationSupport() .getRootValidationSupport()
.validateCodeInValueSet( .validateCodeInValueSet(
myValidationSupportContext, theValidationOptions, theSystem, theCode, theDisplay, theValueSet); myValidationSupportContext, theValidationOptions, theSystem, theCode, theDisplay, theValueSet);
if (result != null && theSystem != null) { if (result != null) {
/* We got a value set result, which could be successful, or could contain errors/warnings. The code /* We got a value set result, which could be successful, or could contain errors/warnings. The code
might also be invalid in the code system, so we will check that as well and add those issues might also be invalid in the code system, so we will check that as well and add those issues
to our result. to our result.
*/ */
IValidationSupport.CodeValidationResult codeSystemResult = IValidationSupport.CodeValidationResult codeSystemResult =
validateCodeInCodeSystem(theValidationOptions, theSystem, theCode, theDisplay); validateCodeInCodeSystem(theValidationOptions, theSystem, theCode, theDisplay);
final boolean valueSetResultContainsInvalidDisplay = result.getIssues().stream() final boolean valueSetResultContainsInvalidDisplay = result.getCodeValidationIssues().stream()
.anyMatch(VersionSpecificWorkerContextWrapper::hasInvalidDisplayDetailCode); .anyMatch(codeValidationIssue -> codeValidationIssue.getCoding()
== IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY);
if (codeSystemResult != null) { if (codeSystemResult != null) {
for (IValidationSupport.CodeValidationIssue codeValidationIssue : codeSystemResult.getIssues()) { for (IValidationSupport.CodeValidationIssue codeValidationIssue :
codeSystemResult.getCodeValidationIssues()) {
/* Value set validation should already have checked the display name. If we get INVALID_DISPLAY /* Value set validation should already have checked the display name. If we get INVALID_DISPLAY
issues from code system validation, they will only repeat what was already caught. issues from code system validation, they will only repeat what was already caught.
*/ */
if (!hasInvalidDisplayDetailCode(codeValidationIssue) || !valueSetResultContainsInvalidDisplay) { if (codeValidationIssue.getCoding() != IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY
result.addIssue(codeValidationIssue); || !valueSetResultContainsInvalidDisplay) {
result.addCodeValidationIssue(codeValidationIssue);
} }
} }
} }
@ -838,10 +877,6 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
return result; return result;
} }
private static boolean hasInvalidDisplayDetailCode(IValidationSupport.CodeValidationIssue theIssue) {
return theIssue.hasIssueDetailCode(INVALID_DISPLAY.getCode());
}
private IValidationSupport.CodeValidationResult validateCodeInCodeSystem( private IValidationSupport.CodeValidationResult validateCodeInCodeSystem(
ConceptValidationOptions theValidationOptions, String theSystem, String theCode, String theDisplay) { ConceptValidationOptions theValidationOptions, String theSystem, String theCode, String theDisplay) {
return myValidationSupportContext return myValidationSupportContext

View File

@ -9,7 +9,6 @@ import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
import ca.uhn.fhir.context.support.IValidationSupport.StringConceptProperty; import ca.uhn.fhir.context.support.IValidationSupport.StringConceptProperty;
import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -22,12 +21,12 @@ import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_GROUP;
import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_STRING; import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_STRING;
import static java.util.stream.IntStream.range; import static java.util.stream.IntStream.range;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_NAME; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_NAME;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_VERSION; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_VERSION;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.DISPLAY; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.DISPLAY;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.LANGUAGE; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.LANGUAGE;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.createConceptProperty; import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.createConceptProperty;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
@ -190,6 +189,8 @@ public interface ILookupCodeTest {
// verify // verify
assertNotNull(outcome); assertNotNull(outcome);
assertEquals(theRequest.getCode(), getLookupCodeProvider().getCode());
assertEquals(theRequest.getSystem(), getLookupCodeProvider().getSystem());
assertEquals(theExpectedResult.isFound(), outcome.isFound()); assertEquals(theExpectedResult.isFound(), outcome.isFound());
assertEquals(theExpectedResult.getErrorMessage(), outcome.getErrorMessage()); assertEquals(theExpectedResult.getErrorMessage(), outcome.getErrorMessage());
assertEquals(theExpectedResult.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName()); assertEquals(theExpectedResult.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());

View File

@ -2,7 +2,6 @@ package org.hl7.fhir.common.hapi.validation;
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult; import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View File

@ -1,76 +1,17 @@
package org.hl7.fhir.common.hapi.validation; package org.hl7.fhir.common.hapi.validation;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.junit.jupiter.api.Test;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.createCodeValidationIssues;
public interface IRemoteTerminologyValidateCodeTest extends IValidateCodeTest { public interface IRemoteTerminologyValidateCodeTest extends IValidateCodeTest {
default List<IValidationSupport.CodeValidationIssue> getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome) { default List<IValidationSupport.CodeValidationIssue> getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome) {
// this method should be removed once support for issues is fully implemented across all validator types // this method should be removed once support for issues is fully implemented across all validator types
Optional<Collection<IValidationSupport.CodeValidationIssue>> issues = RemoteTerminologyServiceValidationSupport.createCodeValidationIssues(theOperationOutcome, getService().getFhirContext().getVersion().getVersion()); Optional<Collection<IValidationSupport.CodeValidationIssue>> issues = RemoteTerminologyServiceValidationSupport.createCodeValidationIssues(theOperationOutcome, getService().getFhirContext().getVersion().getVersion());
return issues.map(theCodeValidationIssues -> theCodeValidationIssues.stream().toList()).orElseGet(List::of); return issues.map(theCodeValidationIssues -> theCodeValidationIssues.stream().toList()).orElseGet(List::of);
} }
@Test
default void createCodeValidationIssues_withCodeSystemOutcomeForInvalidCode_returnsAsExpected() {
IBaseOperationOutcome outcome = getCodeSystemInvalidCodeOutcome();
FhirVersionEnum versionEnum = getService().getFhirContext().getVersion().getVersion();
Optional<Collection<IValidationSupport.CodeValidationIssue>> issuesOptional = createCodeValidationIssues(outcome, versionEnum);
assertThat(issuesOptional).isPresent();
assertThat(issuesOptional.get()).hasSize(1);
IValidationSupport.CodeValidationIssue issue = issuesOptional.get().iterator().next();
assertThat(issue.getType().getCode()).isEqualTo("code-invalid");
assertThat(issue.getSeverity().getCode()).isEqualTo("error");
assertThat(issue.getDetails().getCodings()).hasSize(1);
IValidationSupport.CodeValidationIssueCoding issueCoding = issue.getDetails().getCodings().get(0);
assertThat(issueCoding.getSystem()).isEqualTo("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type");
assertThat(issueCoding.getCode()).isEqualTo("invalid-code");
assertThat(issue.getDetails().getText()).isEqualTo("Unknown code 'CODE' in the CodeSystem 'http://code.system/url' version '1.0.0'");
assertThat(issue.getDiagnostics()).isNull();
}
@Test
default void createCodeValidationIssues_withValueSetOutcomeForInvalidCode_returnsAsExpected() {
IBaseOperationOutcome outcome = getValueSetInvalidCodeOutcome();
FhirVersionEnum versionEnum = getService().getFhirContext().getVersion().getVersion();
Optional<Collection<IValidationSupport.CodeValidationIssue>> issuesOptional = createCodeValidationIssues(outcome, versionEnum);
assertThat(issuesOptional).isPresent();
assertThat(issuesOptional.get()).hasSize(2);
IValidationSupport.CodeValidationIssue issue = issuesOptional.get().iterator().next();
assertThat(issue.getType().getCode()).isEqualTo("code-invalid");
assertThat(issue.getSeverity().getCode()).isEqualTo("error");
assertThat(issue.getDetails().getCodings()).hasSize(1);
IValidationSupport.CodeValidationIssueCoding issueCoding = issue.getDetails().getCodings().get(0);
assertThat(issueCoding.getSystem()).isEqualTo("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type");
assertThat(issueCoding.getCode()).isEqualTo("not-in-vs");
assertThat(issue.getDetails().getText()).isEqualTo("The provided code 'http://code.system/url#CODE' was not found in the value set 'http://value.set/url%7C1.0.0'");
assertThat(issue.getDiagnostics()).isNull();
}
@Test
default void createCodeValidationIssues_withValueSetOutcomeWithCustomDetailCode_returnsAsExpected() {
IBaseOperationOutcome outcome = getValueSetCustomDetailCodeOutcome();
FhirVersionEnum versionEnum = getService().getFhirContext().getVersion().getVersion();
Optional<Collection<IValidationSupport.CodeValidationIssue>> issuesOptional = createCodeValidationIssues(outcome, versionEnum);
assertThat(issuesOptional).isPresent();
assertThat(issuesOptional.get()).hasSize(1);
IValidationSupport.CodeValidationIssue issue = issuesOptional.get().iterator().next();
assertThat(issue.getType().getCode()).isEqualTo("processing");
assertThat(issue.getSeverity().getCode()).isEqualTo("information");
assertThat(issue.getDetails().getCodings()).hasSize(1);
IValidationSupport.CodeValidationIssueCoding issueCoding = issue.getDetails().getCodings().get(0);
assertThat(issueCoding.getSystem()).isEqualTo("http://example.com/custom-issue-type");
assertThat(issueCoding.getCode()).isEqualTo("valueset-is-draft");
assertThat(issue.getDetails().getText()).isNull();
assertThat(issue.getDiagnostics()).isEqualTo("The ValueSet status is marked as draft.");
}
} }

View File

@ -4,9 +4,6 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
import ca.uhn.fhir.util.ClasspathUtil;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -19,13 +16,12 @@ import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
import static ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity.ERROR; import static ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity.ERROR;
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_VERSION;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_VERSION; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.DISPLAY;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.DISPLAY; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.ERROR_MESSAGE;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.ERROR_MESSAGE; import static org.hl7.fhir.common.hapi.validation.IValidationProviders.VALUE_SET_URL;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.VALUE_SET_URL;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -35,35 +31,21 @@ import static org.junit.jupiter.api.Assertions.fail;
public interface IValidateCodeTest { public interface IValidateCodeTest {
IValidationProviders.IMyValidationProvider getCodeSystemProvider(); IValidationProviders.IMyCodeSystemProvider getCodeSystemProvider();
IValidationProviders.IMyValidationProvider getValueSetProvider(); IValidationProviders.IMyValueSetProvider getValueSetProvider();
IValidationSupport getService(); IValidationSupport getService();
IBaseParameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource); IBaseParameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource);
String getCodeSystemError(); String getCodeSystemError();
String getValueSetError(); String getValueSetError();
IBaseOperationOutcome getCodeSystemInvalidCodeOutcome(); IBaseOperationOutcome getCodeSystemInvalidCodeOutcome();
IBaseOperationOutcome getValueSetInvalidCodeOutcome(); IBaseOperationOutcome getValueSetInvalidCodeOutcome();
IBaseOperationOutcome getValueSetCustomDetailCodeOutcome();
default IBaseOperationOutcome getCodeSystemInvalidCodeOutcome(Class<? extends IBaseOperationOutcome> theResourceClass) {
return getOutcome(theResourceClass, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
}
default IBaseOperationOutcome getValueSetInvalidCodeOutcome(Class<? extends IBaseOperationOutcome> theResourceClass) {
return getOutcome(theResourceClass, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
}
default IBaseOperationOutcome getValueSetCustomDetailCodeOutcome(Class<? extends IBaseOperationOutcome> theResourceClass) {
return getOutcome(theResourceClass, "/terminology/OperationOutcome-ValueSet-custom-issue-detail.json");
}
default IBaseOperationOutcome getOutcome(Class<? extends IBaseOperationOutcome> theResourceClass, String theFile) {
return ClasspathUtil.loadResource(getService().getFhirContext(), theResourceClass, theFile);
}
default void createCodeSystemReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) { default void createCodeSystemReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
getCodeSystemProvider().addTerminologyResponse(OPERATION_VALIDATE_CODE, CODE_SYSTEM, CODE, createParameters(theResult, theDisplay, theMessage, theIssuesResource)); getCodeSystemProvider().setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
} }
default void createValueSetReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) { default void createValueSetReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
getValueSetProvider().addTerminologyResponse(OPERATION_VALIDATE_CODE, VALUE_SET_URL, CODE, createParameters(theResult, theDisplay, theMessage, theIssuesResource)); getValueSetProvider().setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
} }
@Test @Test
@ -109,8 +91,8 @@ public interface IValidateCodeTest {
String theValidationMessage, String theValidationMessage,
String theCodeSystem, String theCodeSystem,
String theValueSetUrl) { String theValueSetUrl) {
getCodeSystemProvider().addException(OPERATION_VALIDATE_CODE, theCodeSystem, CODE, theException); getCodeSystemProvider().setException(theException);
getValueSetProvider().addException(OPERATION_VALIDATE_CODE, theValueSetUrl, CODE, theException); getValueSetProvider().setException(theException);
CodeValidationResult outcome = getService().validateCode(null, null, theCodeSystem, CODE, DISPLAY, theValueSetUrl); CodeValidationResult outcome = getService().validateCode(null, null, theCodeSystem, CODE, DISPLAY, theValueSetUrl);
verifyErrorResultFromException(outcome, theValidationMessage, theServerMessage); verifyErrorResultFromException(outcome, theValidationMessage, theServerMessage);
@ -123,7 +105,7 @@ public interface IValidateCodeTest {
for (String message : theMessages) { for (String message : theMessages) {
assertTrue(outcome.getMessage().contains(message)); assertTrue(outcome.getMessage().contains(message));
} }
assertFalse(outcome.getIssues().isEmpty()); assertFalse(outcome.getCodeValidationIssues().isEmpty());
} }
@Test @Test
@ -148,7 +130,11 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay()); assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity()); assertNull(outcome.getSeverity());
assertNull(outcome.getMessage()); assertNull(outcome.getMessage());
assertTrue(outcome.getIssues().isEmpty()); assertTrue(outcome.getCodeValidationIssues().isEmpty());
assertEquals(CODE, getValueSetProvider().getCode());
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
} }
@Test @Test
@ -161,7 +147,9 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay()); assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity()); assertNull(outcome.getSeverity());
assertNull(outcome.getMessage()); assertNull(outcome.getMessage());
assertTrue(outcome.getIssues().isEmpty()); assertTrue(outcome.getCodeValidationIssues().isEmpty());
assertEquals(CODE, getCodeSystemProvider().getCode());
} }
@Test @Test
@ -177,7 +165,10 @@ public interface IValidateCodeTest {
assertNull(outcome.getDisplay()); assertNull(outcome.getDisplay());
assertNull(outcome.getSeverity()); assertNull(outcome.getSeverity());
assertNull(outcome.getMessage()); assertNull(outcome.getMessage());
assertTrue(outcome.getIssues().isEmpty()); assertTrue(outcome.getCodeValidationIssues().isEmpty());
assertEquals(CODE, getCodeSystemProvider().getCode());
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
} }
@Test @Test
@ -193,11 +184,15 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay()); assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity()); assertNull(outcome.getSeverity());
assertNull(outcome.getMessage()); assertNull(outcome.getMessage());
assertTrue(outcome.getIssues().isEmpty()); assertTrue(outcome.getCodeValidationIssues().isEmpty());
assertEquals(CODE, getCodeSystemProvider().getCode());
assertEquals(DISPLAY, getCodeSystemProvider().getDisplay());
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
} }
@Test @Test
default void validateCode_withCodeSystemErrorWithDiagnosticsWithIssues_returnsCorrectly() { default void validateCode_withCodeSystemError_returnsCorrectly() {
IBaseOperationOutcome invalidCodeOutcome = getCodeSystemInvalidCodeOutcome(); IBaseOperationOutcome invalidCodeOutcome = getCodeSystemInvalidCodeOutcome();
createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, invalidCodeOutcome); createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, invalidCodeOutcome);
@ -209,12 +204,12 @@ public interface IValidateCodeTest {
// assertEquals(CODE, outcome.getCode()); // assertEquals(CODE, outcome.getCode());
assertEquals(ERROR, outcome.getSeverity()); assertEquals(ERROR, outcome.getSeverity());
assertEquals(getCodeSystemError(), outcome.getMessage()); assertEquals(getCodeSystemError(), outcome.getMessage());
assertFalse(outcome.getIssues().isEmpty()); assertFalse(outcome.getCodeValidationIssues().isEmpty());
verifyIssues(invalidCodeOutcome, outcome); verifyIssues(invalidCodeOutcome, outcome);
} }
@Test @Test
default void validateCode_withCodeSystemErrorWithDiagnosticsWithoutIssues_returnsCorrectly() { default void validateCode_withCodeSystemErrorAndIssues_returnsCorrectly() {
createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, null); createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, null);
CodeValidationResult outcome = getService() CodeValidationResult outcome = getService()
@ -228,32 +223,10 @@ public interface IValidateCodeTest {
assertNull(outcome.getDisplay()); assertNull(outcome.getDisplay());
assertEquals(ERROR, outcome.getSeverity()); assertEquals(ERROR, outcome.getSeverity());
assertEquals(expectedError, outcome.getMessage()); assertEquals(expectedError, outcome.getMessage());
assertFalse(outcome.getIssues().isEmpty()); assertFalse(outcome.getCodeValidationIssues().isEmpty());
assertEquals(1, outcome.getIssues().size()); assertEquals(1, outcome.getCodeValidationIssues().size());
assertEquals(expectedError, outcome.getIssues().get(0).getDiagnostics()); assertEquals(expectedError, outcome.getCodeValidationIssues().get(0).getMessage());
assertEquals(ERROR, outcome.getIssues().get(0).getSeverity()); assertEquals(ERROR, outcome.getCodeValidationIssues().get(0).getSeverity());
}
@Test
default void validateCode_withCodeSystemErrorWithoutDiagnosticsWithIssues_returnsCorrectly() {
IBaseOperationOutcome invalidCodeOutcome = getCodeSystemInvalidCodeOutcome();
createCodeSystemReturnParameters(false, null, null, invalidCodeOutcome);
CodeValidationResult outcome = getService()
.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
String expectedError = getCodeSystemError();
assertNotNull(outcome);
assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
// assertEquals(CODE, outcome.getCode());
assertNull(outcome.getDisplay());
assertEquals(ERROR, outcome.getSeverity());
assertNull(outcome.getMessage());
assertFalse(outcome.getIssues().isEmpty());
assertEquals(1, outcome.getIssues().size());
assertNull(outcome.getIssues().get(0).getDiagnostics());
assertEquals(ERROR, outcome.getIssues().get(0).getSeverity());
} }
@Test @Test
@ -269,7 +242,10 @@ public interface IValidateCodeTest {
assertNull(outcome.getDisplay()); assertNull(outcome.getDisplay());
assertNull(outcome.getSeverity()); assertNull(outcome.getSeverity());
assertNull(outcome.getMessage()); assertNull(outcome.getMessage());
assertTrue(outcome.getIssues().isEmpty()); assertTrue(outcome.getCodeValidationIssues().isEmpty());
assertEquals(CODE, getValueSetProvider().getCode());
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
} }
@Test @Test
@ -285,7 +261,11 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay()); assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity()); assertNull(outcome.getSeverity());
assertNull(outcome.getMessage()); assertNull(outcome.getMessage());
assertTrue(outcome.getIssues().isEmpty()); assertTrue(outcome.getCodeValidationIssues().isEmpty());
assertEquals(CODE, getValueSetProvider().getCode());
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
} }
@Test @Test
@ -303,9 +283,13 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay()); assertEquals(DISPLAY, outcome.getDisplay());
assertEquals(ERROR, outcome.getSeverity()); assertEquals(ERROR, outcome.getSeverity());
assertEquals(expectedError, outcome.getMessage()); assertEquals(expectedError, outcome.getMessage());
assertEquals(1, outcome.getIssues().size()); assertEquals(1, outcome.getCodeValidationIssues().size());
assertEquals(expectedError, outcome.getIssues().get(0).getDiagnostics()); assertEquals(expectedError, outcome.getCodeValidationIssues().get(0).getMessage());
assertEquals(ERROR, outcome.getIssues().get(0).getSeverity()); assertEquals(ERROR, outcome.getCodeValidationIssues().get(0).getSeverity());
assertEquals(CODE, getValueSetProvider().getCode());
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
} }
@Test @Test
@ -322,28 +306,24 @@ public interface IValidateCodeTest {
assertEquals(DISPLAY, outcome.getDisplay()); assertEquals(DISPLAY, outcome.getDisplay());
assertEquals(ERROR, outcome.getSeverity()); assertEquals(ERROR, outcome.getSeverity());
assertEquals(getValueSetError(), outcome.getMessage()); assertEquals(getValueSetError(), outcome.getMessage());
assertFalse(outcome.getIssues().isEmpty()); assertFalse(outcome.getCodeValidationIssues().isEmpty());
verifyIssues(invalidCodeOutcome, outcome); verifyIssues(invalidCodeOutcome, outcome);
assertEquals(CODE, getValueSetProvider().getCode());
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
} }
default void verifyIssues(IBaseOperationOutcome theOperationOutcome, CodeValidationResult theResult) { default void verifyIssues(IBaseOperationOutcome theOperationOutcome, CodeValidationResult theResult) {
List<IValidationSupport.CodeValidationIssue> issues = getCodeValidationIssues(theOperationOutcome); List<IValidationSupport.CodeValidationIssue> issues = getCodeValidationIssues(theOperationOutcome);
assertEquals(issues.size(), theResult.getIssues().size()); assertEquals(issues.size(), theResult.getCodeValidationIssues().size());
for (int i = 0; i < issues.size(); i++) { for (int i = 0; i < issues.size(); i++) {
IValidationSupport.CodeValidationIssue expectedIssue = issues.get(i); IValidationSupport.CodeValidationIssue expectedIssue = issues.get(i);
IValidationSupport.CodeValidationIssue actualIssue = theResult.getIssues().get(i); IValidationSupport.CodeValidationIssue actualIssue = theResult.getCodeValidationIssues().get(i);
assertEquals(expectedIssue.getType().getCode(), actualIssue.getType().getCode()); assertEquals(expectedIssue.getCode(), actualIssue.getCode());
assertEquals(expectedIssue.getSeverity(), actualIssue.getSeverity()); assertEquals(expectedIssue.getSeverity(), actualIssue.getSeverity());
assertEquals(expectedIssue.getDetails().getText(), actualIssue.getDetails().getText()); assertEquals(expectedIssue.getCoding(), actualIssue.getCoding());
assertEquals(expectedIssue.getDetails().getCodings().size(), actualIssue.getDetails().getCodings().size()); assertEquals(expectedIssue.getMessage(), actualIssue.getMessage());
for (int index = 0; index < expectedIssue.getDetails().getCodings().size(); index++) {
IValidationSupport.CodeValidationIssueCoding expectedCoding = expectedIssue.getDetails().getCodings().get(index);
IValidationSupport.CodeValidationIssueCoding actualCoding = actualIssue.getDetails().getCodings().get(index);
assertEquals(expectedCoding.getSystem(), actualCoding.getSystem());
assertEquals(expectedCoding.getCode(), actualCoding.getCode());
}
assertEquals(expectedIssue.getDetails().getText(), actualIssue.getDetails().getText());
assertEquals(expectedIssue.getDiagnostics(), actualIssue.getDiagnostics());
} }
} }

View File

@ -0,0 +1,39 @@
package org.hl7.fhir.common.hapi.validation;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.rest.server.IResourceProvider;
import org.hl7.fhir.instance.model.api.IBaseParameters;
public interface IValidationProviders {
String CODE_SYSTEM = "http://code.system/url";
String CODE_SYSTEM_VERSION = "1.0.0";
String CODE_SYSTEM_NAME = "Test Code System";
String CODE = "CODE";
String VALUE_SET_URL = "http://value.set/url";
String DISPLAY = "Explanation for code TestCode.";
String LANGUAGE = "en";
String ERROR_MESSAGE = "This is an error message";
interface IMyCodeSystemProvider extends IResourceProvider {
String getCode();
String getSystem();
String getDisplay();
void setException(Exception theException);
void setReturnParams(IBaseParameters theParameters);
}
interface IMyLookupCodeProvider extends IResourceProvider {
String getCode();
String getSystem();
void setLookupCodeResult(IValidationSupport.LookupCodeResult theLookupCodeResult);
}
interface IMyValueSetProvider extends IResourceProvider {
String getCode();
String getSystem();
String getDisplay();
String getValueSet();
void setException(Exception theException);
void setReturnParams(IBaseParameters theParameters);
}
}

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks; import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
import ca.uhn.fhir.i18n.HapiLocalizer; import ca.uhn.fhir.i18n.HapiLocalizer;
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
import org.hl7.fhir.r5.model.Resource; 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.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
@ -15,18 +16,17 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.quality.Strictness; import org.mockito.quality.Strictness;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings; import static org.mockito.Mockito.withSettings;
import java.util.List;
public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestWithInlineMocks { public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestWithInlineMocks {
final byte[] EXPECTED_BINARY_CONTENT_1 = "dummyBinaryContent1".getBytes(); final byte[] EXPECTED_BINARY_CONTENT_1 = "dummyBinaryContent1".getBytes();
@ -80,7 +80,7 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
} }
@Test @Test
public void validateCode_codeInValueSet_resolvesCodeSystemFromValueSet() { public void validateCode_normally_resolvesCodeSystemFromValueSet() {
// setup // setup
IValidationSupport validationSupport = mockValidationSupport(); IValidationSupport validationSupport = mockValidationSupport();
ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport); ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
@ -90,7 +90,8 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
ValueSet valueSet = new ValueSet(); ValueSet valueSet = new ValueSet();
valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system").addConcept().setCode("code0"); valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system").addConcept().setCode("code0");
valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2"); valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
when(validationSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenReturn(mock(IValidationSupport.CodeValidationResult.class)); when(validationSupport.fetchResource(eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(valueSet);
when(validationSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenReturn(new IValidationSupport.CodeValidationResult());
// execute // execute
wrapper.validateCode(new ValidationOptions(), "code0", valueSet); wrapper.validateCode(new ValidationOptions(), "code0", valueSet);
@ -100,26 +101,6 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
verify(validationSupport, times(1)).validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), any()); verify(validationSupport, times(1)).validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), any());
} }
@Test
public void validateCode_codeNotInValueSet_doesNotResolveSystem() {
// setup
IValidationSupport validationSupport = mockValidationSupport();
ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
VersionCanonicalizer versionCanonicalizer = new VersionCanonicalizer(FhirContext.forR5Cached());
VersionSpecificWorkerContextWrapper wrapper = new VersionSpecificWorkerContextWrapper(mockContext, versionCanonicalizer);
ValueSet valueSet = new ValueSet();
valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system").addConcept().setCode("code0");
valueSet.getCompose().addInclude().setSystem("http://codesystems.com/system2").addConcept().setCode("code2");
// execute
wrapper.validateCode(new ValidationOptions(), "code1", valueSet);
// verify
verify(validationSupport, times(1)).validateCodeInValueSet(any(), any(), eq(null), eq("code1"), any(), any());
verify(validationSupport, never()).validateCode(any(), any(), any(), any(), any(), any());
}
@Test @Test
public void isPrimitive_primitive() { public void isPrimitive_primitive() {
// setup // setup

View File

@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks; import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt; import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Parameters;
@ -27,7 +28,10 @@ import org.hl7.fhir.dstu2.model.Observation.ObservationStatus;
import org.hl7.fhir.dstu2.model.QuestionnaireResponse; import org.hl7.fhir.dstu2.model.QuestionnaireResponse;
import org.hl7.fhir.dstu2.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.hl7.fhir.dstu2.model.QuestionnaireResponse.QuestionnaireResponseStatus;
import org.hl7.fhir.dstu2.model.StringType; import org.hl7.fhir.dstu2.model.StringType;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -37,7 +41,9 @@ import org.mockito.stubbing.Answer;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -94,7 +100,7 @@ public class FhirInstanceValidatorDstu2Test extends BaseValidationTestWithInline
if (myValidConcepts.contains(system + "___" + code)) { if (myValidConcepts.contains(system + "___" + code)) {
retVal = new IValidationSupport.CodeValidationResult().setCode(code); retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) { } else if (myValidSystems.contains(system)) {
return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage("Unknown code"); return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code");
} else { } else {
retVal = null; retVal = null;
} }

View File

@ -58,6 +58,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
@ -228,10 +229,10 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
retVal = new IValidationSupport.CodeValidationResult().setCode(code); retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) { } else if (myValidSystems.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')"; final String message = "Unknown code (for '" + system + "#" + code + "')";
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message).setIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))); retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
} else if (myValidSystemsNotReturningIssues.contains(system)) { } else if (myValidSystemsNotReturningIssues.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')"; final String message = "Unknown code (for '" + system + "#" + code + "')";
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message); retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
} else if (myCodeSystems.containsKey(system)) { } else if (myCodeSystems.containsKey(system)) {
CodeSystem cs = myCodeSystems.get(system); CodeSystem cs = myCodeSystems.get(system);
Optional<ConceptDefinitionComponent> found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst(); Optional<ConceptDefinitionComponent> found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst();

View File

@ -0,0 +1,159 @@
package org.hl7.fhir.dstu3.hapi.validation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import jakarta.servlet.http.HttpServletRequest;
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.List;
public interface IValidateCodeProvidersDstu3 {
@SuppressWarnings("unused")
class MyCodeSystemProviderDstu3 implements IValidationProviders.IMyCodeSystemProvider {
private UriType mySystemUrl;
private CodeType myCode;
private StringType myDisplay;
private Exception myException;
private Parameters myReturnParams;
@Operation(name = "validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = org.hl7.fhir.dstu3.model.BooleanType.class, min = 1),
@OperationParam(name = "message", type = org.hl7.fhir.dstu3.model.StringType.class),
@OperationParam(name = "display", type = org.hl7.fhir.dstu3.model.StringType.class)
})
public org.hl7.fhir.dstu3.model.Parameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) org.hl7.fhir.dstu3.model.IdType theId,
@OperationParam(name = "url", min = 0, max = 1) org.hl7.fhir.dstu3.model.UriType theCodeSystemUrl,
@OperationParam(name = "code", min = 0, max = 1) org.hl7.fhir.dstu3.model.CodeType theCode,
@OperationParam(name = "display", min = 0, max = 1) org.hl7.fhir.dstu3.model.StringType theDisplay
) throws Exception {
mySystemUrl = theCodeSystemUrl;
myCode = theCode;
myDisplay = theDisplay;
if (myException != null) {
throw myException;
}
return myReturnParams;
}
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name = "name", type = org.hl7.fhir.dstu3.model.StringType.class, min = 1),
@OperationParam(name = "version", type = org.hl7.fhir.dstu3.model.StringType.class),
@OperationParam(name = "display", type = org.hl7.fhir.dstu3.model.StringType.class, min = 1),
@OperationParam(name = "abstract", type = org.hl7.fhir.dstu3.model.BooleanType.class, min = 1),
@OperationParam(name = "property", type = org.hl7.fhir.dstu3.model.StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
})
public IBaseParameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name = "code", max = 1) org.hl7.fhir.dstu3.model.CodeType theCode,
@OperationParam(name = "system",max = 1) org.hl7.fhir.dstu3.model.UriType theSystem,
@OperationParam(name = "coding", max = 1) Coding theCoding,
@OperationParam(name = "version", max = 1) org.hl7.fhir.dstu3.model.StringType theVersion,
@OperationParam(name = "displayLanguage", max = 1) org.hl7.fhir.dstu3.model.CodeType theDisplayLanguage,
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<org.hl7.fhir.dstu3.model.CodeType> thePropertyNames,
RequestDetails theRequestDetails
) {
myCode = theCode;
return myReturnParams;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
public void setException(Exception theException) {
myException = theException;
}
@Override
public void setReturnParams(IBaseParameters theParameters) {
myReturnParams = (Parameters) theParameters;
}
@Override
public String getCode() {
return myCode != null ? myCode.getValueAsString() : null;
}
@Override
public String getSystem() {
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
}
public String getDisplay() {
return myDisplay != null ? myDisplay.getValue() : null;
}
}
@SuppressWarnings("unused")
class MyValueSetProviderDstu3 implements IValidationProviders.IMyValueSetProvider {
private Exception myException;
private Parameters myReturnParams;
private UriType mySystemUrl;
private UriType myValueSetUrl;
private CodeType myCode;
private StringType myDisplay;
@Operation(name = "validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = org.hl7.fhir.dstu3.model.StringType.class),
@OperationParam(name = "display", type = org.hl7.fhir.dstu3.model.StringType.class)
})
public Parameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) org.hl7.fhir.dstu3.model.UriType theValueSetUrl,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
@OperationParam(name = "valueSet") org.hl7.fhir.dstu3.model.ValueSet theValueSet
) throws Exception {
mySystemUrl = theSystem;
myValueSetUrl = theValueSetUrl;
myCode = theCode;
myDisplay = theDisplay;
if (myException != null) {
throw myException;
}
return myReturnParams;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return ValueSet.class;
}
public void setException(Exception theException) {
myException = theException;
}
@Override
public void setReturnParams(IBaseParameters theParameters) {
myReturnParams = (Parameters) theParameters;
}
@Override
public String getCode() {
return myCode != null ? myCode.getValueAsString() : null;
}
@Override
public String getSystem() {
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
}
@Override
public String getValueSet() {
return myValueSetUrl != null ? myValueSetUrl.getValueAsString() : null;
}
public String getDisplay() {
return myDisplay != null ? myDisplay.getValue() : null;
}
}
}

View File

@ -41,6 +41,7 @@ import org.hl7.fhir.dstu3.model.Type;
import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -55,8 +56,6 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity.ERROR;
import static ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity.WARNING;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.BOOLEAN; import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.BOOLEAN;
import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.CHOICE; import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.CHOICE;
@ -225,7 +224,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(ValueSet.class))) when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(ValueSet.class)))
.thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0"));
when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(ValueSet.class))) when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(ValueSet.class)))
.thenReturn(new IValidationSupport.CodeValidationResult().setSeverity(ERROR).setMessage("Unknown code")); .thenReturn(new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"));
CodeSystem codeSystem = new CodeSystem(); CodeSystem codeSystem = new CodeSystem();
codeSystem.setContent(CodeSystemContentMode.COMPLETE); codeSystem.setContent(CodeSystemContentMode.COMPLETE);
@ -247,7 +246,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class))) when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class)))
.thenReturn(new IValidationSupport.CodeValidationResult().setCode(CODE_ICC_SCHOOLTYPE_PT)); .thenReturn(new IValidationSupport.CodeValidationResult().setCode(CODE_ICC_SCHOOLTYPE_PT));
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class))) when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class)))
.thenReturn(new IValidationSupport.CodeValidationResult().setSeverity(WARNING).setMessage("Unknown code: http://codesystems.com/system / code1")); .thenReturn(new IValidationSupport.CodeValidationResult().setSeverityCode("warning").setMessage("Unknown code: http://codesystems.com/system / code1"));
QuestionnaireResponse qa; QuestionnaireResponse qa;
@ -1035,7 +1034,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class))) when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class)))
.thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0"));
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class))) when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class)))
.thenReturn(new IValidationSupport.CodeValidationResult().setSeverity(ERROR).setMessage("Unknown code")); .thenReturn(new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"));
CodeSystem codeSystem = new CodeSystem(); CodeSystem codeSystem = new CodeSystem();
codeSystem.setContent(CodeSystemContentMode.COMPLETE); codeSystem.setContent(CodeSystemContentMode.COMPLETE);

View File

@ -12,9 +12,9 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest; import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest;
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.dstu3.model.BooleanType; import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeSystem;
@ -164,6 +164,8 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
@SuppressWarnings("unused") @SuppressWarnings("unused")
static class MyLookupCodeProviderDstu3 implements IValidationProviders.IMyLookupCodeProvider { static class MyLookupCodeProviderDstu3 implements IValidationProviders.IMyLookupCodeProvider {
private UriType mySystemUrl;
private CodeType myCode;
private LookupCodeResult myLookupCodeResult; private LookupCodeResult myLookupCodeResult;
@Override @Override
@ -188,6 +190,8 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
@OperationParam(name= " property", max = OperationParam.MAX_UNLIMITED) List<StringType> thePropertyNames, @OperationParam(name= " property", max = OperationParam.MAX_UNLIMITED) List<StringType> thePropertyNames,
RequestDetails theRequestDetails RequestDetails theRequestDetails
) { ) {
myCode = theCode;
mySystemUrl = theSystem;
if (theSystem == null) { if (theSystem == null) {
throw new InvalidRequestException(MessageFormat.format(MESSAGE_RESPONSE_INVALID, theCode)); throw new InvalidRequestException(MessageFormat.format(MESSAGE_RESPONSE_INVALID, theCode));
} }
@ -201,5 +205,15 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
public Class<? extends IBaseResource> getResourceType() { public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class; return CodeSystem.class;
} }
@Override
public String getCode() {
return myCode != null ? myCode.getValueAsString() : null;
}
@Override
public String getSystem() {
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
}
} }
} }

View File

@ -5,10 +5,13 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersDstu3; import ca.uhn.fhir.util.ClasspathUtil;
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -16,15 +19,13 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import java.util.List; import java.util.List;
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_LOOKUP;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test { public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
private IValidationProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider; private IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
@RegisterExtension @RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx); public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
@ -35,7 +36,7 @@ public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort(); String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl); mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true)); mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
myCodeSystemProvider = new IValidationProvidersDstu3.MyCodeSystemProviderDstu3(); myCodeSystemProvider = new IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider); ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider);
} }
@ -46,10 +47,13 @@ public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
} }
@Test @Test
void lookupCode_withParametersOutput_convertsCorrectly() { void lookupCode_withParametersOutput_convertsCorrectly() {
String outputFile ="/terminology/CodeSystem-lookup-output-with-subproperties.json"; String paramsAsString = ClasspathUtil.loadResource("/terminology/CodeSystem-lookup-output-with-subproperties.json");
IBaseParameters resultParameters = myCodeSystemProvider.addTerminologyResponse(OPERATION_LOOKUP, CODE_SYSTEM, CODE, ourCtx, outputFile); IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
assertTrue(baseResource instanceof Parameters);
Parameters resultParameters = (Parameters) baseResource;
myCodeSystemProvider.setReturnParams(resultParameters);
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces")); LookupCodeRequest request = new LookupCodeRequest(IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, null, List.of("interfaces"));
// test // test
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request); IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);

View File

@ -4,18 +4,16 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.test.utilities.validation.IValidationProviders; import ca.uhn.fhir.util.ClasspathUtil;
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersDstu3;
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyValidateCodeTest; import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyValidateCodeTest;
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.dstu3.model.BooleanType; import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Resource; import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -24,11 +22,6 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import java.util.List; import java.util.List;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_VERSION;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.ERROR_MESSAGE;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.VALUE_SET_URL;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM; import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET; import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET;
@ -45,8 +38,8 @@ public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminolog
private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
@RegisterExtension @RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx); public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private IValidationProviders.MyValidationProvider<CodeSystem> myCodeSystemProvider; private IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
private IValidationProviders.MyValidationProvider<ValueSet> myValueSetProvider; private IValidateCodeProvidersDstu3.MyValueSetProviderDstu3 myValueSetProvider;
private RemoteTerminologyServiceValidationSupport mySvc; private RemoteTerminologyServiceValidationSupport mySvc;
private String myCodeSystemError, myValueSetError; private String myCodeSystemError, myValueSetError;
@ -55,14 +48,14 @@ public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminolog
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort(); String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
myCodeSystemError = ourCtx.getLocalizer().getMessage( myCodeSystemError = ourCtx.getLocalizer().getMessage(
RemoteTerminologyServiceValidationSupport.class, RemoteTerminologyServiceValidationSupport.class,
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, CODE_SYSTEM, CODE, baseUrl, ERROR_MESSAGE); ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, baseUrl, IValidationProviders.ERROR_MESSAGE);
myValueSetError = ourCtx.getLocalizer().getMessage( myValueSetError = ourCtx.getLocalizer().getMessage(
RemoteTerminologyServiceValidationSupport.class, RemoteTerminologyServiceValidationSupport.class,
ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, CODE_SYSTEM, CODE, VALUE_SET_URL, baseUrl, ERROR_MESSAGE); ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.VALUE_SET_URL, baseUrl, IValidationProviders.ERROR_MESSAGE);
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl); mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true)); mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
myCodeSystemProvider = new IValidationProvidersDstu3.MyCodeSystemProviderDstu3(); myCodeSystemProvider = new IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3();
myValueSetProvider = new IValidationProvidersDstu3.MyValueSetProviderDstu3(); myValueSetProvider = new IValidateCodeProvidersDstu3.MyValueSetProviderDstu3();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider); ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider);
} }
@ -89,40 +82,45 @@ public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminolog
} }
@Override @Override
public IValidationProviders.IMyValidationProvider getCodeSystemProvider() { public IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 getCodeSystemProvider() {
return myCodeSystemProvider; return myCodeSystemProvider;
} }
@Override @Override
public IValidationProviders.IMyValidationProvider getValueSetProvider() { public IValidateCodeProvidersDstu3.MyValueSetProviderDstu3 getValueSetProvider() {
return myValueSetProvider; return myValueSetProvider;
} }
@Override @Override
public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() { public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() {
return getCodeSystemInvalidCodeOutcome(OperationOutcome.class); return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
} }
@Override @Override
public IBaseOperationOutcome getValueSetInvalidCodeOutcome() { public IBaseOperationOutcome getValueSetInvalidCodeOutcome() {
return getValueSetInvalidCodeOutcome(OperationOutcome.class); return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
}
@Override
public IBaseOperationOutcome getValueSetCustomDetailCodeOutcome() {
return getValueSetCustomDetailCodeOutcome(OperationOutcome.class);
} }
@Override @Override
public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) { public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
Parameters parameters = new Parameters(); Parameters parameters = new Parameters();
parameters.addParameter().setName("result").setValue(new BooleanType(theResult)); parameters.addParameter().setName("result").setValue(new BooleanType(theResult));
parameters.addParameter().setName("code").setValue(new StringType(CODE)); parameters.addParameter().setName("code").setValue(new StringType(IValidationProviders.CODE));
parameters.addParameter().setName("system").setValue(new UriType(CODE_SYSTEM)); parameters.addParameter().setName("system").setValue(new UriType(IValidationProviders.CODE_SYSTEM));
parameters.addParameter().setName("version").setValue(new StringType(CODE_SYSTEM_VERSION)); parameters.addParameter().setName("version").setValue(new StringType(IValidationProviders.CODE_SYSTEM_VERSION));
parameters.addParameter().setName("display").setValue(new StringType(theDisplay)); parameters.addParameter().setName("display").setValue(new StringType(theDisplay));
parameters.addParameter().setName("message").setValue(new StringType(theMessage)); parameters.addParameter().setName("message").setValue(new StringType(theMessage));
parameters.addParameter().setName("issues").setResource((Resource) theIssuesResource); parameters.addParameter().setName("issues").setResource((Resource) theIssuesResource);
return parameters; return parameters;
} }
@Override
public void createCodeSystemReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
myCodeSystemProvider.setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
}
@Override
public void createValueSetReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
myValueSetProvider.setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
}
} }

View File

@ -307,10 +307,10 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc
retVal = new IValidationSupport.CodeValidationResult().setCode(code); retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) { } else if (myValidSystems.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')"; final String message = "Unknown code (for '" + system + "#" + code + "')";
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message).setIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))); retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
} else if (myValidSystemsNotReturningIssues.contains(system)) { } else if (myValidSystemsNotReturningIssues.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')"; final String message = "Unknown code (for '" + system + "#" + code + "')";
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message); retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
} else { } else {
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl); retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
} }

View File

@ -1,10 +1,12 @@
package ca.uhn.fhir.test.utilities.validation; package org.hl7.fhir.r4.validation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.BooleanType;
@ -19,29 +21,38 @@ import org.hl7.fhir.r4.model.ValueSet;
import java.util.List; import java.util.List;
public interface IValidationProvidersR4 { public interface IValidateCodeProvidersR4 {
@SuppressWarnings("unused") @SuppressWarnings("unused")
class MyCodeSystemProviderR4 extends IValidationProviders.MyValidationProvider<CodeSystem> { class MyCodeSystemProviderR4 implements IValidationProviders.IMyCodeSystemProvider {
private UriType mySystemUrl;
private CodeType myCode;
private StringType myDisplay;
private Exception myException;
private Parameters myReturnParams;
@Operation(name = "$validate-code", idempotent = true, returnParameters = { @Operation(name = "validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1), @OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = StringType.class), @OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class) @OperationParam(name = "display", type = StringType.class)
}) })
public IBaseParameters validateCode( public Parameters validateCode(
HttpServletRequest theServletRequest, HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId, @IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl, @OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode, @OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
) throws Exception { ) throws Exception {
String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null; mySystemUrl = theCodeSystemUrl;
String code = theCode != null ? theCode.getValue() : null; myCode = theCode;
return getTerminologyResponse("$validate-code", url, code); myDisplay = theDisplay;
if (myException != null) {
throw myException;
}
return myReturnParams;
} }
@Operation(name = "$lookup", idempotent = true, returnParameters= { @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name = "name", type = StringType.class, min = 1), @OperationParam(name = "name", type = StringType.class, min = 1),
@OperationParam(name = "version", type = StringType.class), @OperationParam(name = "version", type = StringType.class),
@OperationParam(name = "display", type = StringType.class, min = 1), @OperationParam(name = "display", type = StringType.class, min = 1),
@ -58,39 +69,54 @@ public interface IValidationProvidersR4 {
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames, @OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
RequestDetails theRequestDetails RequestDetails theRequestDetails
) throws Exception { ) throws Exception {
String url = theSystem != null ? theSystem.getValue() : null; mySystemUrl = theSystem;
String code = theCode != null ? theCode.getValue() : null; myCode = theCode;
return getTerminologyResponse("$lookup", url, code); if (myException != null) {
throw myException;
} }
return myReturnParams;
}
@Override @Override
public Class<? extends IBaseResource> getResourceType() { public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class; return CodeSystem.class;
} }
@Override public void setException(Exception theException) {
Class<Parameters> getParameterType() { myException = theException;
return Parameters.class;
} }
@Override @Override
public CodeSystem addTerminologyResource(String theUrl) { public void setReturnParams(IBaseParameters theParameters) {
CodeSystem codeSystem = new CodeSystem(); myReturnParams = (Parameters) theParameters;
codeSystem.setId(theUrl.substring(0, theUrl.lastIndexOf("/"))); }
codeSystem.setUrl(theUrl); @Override
addTerminologyResource(theUrl, codeSystem); public String getCode() {
return codeSystem; return myCode != null ? myCode.getValueAsString() : null;
}
@Override
public String getSystem() {
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
}
public String getDisplay() {
return myDisplay != null ? myDisplay.getValue() : null;
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
class MyValueSetProviderR4 extends IValidationProviders.MyValidationProvider<ValueSet> { class MyValueSetProviderR4 implements IValidationProviders.IMyValueSetProvider {
private Exception myException;
private Parameters myReturnParams;
private UriType mySystemUrl;
private UriType myValueSetUrl;
private CodeType myCode;
private StringType myDisplay;
@Operation(name = "$validate-code", idempotent = true, returnParameters = { @Operation(name = "validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1), @OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = StringType.class), @OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class) @OperationParam(name = "display", type = StringType.class)
}) })
public IBaseParameters validateCode( public Parameters validateCode(
HttpServletRequest theServletRequest, HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId, @IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl, @OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
@ -99,25 +125,41 @@ public interface IValidationProvidersR4 {
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay, @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
@OperationParam(name = "valueSet") ValueSet theValueSet @OperationParam(name = "valueSet") ValueSet theValueSet
) throws Exception { ) throws Exception {
String url = theValueSetUrl != null ? theValueSetUrl.getValue() : null; mySystemUrl = theSystem;
String code = theCode != null ? theCode.getValue() : null; myValueSetUrl = theValueSetUrl;
return getTerminologyResponse("$validate-code", url, code); myCode = theCode;
myDisplay = theDisplay;
if (myException != null) {
throw myException;
} }
return myReturnParams;
}
@Override @Override
public Class<? extends IBaseResource> getResourceType() { public Class<? extends IBaseResource> getResourceType() {
return ValueSet.class; return ValueSet.class;
} }
@Override public void setException(Exception theException) {
Class<Parameters> getParameterType() { myException = theException;
return Parameters.class;
} }
@Override @Override
public ValueSet addTerminologyResource(String theUrl) { public void setReturnParams(IBaseParameters theParameters) {
ValueSet valueSet = new ValueSet(); myReturnParams = (Parameters) theParameters;
valueSet.setId(theUrl.substring(0, theUrl.lastIndexOf("/"))); }
valueSet.setUrl(theUrl); @Override
addTerminologyResource(theUrl, valueSet); public String getCode() {
return valueSet; return myCode != null ? myCode.getValueAsString() : null;
}
@Override
public String getSystem() {
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
}
@Override
public String getValueSet() {
return myValueSetUrl != null ? myValueSetUrl.getValueAsString() : null;
}
public String getDisplay() {
return myDisplay != null ? myDisplay.getValue() : null;
} }
} }
} }

View File

@ -11,10 +11,9 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest; import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest;
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
@ -53,7 +52,7 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
@RegisterExtension @RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx); public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx); private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
private IValidationProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider; private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
private MyLookupCodeProviderR4 myLookupCodeProviderR4; private MyLookupCodeProviderR4 myLookupCodeProviderR4;
@BeforeEach @BeforeEach
@ -61,7 +60,7 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort(); String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc.setBaseUrl(baseUrl); mySvc.setBaseUrl(baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(true)); mySvc.addClientInterceptor(new LoggingInterceptor(true));
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4(); myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
myLookupCodeProviderR4 = new MyLookupCodeProviderR4(); myLookupCodeProviderR4 = new MyLookupCodeProviderR4();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myLookupCodeProviderR4); ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myLookupCodeProviderR4);
} }
@ -167,6 +166,8 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
@SuppressWarnings("unused") @SuppressWarnings("unused")
static class MyLookupCodeProviderR4 implements IValidationProviders.IMyLookupCodeProvider { static class MyLookupCodeProviderR4 implements IValidationProviders.IMyLookupCodeProvider {
private UriType mySystemUrl;
private CodeType myCode;
private LookupCodeResult myLookupCodeResult; private LookupCodeResult myLookupCodeResult;
@Override @Override
@ -191,6 +192,8 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames, @OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
RequestDetails theRequestDetails RequestDetails theRequestDetails
) { ) {
myCode = theCode;
mySystemUrl = theSystem;
if (theSystem == null) { if (theSystem == null) {
throw new InvalidRequestException(MessageFormat.format(MESSAGE_RESPONSE_INVALID, theCode)); throw new InvalidRequestException(MessageFormat.format(MESSAGE_RESPONSE_INVALID, theCode));
} }
@ -203,5 +206,15 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
public Class<? extends IBaseResource> getResourceType() { public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class; return CodeSystem.class;
} }
@Override
public String getCode() {
return myCode != null ? myCode.getValueAsString() : null;
}
@Override
public String getSystem() {
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
}
} }
} }

View File

@ -5,9 +5,12 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4; import ca.uhn.fhir.util.ClasspathUtil;
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -16,15 +19,13 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import java.util.List; import java.util.List;
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_LOOKUP;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class RemoteTerminologyLookupCodeWithResponseFileR4Test { public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final FhirContext ourCtx = FhirContext.forR4Cached();
private IValidationProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider; private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
@RegisterExtension @RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx); public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
@ -35,7 +36,7 @@ public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort(); String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl); mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true)); mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4(); myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider); ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider);
} }
@ -47,10 +48,13 @@ public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
@Test @Test
void lookupCode_withParametersOutput_convertsCorrectly() { void lookupCode_withParametersOutput_convertsCorrectly() {
String outputFile ="/terminology/CodeSystem-lookup-output-with-subproperties.json"; String paramsAsString = ClasspathUtil.loadResource("/terminology/CodeSystem-lookup-output-with-subproperties.json");
IBaseParameters resultParameters = myCodeSystemProvider.addTerminologyResponse(OPERATION_LOOKUP, CODE_SYSTEM, CODE, ourCtx, outputFile); IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
assertTrue(baseResource instanceof Parameters);
Parameters resultParameters = (Parameters) baseResource;
myCodeSystemProvider.setReturnParams(resultParameters);
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces")); LookupCodeRequest request = new LookupCodeRequest(IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, null, List.of("interfaces"));
// test // test
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request); IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);

View File

@ -2,8 +2,8 @@ package org.hl7.fhir.r4.validation;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.parser.IJsonLikeParser; import ca.uhn.fhir.parser.IJsonLikeParser;
import ca.uhn.fhir.rest.client.api.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpRequest;
@ -13,11 +13,11 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.test.utilities.validation.IValidationProviders; import ca.uhn.fhir.util.ClasspathUtil;
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.ParametersUtil;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyValidateCodeTest; import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyValidateCodeTest;
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -39,12 +39,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.CODE_SYSTEM_VERSION;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.DISPLAY;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.ERROR_MESSAGE;
import static ca.uhn.fhir.test.utilities.validation.IValidationProviders.VALUE_SET_URL;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM; import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM;
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET; import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET;
@ -67,8 +61,8 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final FhirContext ourCtx = FhirContext.forR4Cached();
@RegisterExtension @RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx); public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private IValidationProviders.IMyValidationProvider myCodeSystemProvider; private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
private IValidationProviders.IMyValidationProvider myValueSetProvider; private IValidateCodeProvidersR4.MyValueSetProviderR4 myValueSetProvider;
private RemoteTerminologyServiceValidationSupport mySvc; private RemoteTerminologyServiceValidationSupport mySvc;
private String myCodeSystemError, myValueSetError; private String myCodeSystemError, myValueSetError;
@ -77,14 +71,14 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort(); String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
myCodeSystemError = ourCtx.getLocalizer().getMessage( myCodeSystemError = ourCtx.getLocalizer().getMessage(
RemoteTerminologyServiceValidationSupport.class, RemoteTerminologyServiceValidationSupport.class,
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, CODE_SYSTEM, CODE, baseUrl, ERROR_MESSAGE); ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, baseUrl, IValidationProviders.ERROR_MESSAGE);
myValueSetError = ourCtx.getLocalizer().getMessage( myValueSetError = ourCtx.getLocalizer().getMessage(
RemoteTerminologyServiceValidationSupport.class, RemoteTerminologyServiceValidationSupport.class,
ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, CODE_SYSTEM, CODE, VALUE_SET_URL, baseUrl, ERROR_MESSAGE); ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.VALUE_SET_URL, baseUrl, IValidationProviders.ERROR_MESSAGE);
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl); mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true)); mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4(); myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4(); myValueSetProvider = new IValidateCodeProvidersR4.MyValueSetProviderR4();
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider); ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider);
} }
@ -101,12 +95,12 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
} }
@Override @Override
public IValidationProviders.IMyValidationProvider getCodeSystemProvider() { public IValidationProviders.IMyCodeSystemProvider getCodeSystemProvider() {
return myCodeSystemProvider; return myCodeSystemProvider;
} }
@Override @Override
public IValidationProviders.IMyValidationProvider getValueSetProvider() { public IValidationProviders.IMyValueSetProvider getValueSetProvider() {
return myValueSetProvider; return myValueSetProvider;
} }
@ -122,40 +116,51 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@Override @Override
public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() { public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() {
return getCodeSystemInvalidCodeOutcome(OperationOutcome.class); return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
} }
@Override @Override
public IBaseOperationOutcome getValueSetInvalidCodeOutcome() { public IBaseOperationOutcome getValueSetInvalidCodeOutcome() {
return getValueSetInvalidCodeOutcome(OperationOutcome.class); return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
} }
@Override @Override
public IBaseOperationOutcome getValueSetCustomDetailCodeOutcome() { public List<IValidationSupport.CodeValidationIssue> getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome) {
return getValueSetCustomDetailCodeOutcome(OperationOutcome.class); return ((OperationOutcome)theOperationOutcome).getIssue().stream()
.map(issueComponent -> new IValidationSupport.CodeValidationIssue(
issueComponent.getDetails().getText(),
IValidationSupport.IssueSeverity.ERROR,
/* assume issue type is OperationOutcome.IssueType#CODEINVALID as it is the only match */
IValidationSupport.CodeValidationIssueCode.INVALID,
IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))
.toList();
} }
@Test @Test
void validateCodeInValueSet_success() { void validateCodeInValueSet_success() {
createValueSetReturnParameters(true, DISPLAY, null, null); createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
ValueSet valueSet = new ValueSet(); ValueSet valueSet = new ValueSet();
valueSet.setUrl(VALUE_SET_URL); valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), CODE_SYSTEM, CODE, DISPLAY, valueSet); CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
assertNotNull(outcome); assertNotNull(outcome);
assertEquals(CODE, outcome.getCode()); assertEquals(IValidationProviders.CODE, outcome.getCode());
assertEquals(DISPLAY, outcome.getDisplay()); assertEquals(IValidationProviders.DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity()); assertNull(outcome.getSeverity());
assertNull(outcome.getMessage()); assertNull(outcome.getMessage());
assertEquals(IValidationProviders.CODE, myValueSetProvider.getCode());
assertEquals(IValidationProviders.DISPLAY, myValueSetProvider.getDisplay());
assertEquals(IValidationProviders.VALUE_SET_URL, myValueSetProvider.getValueSet());
} }
@Override @Override
public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) { public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
Parameters parameters = new Parameters() Parameters parameters = new Parameters()
.addParameter("code", CODE) .addParameter("code", IValidationProviders.CODE)
.addParameter("system", CODE_SYSTEM) .addParameter("system", IValidationProviders.CODE_SYSTEM)
.addParameter("version", CODE_SYSTEM_VERSION) .addParameter("version", IValidationProviders.CODE_SYSTEM_VERSION)
.addParameter("display", theDisplay) .addParameter("display", theDisplay)
.addParameter("message", theMessage); .addParameter("message", theMessage);
if (theResult != null) { if (theResult != null) {
@ -176,16 +181,16 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@Test @Test
void validateCodeInValueSet_uniqueComposeInclude() { void validateCodeInValueSet_uniqueComposeInclude() {
createValueSetReturnParameters(true, DISPLAY, null, null); createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
ValueSet valueSet = new ValueSet(); ValueSet valueSet = new ValueSet();
valueSet.setUrl(VALUE_SET_URL); valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender"; String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude( valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
Collections.singletonList(new ValueSet.ConceptSetComponent().setSystem(systemUrl)) )); Collections.singletonList(new ValueSet.ConceptSetComponent().setSystem(systemUrl)) ));
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet); new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
// validate service doesn't return error message (as when no code system is present) // validate service doesn't return error message (as when no code system is present)
assertNotNull(outcome); assertNotNull(outcome);
@ -206,16 +211,16 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@ParameterizedTest @ParameterizedTest
@MethodSource(value = "getRemoteTerminologyServerExceptions") @MethodSource(value = "getRemoteTerminologyServerExceptions")
void validateCodeInValueSet_systemNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) { void validateCodeInValueSet_systemNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
getValueSetProvider().addException("$validate-code", VALUE_SET_URL, CODE, theException); myValueSetProvider.setException(theException);
createValueSetReturnParameters(true, DISPLAY, null, null); createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
ValueSet valueSet = new ValueSet(); ValueSet valueSet = new ValueSet();
valueSet.setUrl(VALUE_SET_URL); valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude( valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
Lists.newArrayList(new ValueSet.ConceptSetComponent(), new ValueSet.ConceptSetComponent()))); Lists.newArrayList(new ValueSet.ConceptSetComponent(), new ValueSet.ConceptSetComponent())));
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet); new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://"; String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage); verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage);
@ -225,11 +230,11 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@ParameterizedTest @ParameterizedTest
@MethodSource(value = "getRemoteTerminologyServerExceptions") @MethodSource(value = "getRemoteTerminologyServerExceptions")
void validateCodeInValueSet_systemPresentCodeNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) { void validateCodeInValueSet_systemPresentCodeNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
getValueSetProvider().addException(JpaConstants.OPERATION_VALIDATE_CODE, VALUE_SET_URL, CODE, theException); myValueSetProvider.setException(theException);
createValueSetReturnParameters(true, DISPLAY, null, null); createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
ValueSet valueSet = new ValueSet(); ValueSet valueSet = new ValueSet();
valueSet.setUrl(VALUE_SET_URL); valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender"; String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset"; String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude( valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
@ -238,7 +243,7 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
new ValueSet.ConceptSetComponent().setSystem(systemUrl2)))); new ValueSet.ConceptSetComponent().setSystem(systemUrl2))));
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet); new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://"; String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage); verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage);
@ -247,10 +252,10 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@Test @Test
void validateCodeInValueSet_systemPresentCodePresentValidatesOKNoVersioned() { void validateCodeInValueSet_systemPresentCodePresentValidatesOKNoVersioned() {
createValueSetReturnParameters(true, DISPLAY, null, null); createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
ValueSet valueSet = new ValueSet(); ValueSet valueSet = new ValueSet();
valueSet.setUrl(VALUE_SET_URL); valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender"; String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset"; String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude( valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
@ -259,14 +264,14 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setConcept( new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setConcept(
Lists.newArrayList( Lists.newArrayList(
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"), new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
new ValueSet.ConceptReferenceComponent().setCode(CODE) ) new ValueSet.ConceptReferenceComponent().setCode(IValidationProviders.CODE) )
)) )); )) ));
TestClientInterceptor requestInterceptor = new TestClientInterceptor(); TestClientInterceptor requestInterceptor = new TestClientInterceptor();
mySvc.addClientInterceptor(requestInterceptor); mySvc.addClientInterceptor(requestInterceptor);
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet); new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
assertNotNull(outcome); assertNotNull(outcome);
assertEquals(systemUrl2, requestInterceptor.getCapturedSystemParameter()); assertEquals(systemUrl2, requestInterceptor.getCapturedSystemParameter());
@ -275,10 +280,10 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
@Test @Test
void validateCodeInValueSet_systemPresentCodePresentValidatesOKVersioned() { void validateCodeInValueSet_systemPresentCodePresentValidatesOKVersioned() {
createValueSetReturnParameters(true, DISPLAY, null, null); createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
ValueSet valueSet = new ValueSet(); ValueSet valueSet = new ValueSet();
valueSet.setUrl(VALUE_SET_URL); valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender"; String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
String systemVersion = "3.0.2"; String systemVersion = "3.0.2";
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset"; String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
@ -289,14 +294,14 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setVersion(system2Version).setConcept( new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setVersion(system2Version).setConcept(
Lists.newArrayList( Lists.newArrayList(
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"), new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
new ValueSet.ConceptReferenceComponent().setCode(CODE) ) new ValueSet.ConceptReferenceComponent().setCode(IValidationProviders.CODE) )
)) )); )) ));
TestClientInterceptor requestInterceptor = new TestClientInterceptor(); TestClientInterceptor requestInterceptor = new TestClientInterceptor();
mySvc.addClientInterceptor(requestInterceptor); mySvc.addClientInterceptor(requestInterceptor);
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet); new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
assertNotNull(outcome); assertNotNull(outcome);
assertEquals(systemUrl2 + "|" + system2Version, requestInterceptor.getCapturedSystemParameter()); assertEquals(systemUrl2 + "|" + system2Version, requestInterceptor.getCapturedSystemParameter());

View File

@ -31,7 +31,6 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4b.conformance.ProfileUtilities; import org.hl7.fhir.r4b.conformance.ProfileUtilities;
import org.hl7.fhir.r4b.context.IWorkerContext; import org.hl7.fhir.r4b.context.IWorkerContext;
import org.hl7.fhir.r4b.fhirpath.FHIRPathEngine;
import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r4b.model.AllergyIntolerance; import org.hl7.fhir.r4b.model.AllergyIntolerance;
import org.hl7.fhir.r4b.model.Base; import org.hl7.fhir.r4b.model.Base;
@ -62,6 +61,7 @@ import org.hl7.fhir.r4b.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4b.model.ValueSet; import org.hl7.fhir.r4b.model.ValueSet;
import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4b.terminologies.ValueSetExpander; import org.hl7.fhir.r4b.terminologies.ValueSetExpander;
import org.hl7.fhir.r4b.fhirpath.FHIRPathEngine;
import org.hl7.fhir.r5.test.utils.ClassesLoadedFlags; import org.hl7.fhir.r5.test.utils.ClassesLoadedFlags;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
@ -203,7 +203,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo
retVal = new IValidationSupport.CodeValidationResult().setCode(code); retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) { } else if (myValidSystems.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')"; final String message = "Unknown code (for '" + system + "#" + code + "')";
return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message).setIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))); return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
} else { } else {
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl); retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
} }

View File

@ -48,6 +48,7 @@ import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel; import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -199,10 +200,10 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
retVal = new IValidationSupport.CodeValidationResult().setCode(code); retVal = new IValidationSupport.CodeValidationResult().setCode(code);
} else if (myValidSystems.contains(system)) { } else if (myValidSystems.contains(system)) {
String theMessage = "Unknown code (for '" + system + "#" + code + "')"; String theMessage = "Unknown code (for '" + system + "#" + code + "')";
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(theMessage).setIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(theMessage, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))); retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(theMessage).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(theMessage, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
} else if (myValidSystemsNotReturningIssues.contains(system)) { } else if (myValidSystemsNotReturningIssues.contains(system)) {
final String message = "Unknown code (for '" + system + "#" + code + "')"; final String message = "Unknown code (for '" + system + "#" + code + "')";
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message); retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
} else { } else {
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl); retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
} }

View File

@ -1,22 +0,0 @@
{
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "processing",
"details": {
"coding": [
{
"system": "http://example.com/custom-issue-type",
"code": "valueset-is-draft"
}
]
},
"diagnostics": "The ValueSet status is marked as draft.",
"location": [
"Bundle",
"Line[1] Col[2]"
]
}
]
}