Fixes for the translation of parameter issues as part of the output for $validate-code operation. (#6423)
* Move the validation providers to the test utilities package such that they can be reused. * Update IValidationSupport.CodeValidationIssue structure such that it meets the FHIR specification. Update RemoteTerminologyServiceValidationSupport and VersionSpecificWorkerContextWrapper translation for issues. * New tests for issue translation. Move test class to a different package so that we can add another test class. * Some simplification in the issue API to simplify building of issues. * Fix compilation errors. * Update providers to allow multiple responses and add support for the fetchCodeSystem call through method find. * Setup the first test for resource validation with remote terminology providers. * Fix NullPointerException * avoid calling validateCode for CodeSystem where system is null * Keep old public API methods (and class name) in IValidationSupport and mark them as deprecated to avoid breaking dependencies. * Revert local change to debug for duplicate errors. * Add more test cases for resource validation. Throw exception to signal missing test setup to make it obvious. * Simplify test setup. * Add some more javadoc * Add javadoc for the new test class * Add more tests * Address code review comments in IValidationSupport. * Add changelog
This commit is contained in:
parent
a52578e798
commit
fca3ea4445
|
@ -440,74 +440,259 @@ public interface IValidationSupport {
|
|||
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 {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
WARNING,
|
||||
WARNING("warning"),
|
||||
/**
|
||||
* 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 {
|
||||
NOT_FOUND,
|
||||
CODE_INVALID,
|
||||
INVALID,
|
||||
OTHER
|
||||
/**
|
||||
* Defines codes in system <a href="http://hl7.org/fhir/issue-type">http://hl7.org/fhir/issue-type</a>.
|
||||
* The binding is enforced as a part of validation logic in the FHIR Core Validation library where an exception is thrown.
|
||||
* Only a sub-set of these codes are defined as constants because they relate to validation,
|
||||
* If there are additional ones that come up, for Remote Terminology they are currently supported via
|
||||
* {@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 {
|
||||
VS_INVALID,
|
||||
NOT_FOUND,
|
||||
NOT_IN_VS,
|
||||
|
||||
INVALID_CODE,
|
||||
INVALID_DISPLAY,
|
||||
OTHER
|
||||
/**
|
||||
* Retrieve the corresponding code from system <a href="http://hl7.org/fhir/issue-type">http://hl7.org/fhir/issue-type</a>.
|
||||
* @return the code
|
||||
*/
|
||||
public String getCode() {
|
||||
return myCode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
private final String myMessage;
|
||||
private final String myDiagnostics;
|
||||
private final IssueSeverity mySeverity;
|
||||
private final CodeValidationIssueCode myCode;
|
||||
private final CodeValidationIssueCoding myCoding;
|
||||
private CodeValidationIssueDetails myDetails;
|
||||
|
||||
public CodeValidationIssue(
|
||||
String theMessage,
|
||||
IssueSeverity mySeverity,
|
||||
CodeValidationIssueCode theCode,
|
||||
CodeValidationIssueCoding theCoding) {
|
||||
this.myMessage = theMessage;
|
||||
this.mySeverity = mySeverity;
|
||||
this.myCode = theCode;
|
||||
this.myCoding = theCoding;
|
||||
String theDiagnostics, IssueSeverity theSeverity, CodeValidationIssueCode theTypeCode) {
|
||||
this(theDiagnostics, theSeverity, theTypeCode, null);
|
||||
}
|
||||
|
||||
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() {
|
||||
return myMessage;
|
||||
return getDiagnostics();
|
||||
}
|
||||
|
||||
public String getDiagnostics() {
|
||||
return myDiagnostics;
|
||||
}
|
||||
|
||||
public IssueSeverity getSeverity() {
|
||||
return mySeverity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use {@link #getType()} instead.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationIssueCode getCode() {
|
||||
return getType();
|
||||
}
|
||||
|
||||
public CodeValidationIssueCode getType() {
|
||||
return myCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use {@link #getDetails()} instead. That has support for multiple codings.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationIssueCoding getCoding() {
|
||||
return myCoding;
|
||||
return myDetails != null
|
||||
? 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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -671,6 +856,10 @@ 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 {
|
||||
public static final String SOURCE_DETAILS = "sourceDetails";
|
||||
public static final String RESULT = "result";
|
||||
|
@ -686,7 +875,7 @@ public interface IValidationSupport {
|
|||
private String myDisplay;
|
||||
private String mySourceDetails;
|
||||
|
||||
private List<CodeValidationIssue> myCodeValidationIssues;
|
||||
private List<CodeValidationIssue> myIssues;
|
||||
|
||||
public CodeValidationResult() {
|
||||
super();
|
||||
|
@ -771,20 +960,45 @@ public interface IValidationSupport {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use method {@link #getIssues()} instead.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public List<CodeValidationIssue> getCodeValidationIssues() {
|
||||
if (myCodeValidationIssues == null) {
|
||||
myCodeValidationIssues = new ArrayList<>();
|
||||
}
|
||||
return myCodeValidationIssues;
|
||||
return getIssues();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use method {@link #setIssues(List)} instead.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationResult setCodeValidationIssues(List<CodeValidationIssue> theCodeValidationIssues) {
|
||||
myCodeValidationIssues = new ArrayList<>(theCodeValidationIssues);
|
||||
return setIssues(theCodeValidationIssues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use method {@link #addIssue(CodeValidationIssue)} instead.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationResult addCodeValidationIssue(CodeValidationIssue theCodeValidationIssue) {
|
||||
getCodeValidationIssues().add(theCodeValidationIssue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeValidationResult addCodeValidationIssue(CodeValidationIssue theCodeValidationIssue) {
|
||||
getCodeValidationIssues().add(theCodeValidationIssue);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -811,17 +1025,19 @@ public interface IValidationSupport {
|
|||
public String getSeverityCode() {
|
||||
String retVal = null;
|
||||
if (getSeverity() != null) {
|
||||
retVal = getSeverity().name().toLowerCase();
|
||||
retVal = getSeverity().getCode();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an issue severity as a string code. Value must be the name of
|
||||
* one of the enum values in {@link IssueSeverity}. Value is case-insensitive.
|
||||
* Sets an issue severity using a severity code. Please use method {@link #setSeverity(IssueSeverity)} instead.
|
||||
* @param theSeverityCode the code
|
||||
* @return the current {@link CodeValidationResult} instance
|
||||
*/
|
||||
public CodeValidationResult setSeverityCode(@Nonnull String theIssueSeverity) {
|
||||
setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase()));
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationResult setSeverityCode(@Nonnull String theSeverityCode) {
|
||||
setSeverity(IssueSeverity.fromCode(theSeverityCode));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -838,6 +1054,11 @@ public interface IValidationSupport {
|
|||
if (isNotBlank(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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
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."
|
|
@ -1044,7 +1044,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
if (theExpansionOptions != null
|
||||
&& !theExpansionOptions.isFailOnMissingCodeSystem()
|
||||
// Code system is unknown, therefore NOT_FOUND
|
||||
&& e.getCodeValidationIssue().getCoding() == CodeValidationIssueCoding.NOT_FOUND) {
|
||||
&& e.getCodeValidationIssue()
|
||||
.hasIssueDetailCode(CodeValidationIssueCoding.NOT_FOUND.getCode())) {
|
||||
return;
|
||||
}
|
||||
throw new InternalErrorException(Msg.code(888) + e);
|
||||
|
@ -2190,7 +2191,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setCodeSystemVersion(theCodeSystemVersion)
|
||||
.setMessage(theMessage)
|
||||
.addCodeValidationIssue(new CodeValidationIssue(
|
||||
.addIssue(new CodeValidationIssue(
|
||||
theMessage,
|
||||
IssueSeverity.ERROR,
|
||||
CodeValidationIssueCode.CODE_INVALID,
|
||||
|
|
|
@ -1,29 +1,21 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
package ca.uhn.fhir.jpa.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.config.JpaConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
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.test.utilities.server.RestfulServerExtension;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
|
||||
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.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
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.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -33,9 +25,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -43,15 +33,15 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/*
|
||||
/**
|
||||
* This set of integration tests that instantiates and injects an instance of
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
|
||||
* into the ValidationSupportChain, which tests the logic of dynamically selecting the correct Remote Terminology
|
||||
* implementation. It also exercises the code found in
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
|
||||
* implementation. It also exercises the validateCode output translation code found in
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
|
||||
*/
|
||||
public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeOperationWithRemoteTerminologyR4Test.class);
|
||||
public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeWithRemoteTerminologyR4Test.class);
|
||||
private static final String DISPLAY = "DISPLAY";
|
||||
private static final String DISPLAY_BODY_MASS_INDEX = "Body mass index (BMI) [Ratio]";
|
||||
private static final String CODE_BODY_MASS_INDEX = "39156-5";
|
||||
|
@ -64,8 +54,8 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
|
||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||
private MyCodeSystemProvider myCodeSystemProvider;
|
||||
private MyValueSetProvider myValueSetProvider;
|
||||
private IValidationProviders.MyValidationProvider<CodeSystem> myCodeSystemProvider;
|
||||
private IValidationProviders.MyValidationProvider<ValueSet> myValueSetProvider;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||
|
@ -76,8 +66,8 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||
myValidationSupportChain.addValidationSupport(0, mySvc);
|
||||
myCodeSystemProvider = new MyCodeSystemProvider();
|
||||
myValueSetProvider = new MyValueSetProvider();
|
||||
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
|
||||
myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4();
|
||||
ourRestfulServerExtension.registerProvider(myCodeSystemProvider);
|
||||
ourRestfulServerExtension.registerProvider(myValueSetProvider);
|
||||
}
|
||||
|
@ -103,11 +93,11 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
|
||||
myCodeSystemProvider.myReturnParams = new Parameters();
|
||||
myCodeSystemProvider.myReturnParams.addParameter("result", true);
|
||||
myCodeSystemProvider.myReturnParams.addParameter("display", DISPLAY);
|
||||
final String code = "P";
|
||||
final String system = CODE_SYSTEM_V2_0247_URI;;
|
||||
|
||||
Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY);
|
||||
setupCodeSystemValidateCode(system, code, params);
|
||||
|
||||
logAllConcepts();
|
||||
|
||||
|
@ -115,8 +105,8 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
|
||||
.andParameter("url", new UriType(CODE_SYSTEM_V2_0247_URI))
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem(system).setCode(code))
|
||||
.andParameter("url", new UriType(system))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
|
@ -128,7 +118,7 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.setShouldThrowExceptionForResourceNotFound(false);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
|
@ -166,21 +156,21 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
myValueSetProvider.myReturnParams = new Parameters();
|
||||
myValueSetProvider.myReturnParams.addParameter("result", true);
|
||||
myValueSetProvider.myReturnParams.addParameter("display", DISPLAY);
|
||||
final String code = "alerts";
|
||||
final String system = "http://terminology.hl7.org/CodeSystem/list-example-use-codes";
|
||||
final String valueSetUrl = "http://hl7.org/fhir/ValueSet/list-example-codes";
|
||||
|
||||
Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY);
|
||||
setupValueSetValidateCode(valueSetUrl, system, code, params);
|
||||
setupCodeSystemValidateCode(system, code, params);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "code", new CodeType("alerts"))
|
||||
.andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/list-example-use-codes"))
|
||||
.andParameter("url", new UriType("http://hl7.org/fhir/ValueSet/list-example-codes"))
|
||||
.withParameter(Parameters.class, "code", new CodeType(code))
|
||||
.andParameter("system", new UriType(system))
|
||||
.andParameter("url", new UriType(valueSetUrl))
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
|
||||
|
@ -193,21 +183,20 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnValueSet_byUrlSystemAndCode() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
myValueSetProvider.myReturnParams = new Parameters();
|
||||
myValueSetProvider.myReturnParams.addParameter("result", true);
|
||||
myValueSetProvider.myReturnParams.addParameter("display", DISPLAY_BODY_MASS_INDEX);
|
||||
final String code = CODE_BODY_MASS_INDEX;
|
||||
final String system = "http://terminology.hl7.org/CodeSystem/list-example-use-codes";
|
||||
final String valueSetUrl = "http://hl7.org/fhir/ValueSet/list-example-codes";
|
||||
|
||||
Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY_BODY_MASS_INDEX);
|
||||
setupValueSetValidateCode(valueSetUrl, system, code, params);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "code", new CodeType(CODE_BODY_MASS_INDEX))
|
||||
.andParameter("url", new UriType("https://loinc.org"))
|
||||
.andParameter("system", new UriType("http://loinc.org"))
|
||||
.withParameter(Parameters.class, "code", new CodeType(code))
|
||||
.andParameter("url", new UriType(valueSetUrl))
|
||||
.andParameter("system", new UriType(system))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
|
@ -219,7 +208,7 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() {
|
||||
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.setShouldThrowExceptionForResourceNotFound(false);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
|
@ -238,70 +227,18 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
" - Unknown or unusable ValueSet[" + UNKNOWN_VALUE_SYSTEM_URI + "]");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||
private List<CodeSystem> myReturnCodeSystems;
|
||||
private Parameters myReturnParams;
|
||||
private void setupValueSetValidateCode(String theUrl, String theSystem, String theCode, IBaseParameters theResponseParams) {
|
||||
ValueSet valueSet = myValueSetProvider.addTerminologyResource(theUrl);
|
||||
myValueSetProvider.addTerminologyResource(theSystem);
|
||||
myValueSetProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, valueSet.getUrl(), theCode, theResponseParams);
|
||||
|
||||
@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 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;
|
||||
// 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);
|
||||
}
|
||||
|
||||
private void setupCodeSystemValidateCode(String theUrl, String theCode, IBaseParameters theResponseParams) {
|
||||
CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl);
|
||||
myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, theResponseParams);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"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'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"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'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"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'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"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'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"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'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"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'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
package org.hl7.fhir.r4.validation;
|
||||
package ca.uhn.fhir.test.utilities.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.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
|
@ -21,38 +19,29 @@ import org.hl7.fhir.r4.model.ValueSet;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public interface IValidateCodeProvidersR4 {
|
||||
public interface IValidationProvidersR4 {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
class MyCodeSystemProviderR4 implements IValidationProviders.IMyCodeSystemProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
private StringType myDisplay;
|
||||
private Exception myException;
|
||||
private Parameters myReturnParams;
|
||||
class MyCodeSystemProviderR4 extends IValidationProviders.MyValidationProvider<CodeSystem> {
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@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(
|
||||
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 {
|
||||
mySystemUrl = theCodeSystemUrl;
|
||||
myCode = theCode;
|
||||
myDisplay = theDisplay;
|
||||
if (myException != null) {
|
||||
throw myException;
|
||||
}
|
||||
return myReturnParams;
|
||||
String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null;
|
||||
String code = theCode != null ? theCode.getValue() : null;
|
||||
return getTerminologyResponse("$validate-code", url, code);
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||
@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),
|
||||
|
@ -69,54 +58,39 @@ public interface IValidateCodeProvidersR4 {
|
|||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) throws Exception {
|
||||
mySystemUrl = theSystem;
|
||||
myCode = theCode;
|
||||
if (myException != null) {
|
||||
throw myException;
|
||||
String url = theSystem != null ? theSystem.getValue() : null;
|
||||
String code = theCode != null ? theCode.getValue() : null;
|
||||
return getTerminologyResponse("$lookup", url, code);
|
||||
}
|
||||
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;
|
||||
Class<Parameters> getParameterType() {
|
||||
return Parameters.class;
|
||||
}
|
||||
|
||||
@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;
|
||||
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 MyValueSetProviderR4 implements IValidationProviders.IMyValueSetProvider {
|
||||
private Exception myException;
|
||||
private Parameters myReturnParams;
|
||||
private UriType mySystemUrl;
|
||||
private UriType myValueSetUrl;
|
||||
private CodeType myCode;
|
||||
private StringType myDisplay;
|
||||
class MyValueSetProviderR4 extends IValidationProviders.MyValidationProvider<ValueSet> {
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@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(
|
||||
public IBaseParameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
|
@ -125,41 +99,25 @@ public interface IValidateCodeProvidersR4 {
|
|||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "valueSet") ValueSet theValueSet
|
||||
) throws Exception {
|
||||
mySystemUrl = theSystem;
|
||||
myValueSetUrl = theValueSetUrl;
|
||||
myCode = theCode;
|
||||
myDisplay = theDisplay;
|
||||
if (myException != null) {
|
||||
throw myException;
|
||||
String url = theValueSetUrl != null ? theValueSetUrl.getValue() : null;
|
||||
String code = theCode != null ? theCode.getValue() : null;
|
||||
return getTerminologyResponse("$validate-code", url, code);
|
||||
}
|
||||
return myReturnParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return ValueSet.class;
|
||||
}
|
||||
public void setException(Exception theException) {
|
||||
myException = theException;
|
||||
@Override
|
||||
Class<Parameters> getParameterType() {
|
||||
return Parameters.class;
|
||||
}
|
||||
@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;
|
||||
public ValueSet addTerminologyResource(String theUrl) {
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
|
||||
valueSet.setUrl(theUrl);
|
||||
addTerminologyResource(theUrl, valueSet);
|
||||
return valueSet;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -190,7 +190,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
return new CodeValidationResult()
|
||||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setMessage(theMessage)
|
||||
.setCodeValidationIssues(Collections.singletonList(new CodeValidationIssue(
|
||||
.setIssues(Collections.singletonList(new CodeValidationIssue(
|
||||
theMessage,
|
||||
IssueSeverity.ERROR,
|
||||
CodeValidationIssueCode.INVALID,
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|||
import org.hl7.fhir.r5.model.CanonicalType;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.Enumerations;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -258,7 +257,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode);
|
||||
} catch (ExpansionCouldNotBeCompletedInternallyException e) {
|
||||
CodeValidationResult codeValidationResult = new CodeValidationResult();
|
||||
codeValidationResult.setSeverityCode("error");
|
||||
codeValidationResult.setSeverity(IssueSeverity.ERROR);
|
||||
|
||||
String msg = "Failed to expand ValueSet '" + vsUrl + "' (in-memory). Could not validate code "
|
||||
+ theCodeSystemUrlAndVersion + "#" + theCode;
|
||||
|
@ -267,7 +266,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
|
||||
codeValidationResult.setMessage(msg);
|
||||
codeValidationResult.addCodeValidationIssue(e.getCodeValidationIssue());
|
||||
codeValidationResult.addIssue(e.getCodeValidationIssue());
|
||||
return codeValidationResult;
|
||||
}
|
||||
|
||||
|
@ -551,18 +550,18 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
if (valueSetResult != null) {
|
||||
codeValidationResult = valueSetResult;
|
||||
} else {
|
||||
ValidationMessage.IssueSeverity severity;
|
||||
IValidationSupport.IssueSeverity severity;
|
||||
String message;
|
||||
CodeValidationIssueCode issueCode = CodeValidationIssueCode.CODE_INVALID;
|
||||
CodeValidationIssueCoding issueCoding = CodeValidationIssueCoding.INVALID_CODE;
|
||||
if ("fragment".equals(codeSystemResourceContentMode)) {
|
||||
severity = ValidationMessage.IssueSeverity.WARNING;
|
||||
severity = IValidationSupport.IssueSeverity.WARNING;
|
||||
message = "Unknown code in fragment CodeSystem '"
|
||||
+ getFormattedCodeSystemAndCodeForMessage(
|
||||
theCodeSystemUrlAndVersionToValidate, theCodeToValidate)
|
||||
+ "'";
|
||||
} else {
|
||||
severity = ValidationMessage.IssueSeverity.ERROR;
|
||||
severity = IValidationSupport.IssueSeverity.ERROR;
|
||||
message = "Unknown code '"
|
||||
+ getFormattedCodeSystemAndCodeForMessage(
|
||||
theCodeSystemUrlAndVersionToValidate, theCodeToValidate)
|
||||
|
@ -574,10 +573,9 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
}
|
||||
|
||||
codeValidationResult = new CodeValidationResult()
|
||||
.setSeverityCode(severity.toCode())
|
||||
.setSeverity(severity)
|
||||
.setMessage(message)
|
||||
.addCodeValidationIssue(new CodeValidationIssue(
|
||||
message, getIssueSeverityFromCodeValidationIssue(severity), issueCode, issueCoding));
|
||||
.addIssue(new CodeValidationIssue(message, severity, issueCode, issueCoding));
|
||||
}
|
||||
|
||||
return codeValidationResult;
|
||||
|
@ -589,19 +587,6 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
+ 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(
|
||||
String theCodeToValidate,
|
||||
String theDisplayToValidate,
|
||||
|
@ -1123,8 +1108,8 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
new CodeValidationIssue(
|
||||
theMessage,
|
||||
IssueSeverity.ERROR,
|
||||
CodeValidationIssueCode.OTHER,
|
||||
CodeValidationIssueCoding.OTHER));
|
||||
CodeValidationIssueCode.INVALID,
|
||||
CodeValidationIssueCoding.VS_INVALID));
|
||||
}
|
||||
for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next :
|
||||
subExpansion.getExpansion().getContains()) {
|
||||
|
@ -1376,7 +1361,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
.setCodeSystemVersion(theCodeSystemVersion)
|
||||
.setDisplay(theExpectedDisplay);
|
||||
if (issueSeverity != null) {
|
||||
codeValidationResult.setCodeValidationIssues(Collections.singletonList(new CodeValidationIssue(
|
||||
codeValidationResult.setIssues(Collections.singletonList(new CodeValidationIssue(
|
||||
message,
|
||||
theIssueSeverityForCodeDisplayMismatch,
|
||||
CodeValidationIssueCode.INVALID,
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import org.hl7.fhir.r4.model.Base;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
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.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
|
@ -631,7 +632,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
return new CodeValidationResult()
|
||||
.setSeverity(severity)
|
||||
.setMessage(theMessage)
|
||||
.addCodeValidationIssue(new CodeValidationIssue(
|
||||
.addIssue(new CodeValidationIssue(
|
||||
theMessage, severity, theIssueCode, CodeValidationIssueCoding.INVALID_CODE));
|
||||
}
|
||||
|
||||
|
@ -680,13 +681,13 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
createCodeValidationIssues(
|
||||
(IBaseOperationOutcome) issuesValue.get(),
|
||||
fhirContext.getVersion().getVersion())
|
||||
.ifPresent(i -> i.forEach(result::addCodeValidationIssue));
|
||||
.ifPresent(i -> i.forEach(result::addIssue));
|
||||
} else {
|
||||
// create a validation issue out of the message
|
||||
// this is a workaround to overcome an issue in the FHIR Validator library
|
||||
// where ValueSet bindings are only reading issues but not messages
|
||||
// @see https://github.com/hapifhir/org.hl7.fhir.core/issues/1766
|
||||
result.addCodeValidationIssue(createCodeValidationIssue(result.getMessage()));
|
||||
result.addIssue(createCodeValidationIssue(result.getMessage()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -717,23 +718,42 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
|
||||
private static Collection<CodeValidationIssue> createCodeValidationIssuesR4(OperationOutcome theOperationOutcome) {
|
||||
return theOperationOutcome.getIssue().stream()
|
||||
.map(issueComponent ->
|
||||
createCodeValidationIssue(issueComponent.getDetails().getText()))
|
||||
.map(issueComponent -> {
|
||||
String diagnostics = issueComponent.getDiagnostics();
|
||||
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());
|
||||
}
|
||||
|
||||
private static Collection<CodeValidationIssue> createCodeValidationIssuesDstu3(
|
||||
org.hl7.fhir.dstu3.model.OperationOutcome theOperationOutcome) {
|
||||
return theOperationOutcome.getIssue().stream()
|
||||
.map(issueComponent ->
|
||||
createCodeValidationIssue(issueComponent.getDetails().getText()))
|
||||
.map(issueComponent -> {
|
||||
String diagnostics = issueComponent.getDiagnostics();
|
||||
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());
|
||||
}
|
||||
|
||||
private static CodeValidationIssue createCodeValidationIssue(String theMessage) {
|
||||
return new CodeValidationIssue(
|
||||
theMessage,
|
||||
// assume issue type is OperationOutcome.IssueType#CODEINVALID as it is the only match
|
||||
IssueSeverity.ERROR,
|
||||
CodeValidationIssueCode.INVALID,
|
||||
CodeValidationIssueCoding.INVALID_CODE);
|
||||
|
|
|
@ -87,7 +87,7 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup
|
|||
result.setSeverity(null);
|
||||
result.setMessage(null);
|
||||
} else {
|
||||
result.addCodeValidationIssue(new CodeValidationIssue(
|
||||
result.addIssue(new CodeValidationIssue(
|
||||
theMessage,
|
||||
myNonExistentCodeSystemSeverity,
|
||||
CodeValidationIssueCode.NOT_FOUND,
|
||||
|
|
|
@ -11,6 +11,17 @@ public final class 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) {
|
||||
if (theValueSet instanceof org.hl7.fhir.dstu3.model.ValueSet) {
|
||||
return extractCodeSystemForCodeDSTU3((org.hl7.fhir.dstu3.model.ValueSet) theValueSet, theCode);
|
||||
|
|
|
@ -62,6 +62,7 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
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.toSet;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
@ -296,7 +297,7 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
theResult.getCodeSystemVersion(),
|
||||
conceptDefinitionComponent,
|
||||
display,
|
||||
getIssuesForCodeValidation(theResult.getCodeValidationIssues()));
|
||||
getIssuesForCodeValidation(theResult.getIssues()));
|
||||
}
|
||||
|
||||
if (retVal == null) {
|
||||
|
@ -307,73 +308,36 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
}
|
||||
|
||||
private List<OperationOutcome.OperationOutcomeIssueComponent> getIssuesForCodeValidation(
|
||||
List<IValidationSupport.CodeValidationIssue> codeValidationIssues) {
|
||||
List<OperationOutcome.OperationOutcomeIssueComponent> issues = new ArrayList<>();
|
||||
List<IValidationSupport.CodeValidationIssue> theIssues) {
|
||||
List<OperationOutcome.OperationOutcomeIssueComponent> issueComponents = new ArrayList<>();
|
||||
|
||||
for (IValidationSupport.CodeValidationIssue codeValidationIssue : codeValidationIssues) {
|
||||
for (IValidationSupport.CodeValidationIssue issue : theIssues) {
|
||||
OperationOutcome.IssueSeverity severity =
|
||||
OperationOutcome.IssueSeverity.fromCode(issue.getSeverity().getCode());
|
||||
OperationOutcome.IssueType issueType =
|
||||
OperationOutcome.IssueType.fromCode(issue.getType().getCode());
|
||||
String diagnostics = issue.getDiagnostics();
|
||||
|
||||
CodeableConcept codeableConcept = new CodeableConcept().setText(codeValidationIssue.getMessage());
|
||||
codeableConcept.addCoding(
|
||||
"http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
getIssueCodingFromCodeValidationIssue(codeValidationIssue),
|
||||
null);
|
||||
IValidationSupport.CodeValidationIssueDetails details = issue.getDetails();
|
||||
CodeableConcept codeableConcept = new CodeableConcept().setText(details.getText());
|
||||
details.getCodings().forEach(detailCoding -> codeableConcept
|
||||
.addCoding()
|
||||
.setSystem(detailCoding.getSystem())
|
||||
.setCode(detailCoding.getCode()));
|
||||
|
||||
OperationOutcome.OperationOutcomeIssueComponent issue =
|
||||
OperationOutcome.OperationOutcomeIssueComponent issueComponent =
|
||||
new OperationOutcome.OperationOutcomeIssueComponent()
|
||||
.setSeverity(getIssueSeverityFromCodeValidationIssue(codeValidationIssue))
|
||||
.setCode(getIssueTypeFromCodeValidationIssue(codeValidationIssue))
|
||||
.setDetails(codeableConcept);
|
||||
issue.getDetails().setText(codeValidationIssue.getMessage());
|
||||
issue.addExtension()
|
||||
.setSeverity(severity)
|
||||
.setCode(issueType)
|
||||
.setDetails(codeableConcept)
|
||||
.setDiagnostics(diagnostics);
|
||||
issueComponent
|
||||
.addExtension()
|
||||
.setUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id")
|
||||
.setValue(new StringType("Terminology_PassThrough_TX_Message"));
|
||||
issues.add(issue);
|
||||
issueComponents.add(issueComponent);
|
||||
}
|
||||
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;
|
||||
return issueComponents;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -851,25 +815,22 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
.getRootValidationSupport()
|
||||
.validateCodeInValueSet(
|
||||
myValidationSupportContext, theValidationOptions, theSystem, theCode, theDisplay, theValueSet);
|
||||
if (result != null) {
|
||||
if (result != null && theSystem != null) {
|
||||
/* 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
|
||||
to our result.
|
||||
*/
|
||||
IValidationSupport.CodeValidationResult codeSystemResult =
|
||||
validateCodeInCodeSystem(theValidationOptions, theSystem, theCode, theDisplay);
|
||||
final boolean valueSetResultContainsInvalidDisplay = result.getCodeValidationIssues().stream()
|
||||
.anyMatch(codeValidationIssue -> codeValidationIssue.getCoding()
|
||||
== IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY);
|
||||
final boolean valueSetResultContainsInvalidDisplay = result.getIssues().stream()
|
||||
.anyMatch(VersionSpecificWorkerContextWrapper::hasInvalidDisplayDetailCode);
|
||||
if (codeSystemResult != null) {
|
||||
for (IValidationSupport.CodeValidationIssue codeValidationIssue :
|
||||
codeSystemResult.getCodeValidationIssues()) {
|
||||
for (IValidationSupport.CodeValidationIssue codeValidationIssue : codeSystemResult.getIssues()) {
|
||||
/* 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.
|
||||
*/
|
||||
if (codeValidationIssue.getCoding() != IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY
|
||||
|| !valueSetResultContainsInvalidDisplay) {
|
||||
result.addCodeValidationIssue(codeValidationIssue);
|
||||
if (!hasInvalidDisplayDetailCode(codeValidationIssue) || !valueSetResultContainsInvalidDisplay) {
|
||||
result.addIssue(codeValidationIssue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -877,6 +838,10 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
return result;
|
||||
}
|
||||
|
||||
private static boolean hasInvalidDisplayDetailCode(IValidationSupport.CodeValidationIssue theIssue) {
|
||||
return theIssue.hasIssueDetailCode(INVALID_DISPLAY.getCode());
|
||||
}
|
||||
|
||||
private IValidationSupport.CodeValidationResult validateCodeInCodeSystem(
|
||||
ConceptValidationOptions theValidationOptions, String theSystem, String theCode, String theDisplay) {
|
||||
return myValidationSupportContext
|
||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
|||
import ca.uhn.fhir.context.support.IValidationSupport.StringConceptProperty;
|
||||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||
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.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -21,12 +22,12 @@ import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_GROUP;
|
|||
import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_STRING;
|
||||
import static java.util.stream.IntStream.range;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_NAME;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_VERSION;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.DISPLAY;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.LANGUAGE;
|
||||
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_NAME;
|
||||
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.LANGUAGE;
|
||||
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.assertFalse;
|
||||
|
@ -189,8 +190,6 @@ public interface ILookupCodeTest {
|
|||
|
||||
// verify
|
||||
assertNotNull(outcome);
|
||||
assertEquals(theRequest.getCode(), getLookupCodeProvider().getCode());
|
||||
assertEquals(theRequest.getSystem(), getLookupCodeProvider().getSystem());
|
||||
assertEquals(theExpectedResult.isFound(), outcome.isFound());
|
||||
assertEquals(theExpectedResult.getErrorMessage(), outcome.getErrorMessage());
|
||||
assertEquals(theExpectedResult.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.hl7.fhir.common.hapi.validation;
|
|||
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
||||
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.junit.jupiter.api.Test;
|
||||
|
||||
|
|
|
@ -1,17 +1,76 @@
|
|||
package org.hl7.fhir.common.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
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 {
|
||||
default List<IValidationSupport.CodeValidationIssue> getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome) {
|
||||
// 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());
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
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.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -16,12 +19,13 @@ import java.util.List;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import static ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity.ERROR;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_VERSION;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.DISPLAY;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.ERROR_MESSAGE;
|
||||
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.VALUE_SET_URL;
|
||||
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE;
|
||||
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.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -31,21 +35,35 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
|
||||
public interface IValidateCodeTest {
|
||||
|
||||
IValidationProviders.IMyCodeSystemProvider getCodeSystemProvider();
|
||||
IValidationProviders.IMyValueSetProvider getValueSetProvider();
|
||||
IValidationProviders.IMyValidationProvider getCodeSystemProvider();
|
||||
IValidationProviders.IMyValidationProvider getValueSetProvider();
|
||||
IValidationSupport getService();
|
||||
IBaseParameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource);
|
||||
String getCodeSystemError();
|
||||
String getValueSetError();
|
||||
IBaseOperationOutcome getCodeSystemInvalidCodeOutcome();
|
||||
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) {
|
||||
getCodeSystemProvider().setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
|
||||
getCodeSystemProvider().addTerminologyResponse(OPERATION_VALIDATE_CODE, CODE_SYSTEM, CODE, createParameters(theResult, theDisplay, theMessage, theIssuesResource));
|
||||
}
|
||||
|
||||
default void createValueSetReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
|
||||
getValueSetProvider().setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
|
||||
getValueSetProvider().addTerminologyResponse(OPERATION_VALIDATE_CODE, VALUE_SET_URL, CODE, createParameters(theResult, theDisplay, theMessage, theIssuesResource));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -91,8 +109,8 @@ public interface IValidateCodeTest {
|
|||
String theValidationMessage,
|
||||
String theCodeSystem,
|
||||
String theValueSetUrl) {
|
||||
getCodeSystemProvider().setException(theException);
|
||||
getValueSetProvider().setException(theException);
|
||||
getCodeSystemProvider().addException(OPERATION_VALIDATE_CODE, theCodeSystem, CODE, theException);
|
||||
getValueSetProvider().addException(OPERATION_VALIDATE_CODE, theValueSetUrl, CODE, theException);
|
||||
CodeValidationResult outcome = getService().validateCode(null, null, theCodeSystem, CODE, DISPLAY, theValueSetUrl);
|
||||
|
||||
verifyErrorResultFromException(outcome, theValidationMessage, theServerMessage);
|
||||
|
@ -105,7 +123,7 @@ public interface IValidateCodeTest {
|
|||
for (String message : theMessages) {
|
||||
assertTrue(outcome.getMessage().contains(message));
|
||||
}
|
||||
assertFalse(outcome.getCodeValidationIssues().isEmpty());
|
||||
assertFalse(outcome.getIssues().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -130,11 +148,7 @@ public interface IValidateCodeTest {
|
|||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||
|
||||
assertEquals(CODE, getValueSetProvider().getCode());
|
||||
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
|
||||
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||
assertTrue(outcome.getIssues().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -147,9 +161,7 @@ public interface IValidateCodeTest {
|
|||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||
|
||||
assertEquals(CODE, getCodeSystemProvider().getCode());
|
||||
assertTrue(outcome.getIssues().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -165,10 +177,7 @@ public interface IValidateCodeTest {
|
|||
assertNull(outcome.getDisplay());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||
|
||||
assertEquals(CODE, getCodeSystemProvider().getCode());
|
||||
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
|
||||
assertTrue(outcome.getIssues().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -184,15 +193,11 @@ public interface IValidateCodeTest {
|
|||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||
|
||||
assertEquals(CODE, getCodeSystemProvider().getCode());
|
||||
assertEquals(DISPLAY, getCodeSystemProvider().getDisplay());
|
||||
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
|
||||
assertTrue(outcome.getIssues().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
default void validateCode_withCodeSystemError_returnsCorrectly() {
|
||||
default void validateCode_withCodeSystemErrorWithDiagnosticsWithIssues_returnsCorrectly() {
|
||||
IBaseOperationOutcome invalidCodeOutcome = getCodeSystemInvalidCodeOutcome();
|
||||
createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, invalidCodeOutcome);
|
||||
|
||||
|
@ -204,12 +209,12 @@ public interface IValidateCodeTest {
|
|||
// assertEquals(CODE, outcome.getCode());
|
||||
assertEquals(ERROR, outcome.getSeverity());
|
||||
assertEquals(getCodeSystemError(), outcome.getMessage());
|
||||
assertFalse(outcome.getCodeValidationIssues().isEmpty());
|
||||
assertFalse(outcome.getIssues().isEmpty());
|
||||
verifyIssues(invalidCodeOutcome, outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
default void validateCode_withCodeSystemErrorAndIssues_returnsCorrectly() {
|
||||
default void validateCode_withCodeSystemErrorWithDiagnosticsWithoutIssues_returnsCorrectly() {
|
||||
createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, null);
|
||||
|
||||
CodeValidationResult outcome = getService()
|
||||
|
@ -223,10 +228,32 @@ public interface IValidateCodeTest {
|
|||
assertNull(outcome.getDisplay());
|
||||
assertEquals(ERROR, outcome.getSeverity());
|
||||
assertEquals(expectedError, outcome.getMessage());
|
||||
assertFalse(outcome.getCodeValidationIssues().isEmpty());
|
||||
assertEquals(1, outcome.getCodeValidationIssues().size());
|
||||
assertEquals(expectedError, outcome.getCodeValidationIssues().get(0).getMessage());
|
||||
assertEquals(ERROR, outcome.getCodeValidationIssues().get(0).getSeverity());
|
||||
assertFalse(outcome.getIssues().isEmpty());
|
||||
assertEquals(1, outcome.getIssues().size());
|
||||
assertEquals(expectedError, outcome.getIssues().get(0).getDiagnostics());
|
||||
assertEquals(ERROR, outcome.getIssues().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
|
||||
|
@ -242,10 +269,7 @@ public interface IValidateCodeTest {
|
|||
assertNull(outcome.getDisplay());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||
|
||||
assertEquals(CODE, getValueSetProvider().getCode());
|
||||
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||
assertTrue(outcome.getIssues().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -261,11 +285,7 @@ public interface IValidateCodeTest {
|
|||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||
|
||||
assertEquals(CODE, getValueSetProvider().getCode());
|
||||
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
|
||||
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||
assertTrue(outcome.getIssues().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -283,13 +303,9 @@ public interface IValidateCodeTest {
|
|||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertEquals(ERROR, outcome.getSeverity());
|
||||
assertEquals(expectedError, outcome.getMessage());
|
||||
assertEquals(1, outcome.getCodeValidationIssues().size());
|
||||
assertEquals(expectedError, outcome.getCodeValidationIssues().get(0).getMessage());
|
||||
assertEquals(ERROR, outcome.getCodeValidationIssues().get(0).getSeverity());
|
||||
|
||||
assertEquals(CODE, getValueSetProvider().getCode());
|
||||
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
|
||||
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||
assertEquals(1, outcome.getIssues().size());
|
||||
assertEquals(expectedError, outcome.getIssues().get(0).getDiagnostics());
|
||||
assertEquals(ERROR, outcome.getIssues().get(0).getSeverity());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -306,24 +322,28 @@ public interface IValidateCodeTest {
|
|||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertEquals(ERROR, outcome.getSeverity());
|
||||
assertEquals(getValueSetError(), outcome.getMessage());
|
||||
assertFalse(outcome.getCodeValidationIssues().isEmpty());
|
||||
assertFalse(outcome.getIssues().isEmpty());
|
||||
verifyIssues(invalidCodeOutcome, outcome);
|
||||
|
||||
assertEquals(CODE, getValueSetProvider().getCode());
|
||||
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
|
||||
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||
}
|
||||
|
||||
default void verifyIssues(IBaseOperationOutcome theOperationOutcome, CodeValidationResult theResult) {
|
||||
List<IValidationSupport.CodeValidationIssue> issues = getCodeValidationIssues(theOperationOutcome);
|
||||
assertEquals(issues.size(), theResult.getCodeValidationIssues().size());
|
||||
assertEquals(issues.size(), theResult.getIssues().size());
|
||||
for (int i = 0; i < issues.size(); i++) {
|
||||
IValidationSupport.CodeValidationIssue expectedIssue = issues.get(i);
|
||||
IValidationSupport.CodeValidationIssue actualIssue = theResult.getCodeValidationIssues().get(i);
|
||||
assertEquals(expectedIssue.getCode(), actualIssue.getCode());
|
||||
IValidationSupport.CodeValidationIssue actualIssue = theResult.getIssues().get(i);
|
||||
assertEquals(expectedIssue.getType().getCode(), actualIssue.getType().getCode());
|
||||
assertEquals(expectedIssue.getSeverity(), actualIssue.getSeverity());
|
||||
assertEquals(expectedIssue.getCoding(), actualIssue.getCoding());
|
||||
assertEquals(expectedIssue.getMessage(), actualIssue.getMessage());
|
||||
assertEquals(expectedIssue.getDetails().getText(), actualIssue.getDetails().getText());
|
||||
assertEquals(expectedIssue.getDetails().getCodings().size(), actualIssue.getDetails().getCodings().size());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ import ca.uhn.fhir.context.support.ValidationSupportContext;
|
|||
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
|
||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
|
||||
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
|
||||
|
@ -16,17 +15,18 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestWithInlineMocks {
|
||||
|
||||
final byte[] EXPECTED_BINARY_CONTENT_1 = "dummyBinaryContent1".getBytes();
|
||||
|
@ -80,7 +80,7 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
|
|||
}
|
||||
|
||||
@Test
|
||||
public void validateCode_normally_resolvesCodeSystemFromValueSet() {
|
||||
public void validateCode_codeInValueSet_resolvesCodeSystemFromValueSet() {
|
||||
// setup
|
||||
IValidationSupport validationSupport = mockValidationSupport();
|
||||
ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
|
||||
|
@ -90,8 +90,7 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
|
|||
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");
|
||||
when(validationSupport.fetchResource(eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(valueSet);
|
||||
when(validationSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenReturn(new IValidationSupport.CodeValidationResult());
|
||||
when(validationSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenReturn(mock(IValidationSupport.CodeValidationResult.class));
|
||||
|
||||
// execute
|
||||
wrapper.validateCode(new ValidationOptions(), "code0", valueSet);
|
||||
|
@ -101,6 +100,26 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
|
|||
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
|
||||
public void isPrimitive_primitive() {
|
||||
// setup
|
||||
|
|
|
@ -4,7 +4,6 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
|
||||
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||
|
@ -28,10 +27,7 @@ import org.hl7.fhir.dstu2.model.Observation.ObservationStatus;
|
|||
import org.hl7.fhir.dstu2.model.QuestionnaireResponse;
|
||||
import org.hl7.fhir.dstu2.model.QuestionnaireResponse.QuestionnaireResponseStatus;
|
||||
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.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -41,9 +37,7 @@ import org.mockito.stubbing.Answer;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -100,7 +94,7 @@ public class FhirInstanceValidatorDstu2Test extends BaseValidationTestWithInline
|
|||
if (myValidConcepts.contains(system + "___" + code)) {
|
||||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code");
|
||||
return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage("Unknown code");
|
||||
} else {
|
||||
retVal = null;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
|
||||
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
|
||||
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.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -229,10 +228,10 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
|
|||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
final String message = "Unknown code (for '" + system + "#" + 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)));
|
||||
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)));
|
||||
} else if (myValidSystemsNotReturningIssues.contains(system)) {
|
||||
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
||||
retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
|
||||
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message);
|
||||
} else if (myCodeSystems.containsKey(system)) {
|
||||
CodeSystem cs = myCodeSystems.get(system);
|
||||
Optional<ConceptDefinitionComponent> found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst();
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,7 +41,6 @@ import org.hl7.fhir.dstu3.model.Type;
|
|||
import org.hl7.fhir.dstu3.model.UriType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
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.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -56,6 +55,8 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
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.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.BOOLEAN;
|
||||
import static org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType.CHOICE;
|
||||
|
@ -224,7 +225,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(ValueSet.class)))
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0"));
|
||||
when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(ValueSet.class)))
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"));
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult().setSeverity(ERROR).setMessage("Unknown code"));
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
|
||||
|
@ -246,7 +247,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
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));
|
||||
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class)))
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult().setSeverityCode("warning").setMessage("Unknown code: http://codesystems.com/system / code1"));
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult().setSeverity(WARNING).setMessage("Unknown code: http://codesystems.com/system / code1"));
|
||||
|
||||
|
||||
QuestionnaireResponse qa;
|
||||
|
@ -1034,7 +1035,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
|
|||
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class)))
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0"));
|
||||
when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class)))
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"));
|
||||
.thenReturn(new IValidationSupport.CodeValidationResult().setSeverity(ERROR).setMessage("Unknown code"));
|
||||
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
|
||||
|
|
|
@ -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.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
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.dstu3.model.BooleanType;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
|
@ -164,8 +164,6 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
|||
|
||||
@SuppressWarnings("unused")
|
||||
static class MyLookupCodeProviderDstu3 implements IValidationProviders.IMyLookupCodeProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
private LookupCodeResult myLookupCodeResult;
|
||||
|
||||
@Override
|
||||
|
@ -190,8 +188,6 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
|||
@OperationParam(name= " property", max = OperationParam.MAX_UNLIMITED) List<StringType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
myCode = theCode;
|
||||
mySystemUrl = theSystem;
|
||||
if (theSystem == null) {
|
||||
throw new InvalidRequestException(MessageFormat.format(MESSAGE_RESPONSE_INVALID, theCode));
|
||||
}
|
||||
|
@ -205,15 +201,5 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
|||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return myCode != null ? myCode.getValueAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSystem() {
|
||||
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,10 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersDstu3;
|
||||
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.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -19,13 +16,15 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|||
|
||||
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.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
|
||||
private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
|
||||
private IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
|
||||
private IValidationProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
|
||||
@RegisterExtension
|
||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
|
||||
|
@ -36,7 +35,7 @@ public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
|
|||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
||||
myCodeSystemProvider = new IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3();
|
||||
myCodeSystemProvider = new IValidationProvidersDstu3.MyCodeSystemProviderDstu3();
|
||||
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider);
|
||||
}
|
||||
|
||||
|
@ -47,13 +46,10 @@ public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
|
|||
}
|
||||
@Test
|
||||
void lookupCode_withParametersOutput_convertsCorrectly() {
|
||||
String paramsAsString = ClasspathUtil.loadResource("/terminology/CodeSystem-lookup-output-with-subproperties.json");
|
||||
IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
|
||||
assertTrue(baseResource instanceof Parameters);
|
||||
Parameters resultParameters = (Parameters) baseResource;
|
||||
myCodeSystemProvider.setReturnParams(resultParameters);
|
||||
String outputFile ="/terminology/CodeSystem-lookup-output-with-subproperties.json";
|
||||
IBaseParameters resultParameters = myCodeSystemProvider.addTerminologyResponse(OPERATION_LOOKUP, CODE_SYSTEM, CODE, ourCtx, outputFile);
|
||||
|
||||
LookupCodeRequest request = new LookupCodeRequest(IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, null, List.of("interfaces"));
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces"));
|
||||
|
||||
// test
|
||||
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
|
||||
|
|
|
@ -4,16 +4,18 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersDstu3;
|
||||
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.dstu3.model.BooleanType;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.Resource;
|
||||
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.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -22,6 +24,11 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|||
|
||||
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_VALUE_SET;
|
||||
|
||||
|
@ -38,8 +45,8 @@ public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminolog
|
|||
private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
|
||||
@RegisterExtension
|
||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
private IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
|
||||
private IValidateCodeProvidersDstu3.MyValueSetProviderDstu3 myValueSetProvider;
|
||||
private IValidationProviders.MyValidationProvider<CodeSystem> myCodeSystemProvider;
|
||||
private IValidationProviders.MyValidationProvider<ValueSet> myValueSetProvider;
|
||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||
private String myCodeSystemError, myValueSetError;
|
||||
|
||||
|
@ -48,14 +55,14 @@ public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminolog
|
|||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
myCodeSystemError = ourCtx.getLocalizer().getMessage(
|
||||
RemoteTerminologyServiceValidationSupport.class,
|
||||
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, baseUrl, IValidationProviders.ERROR_MESSAGE);
|
||||
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, CODE_SYSTEM, CODE, baseUrl, ERROR_MESSAGE);
|
||||
myValueSetError = ourCtx.getLocalizer().getMessage(
|
||||
RemoteTerminologyServiceValidationSupport.class,
|
||||
ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.VALUE_SET_URL, baseUrl, IValidationProviders.ERROR_MESSAGE);
|
||||
ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, CODE_SYSTEM, CODE, VALUE_SET_URL, baseUrl, ERROR_MESSAGE);
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
||||
myCodeSystemProvider = new IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3();
|
||||
myValueSetProvider = new IValidateCodeProvidersDstu3.MyValueSetProviderDstu3();
|
||||
myCodeSystemProvider = new IValidationProvidersDstu3.MyCodeSystemProviderDstu3();
|
||||
myValueSetProvider = new IValidationProvidersDstu3.MyValueSetProviderDstu3();
|
||||
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider);
|
||||
}
|
||||
|
||||
|
@ -82,45 +89,40 @@ public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminolog
|
|||
}
|
||||
|
||||
@Override
|
||||
public IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 getCodeSystemProvider() {
|
||||
public IValidationProviders.IMyValidationProvider getCodeSystemProvider() {
|
||||
return myCodeSystemProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValidateCodeProvidersDstu3.MyValueSetProviderDstu3 getValueSetProvider() {
|
||||
public IValidationProviders.IMyValidationProvider getValueSetProvider() {
|
||||
return myValueSetProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() {
|
||||
return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
|
||||
return getCodeSystemInvalidCodeOutcome(OperationOutcome.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseOperationOutcome getValueSetInvalidCodeOutcome() {
|
||||
return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
|
||||
return getValueSetInvalidCodeOutcome(OperationOutcome.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseOperationOutcome getValueSetCustomDetailCodeOutcome() {
|
||||
return getValueSetCustomDetailCodeOutcome(OperationOutcome.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter().setName("result").setValue(new BooleanType(theResult));
|
||||
parameters.addParameter().setName("code").setValue(new StringType(IValidationProviders.CODE));
|
||||
parameters.addParameter().setName("system").setValue(new UriType(IValidationProviders.CODE_SYSTEM));
|
||||
parameters.addParameter().setName("version").setValue(new StringType(IValidationProviders.CODE_SYSTEM_VERSION));
|
||||
parameters.addParameter().setName("code").setValue(new StringType(CODE));
|
||||
parameters.addParameter().setName("system").setValue(new UriType(CODE_SYSTEM));
|
||||
parameters.addParameter().setName("version").setValue(new StringType(CODE_SYSTEM_VERSION));
|
||||
parameters.addParameter().setName("display").setValue(new StringType(theDisplay));
|
||||
parameters.addParameter().setName("message").setValue(new StringType(theMessage));
|
||||
parameters.addParameter().setName("issues").setResource((Resource) theIssuesResource);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -307,10 +307,10 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc
|
|||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
final String message = "Unknown code (for '" + system + "#" + 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)));
|
||||
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)));
|
||||
} else if (myValidSystemsNotReturningIssues.contains(system)) {
|
||||
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
||||
retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
|
||||
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message);
|
||||
} else {
|
||||
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
||||
}
|
||||
|
|
|
@ -11,9 +11,10 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
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 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.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
|
@ -52,7 +53,7 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
|||
@RegisterExtension
|
||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
||||
private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
|
||||
private IValidationProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
|
||||
private MyLookupCodeProviderR4 myLookupCodeProviderR4;
|
||||
|
||||
@BeforeEach
|
||||
|
@ -60,7 +61,7 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
|||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc.setBaseUrl(baseUrl);
|
||||
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
||||
myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
|
||||
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
|
||||
myLookupCodeProviderR4 = new MyLookupCodeProviderR4();
|
||||
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myLookupCodeProviderR4);
|
||||
}
|
||||
|
@ -166,8 +167,6 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
|||
|
||||
@SuppressWarnings("unused")
|
||||
static class MyLookupCodeProviderR4 implements IValidationProviders.IMyLookupCodeProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
private LookupCodeResult myLookupCodeResult;
|
||||
|
||||
@Override
|
||||
|
@ -192,8 +191,6 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
|||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
myCode = theCode;
|
||||
mySystemUrl = theSystem;
|
||||
if (theSystem == null) {
|
||||
throw new InvalidRequestException(MessageFormat.format(MESSAGE_RESPONSE_INVALID, theCode));
|
||||
}
|
||||
|
@ -206,15 +203,5 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
|||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return myCode != null ? myCode.getValueAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSystem() {
|
||||
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,9 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
|
||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||
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.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -19,13 +16,15 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|||
|
||||
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.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
|
||||
private static final FhirContext ourCtx = FhirContext.forR4Cached();
|
||||
private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
|
||||
private IValidationProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
|
||||
@RegisterExtension
|
||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
|
||||
|
@ -36,7 +35,7 @@ public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
|
|||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
||||
myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
|
||||
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
|
||||
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider);
|
||||
}
|
||||
|
||||
|
@ -48,13 +47,10 @@ public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
|
|||
|
||||
@Test
|
||||
void lookupCode_withParametersOutput_convertsCorrectly() {
|
||||
String paramsAsString = ClasspathUtil.loadResource("/terminology/CodeSystem-lookup-output-with-subproperties.json");
|
||||
IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
|
||||
assertTrue(baseResource instanceof Parameters);
|
||||
Parameters resultParameters = (Parameters) baseResource;
|
||||
myCodeSystemProvider.setReturnParams(resultParameters);
|
||||
String outputFile ="/terminology/CodeSystem-lookup-output-with-subproperties.json";
|
||||
IBaseParameters resultParameters = myCodeSystemProvider.addTerminologyResponse(OPERATION_LOOKUP, CODE_SYSTEM, CODE, ourCtx, outputFile);
|
||||
|
||||
LookupCodeRequest request = new LookupCodeRequest(IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, null, List.of("interfaces"));
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces"));
|
||||
|
||||
// test
|
||||
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
|
||||
|
|
|
@ -2,8 +2,8 @@ package org.hl7.fhir.r4.validation;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
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.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.parser.IJsonLikeParser;
|
||||
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
||||
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.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
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.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -39,6 +39,12 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
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.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;
|
||||
|
@ -61,8 +67,8 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
private static final FhirContext ourCtx = FhirContext.forR4Cached();
|
||||
@RegisterExtension
|
||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
|
||||
private IValidateCodeProvidersR4.MyValueSetProviderR4 myValueSetProvider;
|
||||
private IValidationProviders.IMyValidationProvider myCodeSystemProvider;
|
||||
private IValidationProviders.IMyValidationProvider myValueSetProvider;
|
||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||
private String myCodeSystemError, myValueSetError;
|
||||
|
||||
|
@ -71,14 +77,14 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
myCodeSystemError = ourCtx.getLocalizer().getMessage(
|
||||
RemoteTerminologyServiceValidationSupport.class,
|
||||
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, baseUrl, IValidationProviders.ERROR_MESSAGE);
|
||||
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, CODE_SYSTEM, CODE, baseUrl, ERROR_MESSAGE);
|
||||
myValueSetError = ourCtx.getLocalizer().getMessage(
|
||||
RemoteTerminologyServiceValidationSupport.class,
|
||||
ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.VALUE_SET_URL, baseUrl, IValidationProviders.ERROR_MESSAGE);
|
||||
ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, CODE_SYSTEM, CODE, VALUE_SET_URL, baseUrl, ERROR_MESSAGE);
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
||||
myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
|
||||
myValueSetProvider = new IValidateCodeProvidersR4.MyValueSetProviderR4();
|
||||
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
|
||||
myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4();
|
||||
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider);
|
||||
}
|
||||
|
||||
|
@ -95,12 +101,12 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
}
|
||||
|
||||
@Override
|
||||
public IValidationProviders.IMyCodeSystemProvider getCodeSystemProvider() {
|
||||
public IValidationProviders.IMyValidationProvider getCodeSystemProvider() {
|
||||
return myCodeSystemProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IValidationProviders.IMyValueSetProvider getValueSetProvider() {
|
||||
public IValidationProviders.IMyValidationProvider getValueSetProvider() {
|
||||
return myValueSetProvider;
|
||||
}
|
||||
|
||||
|
@ -116,51 +122,40 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
|
||||
@Override
|
||||
public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() {
|
||||
return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
|
||||
return getCodeSystemInvalidCodeOutcome(OperationOutcome.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseOperationOutcome getValueSetInvalidCodeOutcome() {
|
||||
return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
|
||||
return getValueSetInvalidCodeOutcome(OperationOutcome.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IValidationSupport.CodeValidationIssue> getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome) {
|
||||
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();
|
||||
public IBaseOperationOutcome getValueSetCustomDetailCodeOutcome() {
|
||||
return getValueSetCustomDetailCodeOutcome(OperationOutcome.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateCodeInValueSet_success() {
|
||||
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||
createValueSetReturnParameters(true, DISPLAY, null, null);
|
||||
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||
valueSet.setUrl(VALUE_SET_URL);
|
||||
|
||||
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), CODE_SYSTEM, CODE, DISPLAY, valueSet);
|
||||
assertNotNull(outcome);
|
||||
assertEquals(IValidationProviders.CODE, outcome.getCode());
|
||||
assertEquals(IValidationProviders.DISPLAY, outcome.getDisplay());
|
||||
assertEquals(CODE, outcome.getCode());
|
||||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
|
||||
assertEquals(IValidationProviders.CODE, myValueSetProvider.getCode());
|
||||
assertEquals(IValidationProviders.DISPLAY, myValueSetProvider.getDisplay());
|
||||
assertEquals(IValidationProviders.VALUE_SET_URL, myValueSetProvider.getValueSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
|
||||
Parameters parameters = new Parameters()
|
||||
.addParameter("code", IValidationProviders.CODE)
|
||||
.addParameter("system", IValidationProviders.CODE_SYSTEM)
|
||||
.addParameter("version", IValidationProviders.CODE_SYSTEM_VERSION)
|
||||
.addParameter("code", CODE)
|
||||
.addParameter("system", CODE_SYSTEM)
|
||||
.addParameter("version", CODE_SYSTEM_VERSION)
|
||||
.addParameter("display", theDisplay)
|
||||
.addParameter("message", theMessage);
|
||||
if (theResult != null) {
|
||||
|
@ -181,16 +176,16 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
|
||||
@Test
|
||||
void validateCodeInValueSet_uniqueComposeInclude() {
|
||||
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||
createValueSetReturnParameters(true, DISPLAY, null, null);
|
||||
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||
valueSet.setUrl(VALUE_SET_URL);
|
||||
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
||||
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
||||
Collections.singletonList(new ValueSet.ConceptSetComponent().setSystem(systemUrl)) ));
|
||||
|
||||
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
||||
|
||||
// validate service doesn't return error message (as when no code system is present)
|
||||
assertNotNull(outcome);
|
||||
|
@ -211,16 +206,16 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
@ParameterizedTest
|
||||
@MethodSource(value = "getRemoteTerminologyServerExceptions")
|
||||
void validateCodeInValueSet_systemNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
|
||||
myValueSetProvider.setException(theException);
|
||||
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||
getValueSetProvider().addException("$validate-code", VALUE_SET_URL, CODE, theException);
|
||||
createValueSetReturnParameters(true, DISPLAY, null, null);
|
||||
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||
valueSet.setUrl(VALUE_SET_URL);
|
||||
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
||||
Lists.newArrayList(new ValueSet.ConceptSetComponent(), new ValueSet.ConceptSetComponent())));
|
||||
|
||||
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
||||
|
||||
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
|
||||
verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage);
|
||||
|
@ -230,11 +225,11 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
@ParameterizedTest
|
||||
@MethodSource(value = "getRemoteTerminologyServerExceptions")
|
||||
void validateCodeInValueSet_systemPresentCodeNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
|
||||
myValueSetProvider.setException(theException);
|
||||
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||
getValueSetProvider().addException(JpaConstants.OPERATION_VALIDATE_CODE, VALUE_SET_URL, CODE, theException);
|
||||
createValueSetReturnParameters(true, DISPLAY, null, null);
|
||||
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||
valueSet.setUrl(VALUE_SET_URL);
|
||||
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
||||
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
|
||||
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
||||
|
@ -243,7 +238,7 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
new ValueSet.ConceptSetComponent().setSystem(systemUrl2))));
|
||||
|
||||
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
||||
|
||||
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
|
||||
verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage);
|
||||
|
@ -252,10 +247,10 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
|
||||
@Test
|
||||
void validateCodeInValueSet_systemPresentCodePresentValidatesOKNoVersioned() {
|
||||
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||
createValueSetReturnParameters(true, DISPLAY, null, null);
|
||||
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||
valueSet.setUrl(VALUE_SET_URL);
|
||||
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
||||
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
|
||||
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
||||
|
@ -264,14 +259,14 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setConcept(
|
||||
Lists.newArrayList(
|
||||
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
|
||||
new ValueSet.ConceptReferenceComponent().setCode(IValidationProviders.CODE) )
|
||||
new ValueSet.ConceptReferenceComponent().setCode(CODE) )
|
||||
)) ));
|
||||
|
||||
TestClientInterceptor requestInterceptor = new TestClientInterceptor();
|
||||
mySvc.addClientInterceptor(requestInterceptor);
|
||||
|
||||
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
||||
|
||||
assertNotNull(outcome);
|
||||
assertEquals(systemUrl2, requestInterceptor.getCapturedSystemParameter());
|
||||
|
@ -280,10 +275,10 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
|
||||
@Test
|
||||
void validateCodeInValueSet_systemPresentCodePresentValidatesOKVersioned() {
|
||||
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||
createValueSetReturnParameters(true, DISPLAY, null, null);
|
||||
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||
valueSet.setUrl(VALUE_SET_URL);
|
||||
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
||||
String systemVersion = "3.0.2";
|
||||
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
|
||||
|
@ -294,14 +289,14 @@ public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyVa
|
|||
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setVersion(system2Version).setConcept(
|
||||
Lists.newArrayList(
|
||||
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
|
||||
new ValueSet.ConceptReferenceComponent().setCode(IValidationProviders.CODE) )
|
||||
new ValueSet.ConceptReferenceComponent().setCode(CODE) )
|
||||
)) ));
|
||||
|
||||
TestClientInterceptor requestInterceptor = new TestClientInterceptor();
|
||||
mySvc.addClientInterceptor(requestInterceptor);
|
||||
|
||||
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
||||
|
||||
assertNotNull(outcome);
|
||||
assertEquals(systemUrl2 + "|" + system2Version, requestInterceptor.getCapturedSystemParameter());
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.hl7.fhir.exceptions.FHIRException;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4b.conformance.ProfileUtilities;
|
||||
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.model.AllergyIntolerance;
|
||||
import org.hl7.fhir.r4b.model.Base;
|
||||
|
@ -61,7 +62,6 @@ import org.hl7.fhir.r4b.model.StructureDefinition.StructureDefinitionKind;
|
|||
import org.hl7.fhir.r4b.model.ValueSet;
|
||||
import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionComponent;
|
||||
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.utils.validation.IValidationPolicyAdvisor;
|
||||
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
|
||||
|
@ -203,7 +203,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo
|
|||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
final String message = "Unknown code (for '" + system + "#" + 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)));
|
||||
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)));
|
||||
} else {
|
||||
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
|
|||
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.ReferenceValidationPolicy;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -200,10 +199,10 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
|
|||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
String theMessage = "Unknown code (for '" + system + "#" + 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)));
|
||||
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)));
|
||||
} else if (myValidSystemsNotReturningIssues.contains(system)) {
|
||||
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
||||
retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
|
||||
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(message);
|
||||
} else {
|
||||
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"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]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue