Make display name validation configurable (#5321)
* Test fixes * Build cleanup * Initial test passing * Test fixes * Tests all seem to be working * Make display validation level configurable * Should be all working * Add changelog * Add to changelog --------- Co-authored-by: Tadgh <garygrantgraham@gmail.com>
This commit is contained in:
parent
50d2eac86f
commit
2b7ebbcb27
|
@ -553,11 +553,29 @@ public interface IValidationSupport {
|
|||
private String myCodeSystemVersion;
|
||||
private List<BaseConceptProperty> myProperties;
|
||||
private String myDisplay;
|
||||
private String mySourceDetails;
|
||||
|
||||
public CodeValidationResult() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* This field may contain information about what the source of the
|
||||
* validation information was.
|
||||
*/
|
||||
public String getSourceDetails() {
|
||||
return mySourceDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* This field may contain information about what the source of the
|
||||
* validation information was.
|
||||
*/
|
||||
public CodeValidationResult setSourceDetails(String theSourceDetails) {
|
||||
mySourceDetails = theSourceDetails;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDisplay() {
|
||||
return myDisplay;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
|
||||
org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport.displayMismatch=Concept Display "{0}" does not match expected "{1}"
|
||||
|
||||
|
||||
ca.uhn.fhir.jpa.term.TermReadSvcImpl.expansionRefersToUnknownCs=Unknown CodeSystem URI "{0}" referenced from ValueSet
|
||||
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetNotYetExpanded=ValueSet "{0}" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: {1} | {2}
|
||||
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetNotYetExpanded_OffsetNotAllowed=ValueSet expansion can not combine "offset" with "ValueSet.compose.exclude" unless the ValueSet has been pre-expanded. ValueSet "{0}" must be pre-expanded for this operation to work.
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: add
|
||||
issue: 5321
|
||||
title: "It is now possible to configure the strictness of concept display name validation
|
||||
using a new flag on the InMemoryTerminologyServerValidationSupport (for non-JPA validation)
|
||||
and JpaStorageSettings (for JPA validation). In addition, the error messages emitted by
|
||||
the validator when a concept display doesn't match have been improved to be much
|
||||
more useful."
|
|
@ -1,3 +1,22 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.config;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.svc.IDeleteExpungeSvc;
|
||||
|
|
|
@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.dao.JpaPersistedResourceValidationSupport;
|
||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain;
|
||||
|
@ -30,6 +31,7 @@ import ca.uhn.fhir.jpa.validation.ValidatorPolicyAdvisor;
|
|||
import ca.uhn.fhir.jpa.validation.ValidatorResourceFetcher;
|
||||
import ca.uhn.fhir.validation.IInstanceValidatorModule;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.HapiToHl7OrgDstu2ValidatingSupportWrapper;
|
||||
|
@ -45,6 +47,15 @@ public class ValidationSupportConfig {
|
|||
return new DefaultProfileValidationSupport(theFhirContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InMemoryTerminologyServerValidationSupport inMemoryTerminologyServerValidationSupport(
|
||||
FhirContext theFhirContext, JpaStorageSettings theStorageSettings) {
|
||||
InMemoryTerminologyServerValidationSupport retVal =
|
||||
new InMemoryTerminologyServerValidationSupport(theFhirContext);
|
||||
retVal.setIssueSeverityForCodeDisplayMismatch(theStorageSettings.getIssueSeverityForCodeDisplayMismatch());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Bean(name = JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||
public JpaValidationSupportChain jpaValidationSupportChain(FhirContext theFhirContext) {
|
||||
return new JpaValidationSupportChain(theFhirContext);
|
||||
|
|
|
@ -61,6 +61,10 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
public class ValueSetOperationProvider extends BaseJpaProvider {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ValueSetOperationProvider.class);
|
||||
public static final String SOURCE_DETAILS = "sourceDetails";
|
||||
public static final String RESULT = "result";
|
||||
public static final String MESSAGE = "message";
|
||||
public static final String DISPLAY = "display";
|
||||
|
||||
@Autowired
|
||||
protected IValidationSupport myValidationSupport;
|
||||
|
@ -145,9 +149,10 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
idempotent = true,
|
||||
typeName = "ValueSet",
|
||||
returnParameters = {
|
||||
@OperationParam(name = "result", typeName = "boolean", min = 1),
|
||||
@OperationParam(name = "message", typeName = "string"),
|
||||
@OperationParam(name = "display", typeName = "string")
|
||||
@OperationParam(name = RESULT, typeName = "boolean", min = 1),
|
||||
@OperationParam(name = MESSAGE, typeName = "string"),
|
||||
@OperationParam(name = DISPLAY, typeName = "string"),
|
||||
@OperationParam(name = SOURCE_DETAILS, typeName = "string")
|
||||
})
|
||||
public IBaseParameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
|
@ -159,7 +164,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
@OperationParam(name = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theSystem,
|
||||
@OperationParam(name = "systemVersion", min = 0, max = 1, typeName = "string")
|
||||
IPrimitiveType<String> theSystemVersion,
|
||||
@OperationParam(name = "display", min = 0, max = 1, typeName = "string") IPrimitiveType<String> theDisplay,
|
||||
@OperationParam(name = DISPLAY, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theDisplay,
|
||||
@OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding,
|
||||
@OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept")
|
||||
ICompositeType theCodeableConcept,
|
||||
|
@ -251,7 +256,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
name = ProviderConstants.OPERATION_INVALIDATE_EXPANSION,
|
||||
idempotent = false,
|
||||
typeName = "ValueSet",
|
||||
returnParameters = {@OperationParam(name = "message", typeName = "string", min = 1, max = 1)})
|
||||
returnParameters = {@OperationParam(name = MESSAGE, typeName = "string", min = 1, max = 1)})
|
||||
public IBaseParameters invalidateValueSetExpansion(
|
||||
@IdParam IIdType theValueSetId, RequestDetails theRequestDetails, HttpServletRequest theServletRequest) {
|
||||
startRequest(theServletRequest);
|
||||
|
@ -260,7 +265,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
String outcome = myTermReadSvc.invalidatePreCalculatedExpansion(theValueSetId, theRequestDetails);
|
||||
|
||||
IBaseParameters retVal = ParametersUtil.newInstance(getContext());
|
||||
ParametersUtil.addParameterToParametersString(getContext(), retVal, "message", outcome);
|
||||
ParametersUtil.addParameterToParametersString(getContext(), retVal, MESSAGE, outcome);
|
||||
return retVal;
|
||||
|
||||
} finally {
|
||||
|
@ -325,12 +330,16 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
public static IBaseParameters toValidateCodeResult(FhirContext theContext, CodeValidationResult theResult) {
|
||||
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
|
||||
|
||||
ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "result", theResult.isOk());
|
||||
ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, theResult.isOk());
|
||||
if (isNotBlank(theResult.getMessage())) {
|
||||
ParametersUtil.addParameterToParametersString(theContext, retVal, "message", theResult.getMessage());
|
||||
ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, theResult.getMessage());
|
||||
}
|
||||
if (isNotBlank(theResult.getDisplay())) {
|
||||
ParametersUtil.addParameterToParametersString(theContext, retVal, "display", theResult.getDisplay());
|
||||
ParametersUtil.addParameterToParametersString(theContext, retVal, DISPLAY, theResult.getDisplay());
|
||||
}
|
||||
if (isNotBlank(theResult.getSourceDetails())) {
|
||||
ParametersUtil.addParameterToParametersString(
|
||||
theContext, retVal, SOURCE_DETAILS, theResult.getSourceDetails());
|
||||
}
|
||||
|
||||
return retVal;
|
||||
|
|
|
@ -295,6 +295,9 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
@Autowired
|
||||
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||
|
||||
@Autowired
|
||||
private InMemoryTerminologyServerValidationSupport myInMemoryTerminologyServerValidationSupport;
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
||||
TermCodeSystemVersionDetails cs = getCurrentCodeSystemVersion(theSystem);
|
||||
|
@ -1025,11 +1028,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
new VersionConvertor_40_50(new BaseAdvisor_40_50()), "ValueSet");
|
||||
org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent includeOrExclude =
|
||||
ValueSet40_50.convertConceptSetComponent(theIncludeOrExclude);
|
||||
new InMemoryTerminologyServerValidationSupport(myContext)
|
||||
.expandValueSetIncludeOrExclude(
|
||||
new ValidationSupportContext(provideValidationSupport()),
|
||||
consumer,
|
||||
includeOrExclude);
|
||||
myInMemoryTerminologyServerValidationSupport.expandValueSetIncludeOrExclude(
|
||||
new ValidationSupportContext(provideValidationSupport()), consumer, includeOrExclude);
|
||||
} catch (InMemoryTerminologyServerValidationSupport.ExpansionCouldNotBeCompletedInternallyException e) {
|
||||
if (theExpansionOptions != null
|
||||
&& !theExpansionOptions.isFailOnMissingCodeSystem()
|
||||
|
@ -2055,7 +2055,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
.findByResourcePid(valueSetResourcePid.getId())
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
String timingDescription = toHumanReadableExpansionTimestamp(valueSetEntity);
|
||||
String msg = myContext
|
||||
String preExpansionMessage = myContext
|
||||
.getLocalizer()
|
||||
.getMessage(TermReadSvcImpl.class, "validationPerformedAgainstPreExpansion", timingDescription);
|
||||
|
||||
|
@ -2068,14 +2068,18 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
.setCode(concept.getCode())
|
||||
.setDisplay(concept.getDisplay())
|
||||
.setCodeSystemVersion(concept.getSystemVersion())
|
||||
.setMessage(msg);
|
||||
.setSourceDetails(preExpansionMessage);
|
||||
}
|
||||
}
|
||||
|
||||
String expectedDisplay = concepts.get(0).getDisplay();
|
||||
String append = createMessageAppendForDisplayMismatch(theSystem, theDisplay, expectedDisplay) + " - " + msg;
|
||||
return createFailureCodeValidationResult(theSystem, theCode, systemVersion, append)
|
||||
.setDisplay(expectedDisplay);
|
||||
return InMemoryTerminologyServerValidationSupport.createResultForDisplayMismatch(
|
||||
myContext,
|
||||
theCode,
|
||||
theDisplay,
|
||||
expectedDisplay,
|
||||
systemVersion,
|
||||
myStorageSettings.getIssueSeverityForCodeDisplayMismatch());
|
||||
}
|
||||
|
||||
if (!concepts.isEmpty()) {
|
||||
|
@ -2083,7 +2087,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
.setCode(concepts.get(0).getCode())
|
||||
.setDisplay(concepts.get(0).getDisplay())
|
||||
.setCodeSystemVersion(concepts.get(0).getSystemVersion())
|
||||
.setMessage(msg);
|
||||
.setMessage(preExpansionMessage);
|
||||
}
|
||||
|
||||
// Ok, we failed
|
||||
|
@ -2096,7 +2100,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
String unknownCodeMessage = myContext
|
||||
.getLocalizer()
|
||||
.getMessage(TermReadSvcImpl.class, "unknownCodeInSystem", theSystem, theCode);
|
||||
append = " - " + unknownCodeMessage + ". " + msg;
|
||||
append = " - " + unknownCodeMessage + ". " + preExpansionMessage;
|
||||
}
|
||||
|
||||
return createFailureCodeValidationResult(theSystem, theCode, null, append);
|
||||
|
@ -2710,11 +2714,13 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
|| code.getDisplay().equals(theDisplay)) {
|
||||
return new CodeValidationResult().setCode(code.getCode()).setDisplay(code.getDisplay());
|
||||
} else {
|
||||
String messageAppend =
|
||||
createMessageAppendForDisplayMismatch(theCodeSystemUrl, theDisplay, code.getDisplay());
|
||||
return createFailureCodeValidationResult(
|
||||
theCodeSystemUrl, theCode, code.getSystemVersion(), messageAppend)
|
||||
.setDisplay(code.getDisplay());
|
||||
return InMemoryTerminologyServerValidationSupport.createResultForDisplayMismatch(
|
||||
myContext,
|
||||
theCode,
|
||||
theDisplay,
|
||||
code.getDisplay(),
|
||||
code.getSystemVersion(),
|
||||
myStorageSettings.getIssueSeverityForCodeDisplayMismatch());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2752,14 +2758,13 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
|
||||
if (retVal == null) {
|
||||
if (valueSet != null) {
|
||||
retVal = new InMemoryTerminologyServerValidationSupport(myContext)
|
||||
.validateCodeInValueSet(
|
||||
theValidationSupportContext,
|
||||
theValidationOptions,
|
||||
theCodeSystem,
|
||||
theCode,
|
||||
theDisplay,
|
||||
valueSet);
|
||||
retVal = myInMemoryTerminologyServerValidationSupport.validateCodeInValueSet(
|
||||
theValidationSupportContext,
|
||||
theValidationOptions,
|
||||
theCodeSystem,
|
||||
theCode,
|
||||
theDisplay,
|
||||
valueSet);
|
||||
} else {
|
||||
String append = " - Unable to locate ValueSet[" + theValueSetUrl + "]";
|
||||
retVal = createFailureCodeValidationResult(theCodeSystem, theCode, null, append);
|
||||
|
@ -3182,13 +3187,6 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
return theExpansionOptions.getTheDisplayLanguage().equalsIgnoreCase(theStoredLang);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String createMessageAppendForDisplayMismatch(
|
||||
String theCodeSystemUrl, String theDisplay, String theExpectedDisplay) {
|
||||
return " - Concept Display \"" + theDisplay + "\" does not match expected \"" + theExpectedDisplay
|
||||
+ "\" for CodeSystem: " + theCodeSystemUrl;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String createMessageAppendForCodeNotFoundInCodeSystem(String theCodeSystemUrl) {
|
||||
return " - Code is not found in CodeSystem: " + theCodeSystemUrl;
|
||||
|
|
|
@ -59,6 +59,9 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
|||
@Autowired
|
||||
private UnknownCodeSystemWarningValidationSupport myUnknownCodeSystemWarningValidationSupport;
|
||||
|
||||
@Autowired
|
||||
private InMemoryTerminologyServerValidationSupport myInMemoryTerminologyServerValidationSupport;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -82,7 +85,7 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
|||
addValidationSupport(myJpaValidationSupport);
|
||||
addValidationSupport(myTerminologyService);
|
||||
addValidationSupport(new SnapshotGeneratingValidationSupport(myFhirContext));
|
||||
addValidationSupport(new InMemoryTerminologyServerValidationSupport(myFhirContext));
|
||||
addValidationSupport(myInMemoryTerminologyServerValidationSupport);
|
||||
addValidationSupport(myNpmJpaValidationSupport);
|
||||
addValidationSupport(new CommonCodeSystemsTerminologyService(myFhirContext));
|
||||
addValidationSupport(myConceptMappingSvc);
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server - Master Data Management
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.mdm.config;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
|
|
@ -103,9 +103,10 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
|
|||
CodingDt coding = null;
|
||||
CodeableConceptDt codeableConcept = null;
|
||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
||||
assertFalse(result.isOk());
|
||||
assertTrue(result.isOk());
|
||||
assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage());
|
||||
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||
assertEquals(IValidationSupport.IssueSeverity.WARNING, result.getSeverity());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -226,7 +226,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
|
|||
Coding coding = null;
|
||||
CodeableConcept codeableConcept = null;
|
||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
||||
assertFalse(result.isOk());
|
||||
assertTrue(result.isOk());
|
||||
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.entity.TermConcept;
|
|||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -789,14 +790,14 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("result", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName());
|
||||
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -819,14 +820,15 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("result", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName());
|
||||
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
|
||||
assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -34,6 +34,7 @@ import ca.uhn.fhir.validation.IValidatorModule;
|
|||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.UnknownCodeSystemWarningValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
|
@ -49,6 +50,9 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
|||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
|
||||
|
@ -85,15 +89,21 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
private ValidationSettings myValidationSettings;
|
||||
@Autowired
|
||||
private UnknownCodeSystemWarningValidationSupport myUnknownCodeSystemWarningValidationSupport;
|
||||
@Autowired
|
||||
private InMemoryTerminologyServerValidationSupport myInMemoryTerminologyServerValidationSupport;
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
|
||||
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
|
||||
|
||||
myStorageSettings.setAllowExternalReferences(new JpaStorageSettings().isAllowExternalReferences());
|
||||
myStorageSettings.setMaximumExpansionSize(JpaStorageSettings.DEFAULT_MAX_EXPANSION_SIZE);
|
||||
myStorageSettings.setPreExpandValueSets(new JpaStorageSettings().isPreExpandValueSets());
|
||||
JpaStorageSettings defaults = new JpaStorageSettings();
|
||||
myStorageSettings.setAllowExternalReferences(defaults.isAllowExternalReferences());
|
||||
myStorageSettings.setMaximumExpansionSize(defaults.getMaximumExpansionSize());
|
||||
myStorageSettings.setPreExpandValueSets(defaults.isPreExpandValueSets());
|
||||
myStorageSettings.setIssueSeverityForCodeDisplayMismatch(defaults.getIssueSeverityForCodeDisplayMismatch());
|
||||
|
||||
myInMemoryTerminologyServerValidationSupport.setIssueSeverityForCodeDisplayMismatch(defaults.getIssueSeverityForCodeDisplayMismatch());
|
||||
|
||||
TermReadSvcImpl.setInvokeOnNextCallForUnitTest(null);
|
||||
|
||||
|
@ -125,7 +135,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
ourLog.info(encoded);
|
||||
assertEquals(1, oo.getIssue().size(), encoded);
|
||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
||||
containsString("The code provided (http://cs#code99) is not in the value set"));
|
||||
containsString("provided (http://cs#code99) is not in the value set"));
|
||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
||||
containsString("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"));
|
||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity(), encoded);
|
||||
|
@ -159,7 +169,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
ourLog.info(encoded);
|
||||
assertEquals(1, oo.getIssue().size());
|
||||
assertThat(oo.getIssueFirstRep().getDiagnostics(),
|
||||
containsString("The code provided (http://cs#code99) is not in the value set"));
|
||||
containsString("provided (http://cs#code99) is not in the value set"));
|
||||
assertThat(oo.getIssueFirstRep().getDiagnostics(),
|
||||
containsString("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"));
|
||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity());
|
||||
|
@ -199,7 +209,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
containsString("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"));
|
||||
assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssue().get(0).getSeverity());
|
||||
assertThat(oo.getIssue().get(1).getDiagnostics(),
|
||||
containsString("The code provided (http://cs#code99) is not in the value set 'ValueSet[http://vs]'"));
|
||||
containsString("provided (http://cs#code99) is not in the value set 'ValueSet[http://vs]'"));
|
||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(1).getSeverity());
|
||||
}
|
||||
|
||||
|
@ -239,7 +249,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
ourLog.info(encoded);
|
||||
assertEquals(1, oo.getIssue().size());
|
||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
||||
containsString("The code provided (http://cs#code99) is not in the value set"));
|
||||
containsString("provided (http://cs#code99) is not in the value set"));
|
||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity());
|
||||
}
|
||||
|
||||
|
@ -335,7 +345,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
ourLog.info(encoded);
|
||||
assertEquals(1, oo.getIssue().size());
|
||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
||||
containsString("The code provided (http://cs#code1) is not in the value set"));
|
||||
containsString("provided (http://cs#code1) is not in the value set"));
|
||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
||||
containsString("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code1"));
|
||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
||||
|
@ -343,7 +353,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity());
|
||||
assertEquals(27, ((IntegerType)oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line").getValue()).getValue());
|
||||
assertEquals(4, ((IntegerType)oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col").getValue()).getValue());
|
||||
assertEquals("Terminology_TX_Confirm_4a", ((StringType)oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id").getValue()).getValue());
|
||||
assertEquals("Terminology_TX_NoValid_12", ((StringType)oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id").getValue()).getValue());
|
||||
assertEquals(OperationOutcome.IssueType.PROCESSING, oo.getIssue().get(0).getCode());
|
||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity());
|
||||
assertEquals(2, oo.getIssue().get(0).getLocation().size());
|
||||
|
@ -509,7 +519,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
String outcomeStr = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
assertThat(outcomeStr,
|
||||
containsString("The code provided (http://unitsofmeasure.org#cm) is not in the value set"));
|
||||
containsString("provided (http://unitsofmeasure.org#cm) is not in the value set"));
|
||||
|
||||
// Before, the VS wasn't pre-expanded. Try again with it pre-expanded
|
||||
runInTransaction(() -> {
|
||||
|
@ -538,7 +548,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
outcomeStr = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||
assertThat(outcomeStr,
|
||||
containsString("The code provided (http://unitsofmeasure.org#cm) is not in the value set"));
|
||||
containsString("provided (http://unitsofmeasure.org#cm) is not in the value set"));
|
||||
|
||||
}
|
||||
|
||||
|
@ -1364,7 +1374,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateUsingExternallyDefinedCodeMisMatchDisplay_ShouldError() {
|
||||
public void testValidateUsingExternallyDefinedCodeMisMatchDisplay_InMemory_ShouldLogWarning() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl("http://foo");
|
||||
codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
||||
|
@ -1396,8 +1406,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
containsString("None of the codings provided are in the value set 'IdentifierType'"));
|
||||
assertThat(OperationOutcomeUtil.getFirstIssueDetails(myFhirContext, oo),
|
||||
containsString("a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://foo#bar)"));
|
||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(1).getSeverity());
|
||||
assertThat(oo.getIssue().get(1).getDiagnostics(), containsString("Unable to validate code http://foo#bar - Concept Display "));
|
||||
assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssue().get(1).getSeverity());
|
||||
assertEquals("Concept Display \"not bar code\" does not match expected \"Bar Code\" for 'http://foo#bar'", oo.getIssue().get(1).getDiagnostics());
|
||||
}
|
||||
|
||||
private OperationOutcome doTestValidateResourceContainingProfileDeclaration(String methodName, EncodingEnum enc) throws IOException {
|
||||
|
@ -1994,6 +2004,105 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
assertThat(encoded, containsString("No issues detected"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(value = {
|
||||
"INFORMATION, false",
|
||||
"INFORMATION, true",
|
||||
"WARNING, false",
|
||||
"WARNING, true",
|
||||
"ERROR, false",
|
||||
"ERROR, true",
|
||||
})
|
||||
public void testValidateWrongDisplayOnRequiredBinding(IValidationSupport.IssueSeverity theDisplayCodeMismatchIssueSeverity, boolean thePreCalculateExpansion) {
|
||||
myStorageSettings.setIssueSeverityForCodeDisplayMismatch(theDisplayCodeMismatchIssueSeverity);
|
||||
myInMemoryTerminologyServerValidationSupport.setIssueSeverityForCodeDisplayMismatch(theDisplayCodeMismatchIssueSeverity);
|
||||
|
||||
StructureDefinition sd = new StructureDefinition();
|
||||
sd.setUrl("http://profile");
|
||||
sd.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
sd.setType("Observation");
|
||||
sd.setAbstract(false);
|
||||
sd.setDerivation(StructureDefinition.TypeDerivationRule.CONSTRAINT);
|
||||
sd.setBaseDefinition("http://hl7.org/fhir/StructureDefinition/Observation");
|
||||
ElementDefinition codeElement = sd.getDifferential().addElement();
|
||||
codeElement.setId("Observation.code");
|
||||
codeElement.setPath("Observation.code");
|
||||
codeElement.addType().setCode("CodeableConcept");
|
||||
codeElement.getBinding().setStrength(Enumerations.BindingStrength.REQUIRED);
|
||||
codeElement.getBinding().setValueSet("http://vs");
|
||||
myStructureDefinitionDao.create(sd, new SystemRequestDetails());
|
||||
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setUrl("http://cs");
|
||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||
cs.addConcept()
|
||||
.setCode("8302-2")
|
||||
.setDisplay("Body Height");
|
||||
myCodeSystemDao.create(cs, new SystemRequestDetails());
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
vs.setUrl("http://vs");
|
||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
vs.getCompose().addInclude().setSystem("http://cs");
|
||||
myValueSetDao.create(vs, new SystemRequestDetails());
|
||||
|
||||
if (thePreCalculateExpansion) {
|
||||
myTermReadSvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
}
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getText().setDivAsString("<div>hello</div>");
|
||||
obs.getMeta().addProfile("http://profile");
|
||||
obs.setStatus(Observation.ObservationStatus.FINAL);
|
||||
obs.getCode().addCoding()
|
||||
.setSystem("http://cs")
|
||||
.setCode("8302-2")
|
||||
.setDisplay("Body height2");
|
||||
obs.setEffective(DateTimeType.now());
|
||||
obs.addPerformer(new Reference("Practitioner/123"));
|
||||
obs.setSubject(new Reference("Patient/123"));
|
||||
obs.setValue(new Quantity(null, 123, "http://unitsofmeasure.org", "[in_i]", "in"));
|
||||
|
||||
String encoded = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs);
|
||||
MethodOutcome outcome = myObservationDao.validate(obs, null, encoded, EncodingEnum.JSON, ValidationModeEnum.CREATE, null, new SystemRequestDetails());
|
||||
|
||||
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
|
||||
ourLog.info("Outcome: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo).replace("\"resourceType\"", "\"resType\""));
|
||||
|
||||
OperationOutcome.OperationOutcomeIssueComponent badDisplayIssue;
|
||||
if (theDisplayCodeMismatchIssueSeverity == IValidationSupport.IssueSeverity.ERROR) {
|
||||
|
||||
assertEquals(2, oo.getIssue().size());
|
||||
badDisplayIssue = oo.getIssue().get(1);
|
||||
|
||||
OperationOutcome.OperationOutcomeIssueComponent noGoodCodings = oo.getIssue().get(0);
|
||||
assertEquals("error", noGoodCodings.getSeverity().toCode());
|
||||
assertEquals("None of the codings provided are in the value set 'ValueSet[http://vs]' (http://vs), and a coding from this value set is required) (codes = http://cs#8302-2)", noGoodCodings.getDiagnostics());
|
||||
|
||||
} else if (theDisplayCodeMismatchIssueSeverity == IValidationSupport.IssueSeverity.WARNING) {
|
||||
|
||||
assertEquals(1, oo.getIssue().size());
|
||||
badDisplayIssue = oo.getIssue().get(0);
|
||||
assertThat(badDisplayIssue.getDiagnostics(),
|
||||
containsString("Concept Display \"Body height2\" does not match expected \"Body Height\""));
|
||||
assertEquals(OperationOutcome.IssueType.PROCESSING, badDisplayIssue.getCode());
|
||||
assertEquals(theDisplayCodeMismatchIssueSeverity.name().toLowerCase(), badDisplayIssue.getSeverity().toCode());
|
||||
|
||||
} else {
|
||||
|
||||
assertEquals(1, oo.getIssue().size());
|
||||
badDisplayIssue = oo.getIssue().get(0);
|
||||
assertThat(badDisplayIssue.getDiagnostics(),
|
||||
containsString("No issues detected during validation"));
|
||||
assertEquals(OperationOutcome.IssueType.INFORMATIONAL, badDisplayIssue.getCode());
|
||||
assertEquals(theDisplayCodeMismatchIssueSeverity.name().toLowerCase(), badDisplayIssue.getSeverity().toCode());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #1780
|
||||
*/
|
||||
|
|
|
@ -148,7 +148,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
|||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "child10", null, "http://vs");
|
||||
assertNotNull(outcome);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getMessage());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getSourceDetails());
|
||||
|
||||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "childX", null, "http://vs");
|
||||
assertNotNull(outcome);
|
||||
|
@ -160,7 +160,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
|||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "code1", null, "http://vs");
|
||||
assertNotNull(outcome);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getMessage());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getSourceDetails());
|
||||
|
||||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "codeX", null, "http://vs");
|
||||
assertNotNull(outcome);
|
||||
|
@ -251,7 +251,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
|||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "child10", null, "http://vs");
|
||||
assertNotNull(outcome);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getMessage());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getSourceDetails());
|
||||
|
||||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "childX", null, "http://vs");
|
||||
assertNotNull(outcome);
|
||||
|
@ -263,7 +263,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
|||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "code1", null, "http://vs");
|
||||
assertNotNull(outcome);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getMessage());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getSourceDetails());
|
||||
|
||||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "codeX", null, "http://vs");
|
||||
assertNotNull(outcome);
|
||||
|
@ -344,7 +344,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
|||
Coding coding = null;
|
||||
CodeableConcept codeableConcept = null;
|
||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
||||
assertFalse(result.isOk());
|
||||
assertTrue(result.isOk());
|
||||
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||
assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage());
|
||||
}
|
||||
|
|
|
@ -529,8 +529,8 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
|
|||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||
assertEquals("Unable to validate code http://acme.org#8452-5 - Concept Display \"Old Systolic blood pressure.inspiration - expiration\" does not match expected \"Systolic blood pressure.inspiration - expiration\" for CodeSystem: http://acme.org", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||
assertEquals("Concept Display \"Old Systolic blood pressure.inspiration - expiration\" does not match expected \"Systolic blood pressure.inspiration - expiration\"", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -14,6 +14,7 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
|||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
|
@ -1294,14 +1295,14 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
|
|||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("result", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName());
|
||||
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -30,8 +30,6 @@ import ca.uhn.fhir.jpa.esr.ExternallyStoredResourceServiceRegistry;
|
|||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
|
||||
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
|
||||
|
@ -62,7 +60,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.quartz.JobKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
|
|
@ -466,7 +466,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
String code = "male";
|
||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", outcome.getMessage());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", outcome.getSourceDetails());
|
||||
|
||||
// Validate Code - Bad
|
||||
code = "AAA";
|
||||
|
@ -1635,10 +1635,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
code = "28571000087109";
|
||||
display = "BLAH";
|
||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals(null, outcome.getCode());
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("28571000087109", outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\" for in-memory expansion of ValueSet: http://ehealthontario.ca/fhir/ValueSet/vaccinecode", outcome.getMessage());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://ehealthontario.ca/fhir/ValueSet/vaccinecode", outcome.getSourceDetails());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
|
||||
// Validate code - good code, good display
|
||||
|
@ -1680,11 +1681,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
|||
code = "28571000087109";
|
||||
display = "BLAH";
|
||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals(null, outcome.getCode());
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("28571000087109", outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
assertThat(outcome.getMessage(), containsString("Unable to validate code http://snomed.info/sct#28571000087109 - Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\" for CodeSystem: http://snomed.info/sct - Code validation occurred using a ValueSet expansion that was pre-calculated at"));
|
||||
assertEquals("Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\"", outcome.getMessage());
|
||||
|
||||
// Validate code - good code, good display
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
|
|
|
@ -117,7 +117,7 @@ public class FhirResourceDaoR5ValueSetTest extends BaseJpaR5Test {
|
|||
Coding coding = null;
|
||||
CodeableConcept codeableConcept = null;
|
||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
||||
assertFalse(result.isOk());
|
||||
assertTrue(result.isOk());
|
||||
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
|
||||
assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage());
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
|
|||
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
|
||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -1228,14 +1229,14 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test {
|
|||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals("result", respParam.getParameter().get(0).getName());
|
||||
assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName());
|
||||
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||
|
||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||
|
||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
||||
}
|
||||
|
||||
// Good code and system, but not in specified valueset
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server - Master Data Management
|
||||
* HAPI FHIR - Master Data Management
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
|
|
|
@ -42,7 +42,8 @@ public class MailSvcIT {
|
|||
// execute
|
||||
fixture.sendMail(email);
|
||||
// validate
|
||||
assertTrue(ourGreenMail.waitForIncomingEmail(5000, 1));
|
||||
boolean condition = ourGreenMail.waitForIncomingEmail(5000, 1);
|
||||
assertTrue(condition);
|
||||
final MimeMessage[] receivedMessages = ourGreenMail.getReceivedMessages();
|
||||
assertEquals(1, receivedMessages.length);
|
||||
assertEquals(SUBJECT, receivedMessages[0].getSubject());
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
*/
|
||||
package ca.uhn.fhir.jpa.api.config;
|
||||
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.jpa.api.model.HistoryCountModeEnum;
|
||||
import ca.uhn.fhir.jpa.api.model.WarmCacheEntry;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
||||
|
@ -334,6 +335,13 @@ public class JpaStorageSettings extends StorageSettings {
|
|||
*/
|
||||
private boolean myResourceHistoryDbEnabled = true;
|
||||
|
||||
/**
|
||||
* @since 7.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
private IValidationSupport.IssueSeverity myIssueSeverityForCodeDisplayMismatch =
|
||||
IValidationSupport.IssueSeverity.WARNING;
|
||||
|
||||
/**
|
||||
* This setting allows preventing a conditional update to invalidate the match criteria.
|
||||
* <p/>
|
||||
|
@ -2376,6 +2384,41 @@ public class JpaStorageSettings extends StorageSettings {
|
|||
return myNonResourceDbHistoryEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* This setting controls the validation issue severity to report when a code validation
|
||||
* finds that the code is present in the given CodeSystem, but the display name being
|
||||
* validated doesn't match the expected value(s). Defaults to
|
||||
* {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#WARNING}. Set this
|
||||
* value to {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#INFORMATION}
|
||||
* if you don't want to see display name validation issues at all in resource validation
|
||||
* outcomes.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
public IValidationSupport.IssueSeverity getIssueSeverityForCodeDisplayMismatch() {
|
||||
return myIssueSeverityForCodeDisplayMismatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* This setting controls the validation issue severity to report when a code validation
|
||||
* finds that the code is present in the given CodeSystem, but the display name being
|
||||
* validated doesn't match the expected value(s). Defaults to
|
||||
* {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#WARNING}. Set this
|
||||
* value to {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#INFORMATION}
|
||||
* if you don't want to see display name validation issues at all in resource validation
|
||||
* outcomes.
|
||||
*
|
||||
* @param theIssueSeverityForCodeDisplayMismatch The severity. Must not be {@literal null}.
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public void setIssueSeverityForCodeDisplayMismatch(
|
||||
@Nonnull IValidationSupport.IssueSeverity theIssueSeverityForCodeDisplayMismatch) {
|
||||
Validate.notNull(
|
||||
theIssueSeverityForCodeDisplayMismatch, "theIssueSeverityForCodeDisplayMismatch must not be null");
|
||||
myIssueSeverityForCodeDisplayMismatch = theIssueSeverityForCodeDisplayMismatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* This setting controls whether MdmLink and other non-resource DB history is enabled.
|
||||
* <p/>
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Test Utilities
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.models;
|
||||
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
|
|
|
@ -57,16 +57,54 @@ import static org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTermi
|
|||
@SuppressWarnings("EnhancedSwitchMigration")
|
||||
public class InMemoryTerminologyServerValidationSupport implements IValidationSupport {
|
||||
private static final String OUR_PIPE_CHARACTER = "|";
|
||||
|
||||
private final FhirContext myCtx;
|
||||
private final VersionCanonicalizer myVersionCanonicalizer;
|
||||
private IssueSeverity myIssueSeverityForCodeDisplayMismatch = IssueSeverity.WARNING;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theCtx A FhirContext for the FHIR version being validated
|
||||
*/
|
||||
public InMemoryTerminologyServerValidationSupport(FhirContext theCtx) {
|
||||
Validate.notNull(theCtx, "theCtx must not be null");
|
||||
myCtx = theCtx;
|
||||
myVersionCanonicalizer = new VersionCanonicalizer(theCtx);
|
||||
}
|
||||
|
||||
/**
|
||||
* This setting controls the validation issue severity to report when a code validation
|
||||
* finds that the code is present in the given CodeSystem, but the display name being
|
||||
* validated doesn't match the expected value(s). Defaults to
|
||||
* {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#WARNING}. Set this
|
||||
* value to {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#INFORMATION}
|
||||
* if you don't want to see display name validation issues at all in resource validation
|
||||
* outcomes.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public IssueSeverity getIssueSeverityForCodeDisplayMismatch() {
|
||||
return myIssueSeverityForCodeDisplayMismatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* This setting controls the validation issue severity to report when a code validation
|
||||
* finds that the code is present in the given CodeSystem, but the display name being
|
||||
* validated doesn't match the expected value(s). Defaults to
|
||||
* {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#WARNING}. Set this
|
||||
* value to {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#INFORMATION}
|
||||
* if you don't want to see display name validation issues at all in resource validation
|
||||
* outcomes.
|
||||
*
|
||||
* @param theIssueSeverityForCodeDisplayMismatch The severity. Must not be {@literal null}.
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public void setIssueSeverityForCodeDisplayMismatch(@Nonnull IssueSeverity theIssueSeverityForCodeDisplayMismatch) {
|
||||
Validate.notNull(
|
||||
theIssueSeverityForCodeDisplayMismatch, "theIssueSeverityForCodeDisplayMismatch must not be null");
|
||||
myIssueSeverityForCodeDisplayMismatch = theIssueSeverityForCodeDisplayMismatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
return myCtx;
|
||||
|
@ -517,22 +555,26 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
.setCodeSystemName(codeSystemResourceName)
|
||||
.setCodeSystemVersion(csVersion);
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
codeValidationResult.setMessage(
|
||||
"Code was validated against in-memory expansion of ValueSet: " + theValueSetUrl);
|
||||
populateSourceDetailsForInMemoryExpansion(theValueSetUrl, codeValidationResult);
|
||||
}
|
||||
return codeValidationResult;
|
||||
} else {
|
||||
String message = "Concept Display \"" + theDisplayToValidate + "\" does not match expected \""
|
||||
+ nextExpansionCode.getDisplay() + "\"";
|
||||
String messageAppend = "";
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
message += " for in-memory expansion of ValueSet: " + theValueSetUrl;
|
||||
messageAppend = " for in-memory expansion of ValueSet: " + theValueSetUrl;
|
||||
}
|
||||
return new CodeValidationResult()
|
||||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setDisplay(nextExpansionCode.getDisplay())
|
||||
.setMessage(message)
|
||||
.setCodeSystemName(codeSystemResourceName)
|
||||
.setCodeSystemVersion(csVersion);
|
||||
CodeValidationResult codeValidationResult = createResultForDisplayMismatch(
|
||||
myCtx,
|
||||
theCodeToValidate,
|
||||
theDisplayToValidate,
|
||||
nextExpansionCode.getDisplay(),
|
||||
csVersion,
|
||||
messageAppend,
|
||||
getIssueSeverityForCodeDisplayMismatch());
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
populateSourceDetailsForInMemoryExpansion(theValueSetUrl, codeValidationResult);
|
||||
}
|
||||
return codeValidationResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1194,24 +1236,59 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
return theVersion;
|
||||
}
|
||||
|
||||
public enum FailureType {
|
||||
UNKNOWN_CODE_SYSTEM,
|
||||
OTHER
|
||||
private static void populateSourceDetailsForInMemoryExpansion(
|
||||
String theValueSetUrl, CodeValidationResult codeValidationResult) {
|
||||
codeValidationResult.setSourceDetails(
|
||||
"Code was validated against in-memory expansion of ValueSet: " + theValueSetUrl);
|
||||
}
|
||||
|
||||
public static class ExpansionCouldNotBeCompletedInternallyException extends Exception {
|
||||
public static CodeValidationResult createResultForDisplayMismatch(
|
||||
FhirContext theFhirContext,
|
||||
String theCode,
|
||||
String theDisplay,
|
||||
String theExpectedDisplay,
|
||||
String theCodeSystemVersion,
|
||||
IssueSeverity theIssueSeverityForCodeDisplayMismatch) {
|
||||
return createResultForDisplayMismatch(
|
||||
theFhirContext,
|
||||
theCode,
|
||||
theDisplay,
|
||||
theExpectedDisplay,
|
||||
theCodeSystemVersion,
|
||||
"",
|
||||
theIssueSeverityForCodeDisplayMismatch);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -2226561628771483085L;
|
||||
private final FailureType myFailureType;
|
||||
private static CodeValidationResult createResultForDisplayMismatch(
|
||||
FhirContext theFhirContext,
|
||||
String theCode,
|
||||
String theDisplay,
|
||||
String theExpectedDisplay,
|
||||
String theCodeSystemVersion,
|
||||
String theMessageAppend,
|
||||
IssueSeverity theIssueSeverityForCodeDisplayMismatch) {
|
||||
|
||||
public ExpansionCouldNotBeCompletedInternallyException(String theMessage, FailureType theFailureType) {
|
||||
super(theMessage);
|
||||
myFailureType = theFailureType;
|
||||
}
|
||||
|
||||
public FailureType getFailureType() {
|
||||
return myFailureType;
|
||||
String message;
|
||||
IssueSeverity issueSeverity = theIssueSeverityForCodeDisplayMismatch;
|
||||
if (issueSeverity == IssueSeverity.INFORMATION) {
|
||||
message = null;
|
||||
issueSeverity = null;
|
||||
} else {
|
||||
message = theFhirContext
|
||||
.getLocalizer()
|
||||
.getMessage(
|
||||
InMemoryTerminologyServerValidationSupport.class,
|
||||
"displayMismatch",
|
||||
theDisplay,
|
||||
theExpectedDisplay)
|
||||
+ theMessageAppend;
|
||||
}
|
||||
return new CodeValidationResult()
|
||||
.setSeverity(issueSeverity)
|
||||
.setMessage(message)
|
||||
.setCode(theCode)
|
||||
.setCodeSystemVersion(theCodeSystemVersion)
|
||||
.setDisplay(theExpectedDisplay);
|
||||
}
|
||||
|
||||
private static void flattenAndConvertCodesDstu2(
|
||||
|
@ -1273,4 +1350,24 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
|||
flattenAndConvertCodesR5(next.getContains(), theFhirVersionIndependentConcepts);
|
||||
}
|
||||
}
|
||||
|
||||
public enum FailureType {
|
||||
UNKNOWN_CODE_SYSTEM,
|
||||
OTHER
|
||||
}
|
||||
|
||||
public static class ExpansionCouldNotBeCompletedInternallyException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -2226561628771483085L;
|
||||
private final FailureType myFailureType;
|
||||
|
||||
public ExpansionCouldNotBeCompletedInternallyException(String theMessage, FailureType theFailureType) {
|
||||
super(theMessage);
|
||||
myFailureType = theFailureType;
|
||||
}
|
||||
|
||||
public FailureType getFailureType() {
|
||||
return myFailureType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,11 +76,12 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup
|
|||
result.setSeverity(myNonExistentCodeSystemSeverity);
|
||||
result.setMessage("CodeSystem is unknown and can't be validated: " + theCodeSystem);
|
||||
|
||||
// For information level, we just strip out the severity+message entirely
|
||||
// so that nothing appears in the validation result
|
||||
if (myNonExistentCodeSystemSeverity == IssueSeverity.INFORMATION) {
|
||||
// for warnings, we don't set the code
|
||||
// cause if we do, the severity is stripped out
|
||||
// (see VersionSpecificWorkerContextWrapper.convertValidationResult)
|
||||
result.setCode(theCode);
|
||||
result.setSeverity(null);
|
||||
result.setMessage(null);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -37,7 +37,13 @@ abstract class BaseValidatorBridge implements IValidatorModule {
|
|||
ResultSeverityEnum.fromCode(riMessage.getLevel().toCode()));
|
||||
}
|
||||
if (riMessage.getMessageId() != null) {
|
||||
hapiMessage.setMessageId(riMessage.getMessageId());
|
||||
// In BaseValidator, the messageId gets populated with the raw message because
|
||||
// there is an assumption that it's a message key and not an actual message. But
|
||||
// messsages coming from our internal terminology service don't work that
|
||||
// way, so we strip them by checking if the ID is actually a sentence
|
||||
if (!riMessage.getMessageId().contains(" ")) {
|
||||
hapiMessage.setMessageId(riMessage.getMessageId());
|
||||
}
|
||||
}
|
||||
if (riMessage.sliceText != null && riMessage.sliceText.length > 0) {
|
||||
hapiMessage.setSliceMessages(Arrays.asList(riMessage.sliceText));
|
||||
|
|
|
@ -255,23 +255,30 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
String code = theResult.getCode();
|
||||
String display = theResult.getDisplay();
|
||||
|
||||
String issueSeverity = theResult.getSeverityCode();
|
||||
String issueSeverityCode = theResult.getSeverityCode();
|
||||
String message = theResult.getMessage();
|
||||
if (isNotBlank(code)) {
|
||||
retVal = new ValidationResult(
|
||||
theSystem,
|
||||
null,
|
||||
new org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent()
|
||||
.setCode(code)
|
||||
.setDisplay(display),
|
||||
null);
|
||||
} else if (isNotBlank(issueSeverity)) {
|
||||
retVal = new ValidationResult(
|
||||
ValidationMessage.IssueSeverity.fromCode(issueSeverity),
|
||||
message,
|
||||
TerminologyServiceErrorClass.UNKNOWN,
|
||||
null);
|
||||
ValidationMessage.IssueSeverity issueSeverity = null;
|
||||
if (issueSeverityCode != null) {
|
||||
issueSeverity = ValidationMessage.IssueSeverity.fromCode(issueSeverityCode);
|
||||
} else if (isNotBlank(message)) {
|
||||
issueSeverity = ValidationMessage.IssueSeverity.INFORMATION;
|
||||
}
|
||||
|
||||
CodeSystem.ConceptDefinitionComponent conceptDefinitionComponent = null;
|
||||
if (code != null) {
|
||||
conceptDefinitionComponent = new CodeSystem.ConceptDefinitionComponent()
|
||||
.setCode(code)
|
||||
.setDisplay(display);
|
||||
}
|
||||
|
||||
retVal = new ValidationResult(
|
||||
issueSeverity,
|
||||
message,
|
||||
theSystem,
|
||||
theResult.getCodeSystemVersion(),
|
||||
conceptDefinitionComponent,
|
||||
display,
|
||||
null);
|
||||
}
|
||||
|
||||
if (retVal == null) {
|
||||
|
@ -684,6 +691,32 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
throw new UnsupportedOperationException(Msg.code(650) + "Unable to fetch resources of type: " + theClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForPublication() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForPublication(boolean b) {
|
||||
throw new UnsupportedOperationException(Msg.code(2351));
|
||||
}
|
||||
|
||||
public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) {
|
||||
ConceptValidationOptions retVal = new ConceptValidationOptions();
|
||||
if (theOptions.isGuessSystem()) {
|
||||
retVal = retVal.setInferSystem(true);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static VersionSpecificWorkerContextWrapper newVersionSpecificWorkerContextWrapper(
|
||||
IValidationSupport theValidationSupport) {
|
||||
VersionCanonicalizer versionCanonicalizer = new VersionCanonicalizer(theValidationSupport.getFhirContext());
|
||||
return new VersionSpecificWorkerContextWrapper(
|
||||
new ValidationSupportContext(theValidationSupport), versionCanonicalizer);
|
||||
}
|
||||
|
||||
private static class ResourceKey {
|
||||
private final int myHashCode;
|
||||
private final String myResourceName;
|
||||
|
@ -729,30 +762,4 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
return myHashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) {
|
||||
ConceptValidationOptions retVal = new ConceptValidationOptions();
|
||||
if (theOptions.isGuessSystem()) {
|
||||
retVal = retVal.setInferSystem(true);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static VersionSpecificWorkerContextWrapper newVersionSpecificWorkerContextWrapper(
|
||||
IValidationSupport theValidationSupport) {
|
||||
VersionCanonicalizer versionCanonicalizer = new VersionCanonicalizer(theValidationSupport.getFhirContext());
|
||||
return new VersionSpecificWorkerContextWrapper(
|
||||
new ValidationSupportContext(theValidationSupport), versionCanonicalizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForPublication() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForPublication(boolean b) {
|
||||
throw new UnsupportedOperationException(Msg.code(2351));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,9 @@ import org.slf4j.LoggerFactory;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -62,7 +65,7 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
|||
// ValidateCode
|
||||
outcome = myChain.validateCode(valCtx, options, null, "txt", null, valueSetUrl);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/mimetypes", outcome.getMessage());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/mimetypes", outcome.getSourceDetails());
|
||||
assertEquals("txt", outcome.getCode());
|
||||
|
||||
// ValidateCodeInValueSet
|
||||
|
@ -70,7 +73,7 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
|||
assertNotNull(valueSet);
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, null, "txt", null, valueSet);
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/mimetypes", outcome.getMessage());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/mimetypes", outcome.getSourceDetails());
|
||||
assertEquals("txt", outcome.getCode());
|
||||
}
|
||||
|
||||
|
@ -91,7 +94,7 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
|||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code1", null, vs);
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getMessage());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getSourceDetails());
|
||||
assertTrue(outcome.isOk());
|
||||
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code99", null, vs);
|
||||
|
@ -127,7 +130,9 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
|||
IValidationSupport.CodeValidationResult outcome;
|
||||
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code1", null, vs);
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getMessage());
|
||||
assertNull(outcome.getMessage());
|
||||
assertNull(outcome.getSeverityCode());
|
||||
assertEquals("Code was validated against in-memory expansion of ValueSet: http://vs", outcome.getSourceDetails());
|
||||
assertTrue(outcome.isOk());
|
||||
|
||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code99", null, vs);
|
||||
|
@ -243,10 +248,13 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
|||
code = "28571000087109";
|
||||
display = "BLAH";
|
||||
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, display, valueSetUrl);
|
||||
assertFalse(outcome.isOk());
|
||||
assertEquals(null, outcome.getCode());
|
||||
assertTrue(outcome.isOk());
|
||||
assertEquals("28571000087109", outcome.getCode());
|
||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||
assertThat(outcome.getMessage(), containsString("Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\""));
|
||||
assertEquals("warning", outcome.getSeverityCode());
|
||||
assertThat(outcome.getSourceDetails(), startsWith("Code was validated against in-memory expansion"));
|
||||
|
||||
// Validate code - good code, good display
|
||||
codeSystemUrl = "http://snomed.info/sct";
|
||||
|
|
|
@ -798,7 +798,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
Patient resource = loadResource("/dstu3/nl/nl-core-patient-01.json", Patient.class);
|
||||
ValidationResult results = myVal.validateWithResult(resource);
|
||||
List<SingleValidationMessage> outcome = logResultsAndReturnNonInformationalOnes(results);
|
||||
assertThat(outcome.toString(), containsString("Could not confirm that the codes provided are in the value set 'LandGBACodelijst'"));
|
||||
assertThat(outcome.toString(), containsString("The Coding provided (urn:oid:2.16.840.1.113883.2.4.4.16.34#6030) is not in the value set 'LandGBACodelijst'"));
|
||||
}
|
||||
|
||||
private void loadNL() throws IOException {
|
||||
|
|
Loading…
Reference in New Issue