validate code on remote terminology service returns nullpointerexception (#4958)
* validate-code on remote-terminology service returns NullPointerException - tests * validate-code on remote-terminology service returns NullPointerException - fixed * validate-code on remote-terminology service returns NullPointerException - added changelog * validate-code on remote-terminology service returns NullPointerException - tests readability improvement and fixes * validate-code on remote-terminology service returns NullPointerException - fixes
This commit is contained in:
parent
fddea8db92
commit
737238b97d
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 4957
|
||||
title: "Previously, performing a $validate-code operation with a remote terminology service on an invalid CodeSystem or ValueSet returned 500. This problem has been fixed."
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.provider;
|
|||
|
||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||
|
@ -41,6 +42,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static ca.uhn.fhir.jpa.provider.ValueSetOperationProvider.toValidateCodeResult;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -141,7 +144,7 @@ public abstract class BaseJpaResourceProviderCodeSystem<T extends IBaseResource>
|
|||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
IValidationSupport.CodeValidationResult result = null;
|
||||
CodeValidationResult result = null;
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
// TODO: JA why not just always just the chain here? and we can then get rid of the corresponding DAO method entirely
|
||||
|
@ -159,9 +162,8 @@ public abstract class BaseJpaResourceProviderCodeSystem<T extends IBaseResource>
|
|||
String code = theCoding.getCode();
|
||||
String display = theCoding.getDisplay();
|
||||
|
||||
result = myValidationSupportChain.validateCode(
|
||||
new ValidationSupportContext(myValidationSupportChain), new ConceptValidationOptions(),
|
||||
codeSystemUrl, code, display, null);
|
||||
result = validateCodeWithTerminologyService(codeSystemUrl, code, display)
|
||||
.orElseGet(supplyUnableToValidateResult(codeSystemUrl, code));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -175,4 +177,13 @@ public abstract class BaseJpaResourceProviderCodeSystem<T extends IBaseResource>
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
private Optional<CodeValidationResult> validateCodeWithTerminologyService(String theCodeSystemUrl, String theCode, String theDisplay) {
|
||||
return Optional.ofNullable(myValidationSupportChain.validateCode(new ValidationSupportContext(myValidationSupportChain),
|
||||
new ConceptValidationOptions(), theCodeSystemUrl, theCode, theDisplay, null));
|
||||
}
|
||||
|
||||
private Supplier<CodeValidationResult> supplyUnableToValidateResult(String theCodeSystemUrl, String theCode) {
|
||||
return () -> new CodeValidationResult().setMessage("Terminology service was unable to provide validation for " + theCodeSystemUrl + "#" + theCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.provider;
|
|||
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.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
|
@ -53,6 +54,9 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
|||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class ValueSetOperationProvider extends BaseJpaProvider {
|
||||
|
@ -128,7 +132,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
IValidationSupport.CodeValidationResult result;
|
||||
CodeValidationResult result;
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
// If a Remote Terminology Server has been configured, use it
|
||||
|
@ -138,8 +142,20 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
String theDisplayString = (theDisplay != null && theDisplay.hasValue()) ? theDisplay.getValueAsString() : null;
|
||||
String theValueSetUrlString = (theValueSetUrl != null && theValueSetUrl.hasValue()) ?
|
||||
theValueSetUrl.getValueAsString() : null;
|
||||
result = myValidationSupportChain.validateCode(new ValidationSupportContext(myValidationSupportChain),
|
||||
new ConceptValidationOptions(), theSystemString, theCodeString, theDisplayString, theValueSetUrlString);
|
||||
if (theCoding != null) {
|
||||
if (isNotBlank(theCoding.getSystem())) {
|
||||
if (theSystemString != null && !theSystemString.equalsIgnoreCase(theCoding.getSystem())) {
|
||||
throw new InvalidRequestException(Msg.code(2352) + "Coding.system '" + theCoding.getSystem() +
|
||||
"' does not equal param system '" + theSystemString + "'. Unable to validate-code.");
|
||||
}
|
||||
theSystemString = theCoding.getSystem();
|
||||
theCodeString = theCoding.getCode();
|
||||
theDisplayString = theCoding.getDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
result = validateCodeWithTerminologyService(theSystemString, theCodeString, theDisplayString, theValueSetUrlString)
|
||||
.orElseGet(supplyUnableToValidateResult(theSystemString, theCodeString, theValueSetUrlString));
|
||||
} else {
|
||||
// Otherwise, use the local DAO layer to validate the code
|
||||
IFhirResourceDaoValueSet<IBaseResource> dao = getDao();
|
||||
|
@ -165,6 +181,17 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
}
|
||||
}
|
||||
|
||||
private Optional<CodeValidationResult> validateCodeWithTerminologyService(String theSystem, String theCode,
|
||||
String theDisplay, String theValueSetUrl) {
|
||||
return Optional.ofNullable(myValidationSupportChain.validateCode(new ValidationSupportContext(myValidationSupportChain),
|
||||
new ConceptValidationOptions(), theSystem, theCode, theDisplay, theValueSetUrl));
|
||||
}
|
||||
|
||||
private Supplier<CodeValidationResult> supplyUnableToValidateResult(String theSystem, String theCode, String theValueSetUrl) {
|
||||
return () -> new CodeValidationResult().setMessage("Validator is unable to provide validation for " +
|
||||
theCode + "#" + theSystem + " - Unknown or unusable ValueSet[" + theValueSetUrl + "]");
|
||||
}
|
||||
|
||||
@Operation(name = ProviderConstants.OPERATION_INVALIDATE_EXPANSION, idempotent = false, typeName = "ValueSet", returnParameters = {
|
||||
@OperationParam(name = "message", typeName = "string", min = 1, max = 1)
|
||||
})
|
||||
|
@ -228,7 +255,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
return options;
|
||||
}
|
||||
|
||||
public static IBaseParameters toValidateCodeResult(FhirContext theContext, IValidationSupport.CodeValidationResult theResult) {
|
||||
public static IBaseParameters toValidateCodeResult(FhirContext theContext, CodeValidationResult theResult) {
|
||||
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
|
||||
|
||||
ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "result", theResult.isOk());
|
||||
|
|
|
@ -37,7 +37,9 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/*
|
||||
* This set of Unit Tests instantiates and injects an instance of
|
||||
|
@ -51,6 +53,9 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
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";
|
||||
private static final String CODE_SYSTEM_V2_0247_URI = "http://terminology.hl7.org/CodeSystem/v2-0247";
|
||||
private static final String INVALID_CODE_SYSTEM_URI = "http://terminology.hl7.org/CodeSystem/INVALID-CODESYSTEM";
|
||||
private static final String UNKNOWN_VALUE_SYSTEM_URI = "http://hl7.org/fhir/ValueSet/unknown-value-set";
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private MyCodeSystemProvider myCodeSystemProvider = new MyCodeSystemProvider();
|
||||
private MyValueSetProvider myValueSetProvider = new MyValueSetProvider();
|
||||
|
@ -79,20 +84,20 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnCodeSystem_ByCodingAndUrlWhereSystemIsDifferent_ThrowsException() {
|
||||
public void testValidateCodeOperationOnCodeSystem_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
||||
assertThrows(InvalidRequestException.class, () -> {
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://terminology.hl7.org/CodeSystem/v2-0247").setCode("P"))
|
||||
.andParameter("url", new UriType("http://terminology.hl7.org/CodeSystem/INVALID-CODESYSTEM"))
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
|
||||
.andParameter("url", new UriType(INVALID_CODE_SYSTEM_URI))
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnCodeSystem_ByCodingAndUrl_UsingBuiltInCodeSystems() {
|
||||
public void testValidateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
|
||||
createNextCodeSystemReturnParameters(true, DISPLAY, null);
|
||||
|
@ -103,8 +108,8 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://terminology.hl7.org/CodeSystem/v2-0247").setCode("P"))
|
||||
.andParameter("url", new UriType("http://terminology.hl7.org/CodeSystem/v2-0247"))
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
|
||||
.andParameter("url", new UriType(CODE_SYSTEM_V2_0247_URI))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
|
@ -115,7 +120,45 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_ByUrlAndSystem_UsingBuiltInCodeSystems() {
|
||||
public void testValidateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding()
|
||||
.setSystem(INVALID_CODE_SYSTEM_URI).setCode("P"))
|
||||
.andParameter("url", new UriType(INVALID_CODE_SYSTEM_URI))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertFalse(((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
||||
assertEquals("Terminology service was unable to provide validation for " + INVALID_CODE_SYSTEM_URI +
|
||||
"#P", respParam.getParameterValue("message").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
||||
try {
|
||||
myClient.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
|
||||
.andParameter("url", new UriType("http://hl7.org/fhir/ValueSet/list-example-codes"))
|
||||
.andParameter("system", new UriType(INVALID_CODE_SYSTEM_URI))
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException exception) {
|
||||
assertEquals("HTTP 400 Bad Request: HAPI-2352: Coding.system '" + CODE_SYSTEM_V2_0247_URI + "' " +
|
||||
"does not equal param system '" + INVALID_CODE_SYSTEM_URI + "'. Unable to validate-code.", exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
|
@ -140,7 +183,7 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_ByUrlSystemAndCode() {
|
||||
public void testValidateCodeOperationOnValueSet_byUrlSystemAndCode() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
|
@ -163,6 +206,27 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
assertEquals(DISPLAY_BODY_MASS_INDEX, respParam.getParameterValue("display").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() {
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding()
|
||||
.setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
|
||||
.andParameter("url", new UriType(UNKNOWN_VALUE_SYSTEM_URI))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertFalse(((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
||||
assertEquals("Validator is unable to provide validation for P#" + CODE_SYSTEM_V2_0247_URI +
|
||||
" - Unknown or unusable ValueSet[" + UNKNOWN_VALUE_SYSTEM_URI + "]", respParam.getParameterValue("message").toString());
|
||||
}
|
||||
|
||||
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
myCodeSystemProvider.myNextReturnParams = new Parameters();
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
|
||||
|
|
Loading…
Reference in New Issue