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 String myCodeSystemVersion;
|
||||||
private List<BaseConceptProperty> myProperties;
|
private List<BaseConceptProperty> myProperties;
|
||||||
private String myDisplay;
|
private String myDisplay;
|
||||||
|
private String mySourceDetails;
|
||||||
|
|
||||||
public CodeValidationResult() {
|
public CodeValidationResult() {
|
||||||
super();
|
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() {
|
public String getDisplay() {
|
||||||
return myDisplay;
|
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.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=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.
|
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;
|
package ca.uhn.fhir.jpa.config;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.svc.IDeleteExpungeSvc;
|
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.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.dao.JpaPersistedResourceValidationSupport;
|
import ca.uhn.fhir.jpa.dao.JpaPersistedResourceValidationSupport;
|
||||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain;
|
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.jpa.validation.ValidatorResourceFetcher;
|
||||||
import ca.uhn.fhir.validation.IInstanceValidatorModule;
|
import ca.uhn.fhir.validation.IInstanceValidatorModule;
|
||||||
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
|
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.support.ValidationSupportChain;
|
||||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||||
import org.hl7.fhir.common.hapi.validation.validator.HapiToHl7OrgDstu2ValidatingSupportWrapper;
|
import org.hl7.fhir.common.hapi.validation.validator.HapiToHl7OrgDstu2ValidatingSupportWrapper;
|
||||||
|
@ -45,6 +47,15 @@ public class ValidationSupportConfig {
|
||||||
return new DefaultProfileValidationSupport(theFhirContext);
|
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)
|
@Bean(name = JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||||
public JpaValidationSupportChain jpaValidationSupportChain(FhirContext theFhirContext) {
|
public JpaValidationSupportChain jpaValidationSupportChain(FhirContext theFhirContext) {
|
||||||
return new JpaValidationSupportChain(theFhirContext);
|
return new JpaValidationSupportChain(theFhirContext);
|
||||||
|
|
|
@ -61,6 +61,10 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
public class ValueSetOperationProvider extends BaseJpaProvider {
|
public class ValueSetOperationProvider extends BaseJpaProvider {
|
||||||
|
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(ValueSetOperationProvider.class);
|
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
|
@Autowired
|
||||||
protected IValidationSupport myValidationSupport;
|
protected IValidationSupport myValidationSupport;
|
||||||
|
@ -145,9 +149,10 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
||||||
idempotent = true,
|
idempotent = true,
|
||||||
typeName = "ValueSet",
|
typeName = "ValueSet",
|
||||||
returnParameters = {
|
returnParameters = {
|
||||||
@OperationParam(name = "result", typeName = "boolean", min = 1),
|
@OperationParam(name = RESULT, typeName = "boolean", min = 1),
|
||||||
@OperationParam(name = "message", typeName = "string"),
|
@OperationParam(name = MESSAGE, typeName = "string"),
|
||||||
@OperationParam(name = "display", typeName = "string")
|
@OperationParam(name = DISPLAY, typeName = "string"),
|
||||||
|
@OperationParam(name = SOURCE_DETAILS, typeName = "string")
|
||||||
})
|
})
|
||||||
public IBaseParameters validateCode(
|
public IBaseParameters validateCode(
|
||||||
HttpServletRequest theServletRequest,
|
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 = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theSystem,
|
||||||
@OperationParam(name = "systemVersion", min = 0, max = 1, typeName = "string")
|
@OperationParam(name = "systemVersion", min = 0, max = 1, typeName = "string")
|
||||||
IPrimitiveType<String> theSystemVersion,
|
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 = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding,
|
||||||
@OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept")
|
@OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept")
|
||||||
ICompositeType theCodeableConcept,
|
ICompositeType theCodeableConcept,
|
||||||
|
@ -251,7 +256,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
||||||
name = ProviderConstants.OPERATION_INVALIDATE_EXPANSION,
|
name = ProviderConstants.OPERATION_INVALIDATE_EXPANSION,
|
||||||
idempotent = false,
|
idempotent = false,
|
||||||
typeName = "ValueSet",
|
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(
|
public IBaseParameters invalidateValueSetExpansion(
|
||||||
@IdParam IIdType theValueSetId, RequestDetails theRequestDetails, HttpServletRequest theServletRequest) {
|
@IdParam IIdType theValueSetId, RequestDetails theRequestDetails, HttpServletRequest theServletRequest) {
|
||||||
startRequest(theServletRequest);
|
startRequest(theServletRequest);
|
||||||
|
@ -260,7 +265,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
||||||
String outcome = myTermReadSvc.invalidatePreCalculatedExpansion(theValueSetId, theRequestDetails);
|
String outcome = myTermReadSvc.invalidatePreCalculatedExpansion(theValueSetId, theRequestDetails);
|
||||||
|
|
||||||
IBaseParameters retVal = ParametersUtil.newInstance(getContext());
|
IBaseParameters retVal = ParametersUtil.newInstance(getContext());
|
||||||
ParametersUtil.addParameterToParametersString(getContext(), retVal, "message", outcome);
|
ParametersUtil.addParameterToParametersString(getContext(), retVal, MESSAGE, outcome);
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -325,12 +330,16 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
||||||
public static IBaseParameters toValidateCodeResult(FhirContext theContext, CodeValidationResult theResult) {
|
public static IBaseParameters toValidateCodeResult(FhirContext theContext, CodeValidationResult theResult) {
|
||||||
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
|
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
|
||||||
|
|
||||||
ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "result", theResult.isOk());
|
ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, theResult.isOk());
|
||||||
if (isNotBlank(theResult.getMessage())) {
|
if (isNotBlank(theResult.getMessage())) {
|
||||||
ParametersUtil.addParameterToParametersString(theContext, retVal, "message", theResult.getMessage());
|
ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, theResult.getMessage());
|
||||||
}
|
}
|
||||||
if (isNotBlank(theResult.getDisplay())) {
|
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;
|
return retVal;
|
||||||
|
|
|
@ -295,6 +295,9 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private InMemoryTerminologyServerValidationSupport myInMemoryTerminologyServerValidationSupport;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
||||||
TermCodeSystemVersionDetails cs = getCurrentCodeSystemVersion(theSystem);
|
TermCodeSystemVersionDetails cs = getCurrentCodeSystemVersion(theSystem);
|
||||||
|
@ -1025,11 +1028,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
||||||
new VersionConvertor_40_50(new BaseAdvisor_40_50()), "ValueSet");
|
new VersionConvertor_40_50(new BaseAdvisor_40_50()), "ValueSet");
|
||||||
org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent includeOrExclude =
|
org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent includeOrExclude =
|
||||||
ValueSet40_50.convertConceptSetComponent(theIncludeOrExclude);
|
ValueSet40_50.convertConceptSetComponent(theIncludeOrExclude);
|
||||||
new InMemoryTerminologyServerValidationSupport(myContext)
|
myInMemoryTerminologyServerValidationSupport.expandValueSetIncludeOrExclude(
|
||||||
.expandValueSetIncludeOrExclude(
|
new ValidationSupportContext(provideValidationSupport()), consumer, includeOrExclude);
|
||||||
new ValidationSupportContext(provideValidationSupport()),
|
|
||||||
consumer,
|
|
||||||
includeOrExclude);
|
|
||||||
} catch (InMemoryTerminologyServerValidationSupport.ExpansionCouldNotBeCompletedInternallyException e) {
|
} catch (InMemoryTerminologyServerValidationSupport.ExpansionCouldNotBeCompletedInternallyException e) {
|
||||||
if (theExpansionOptions != null
|
if (theExpansionOptions != null
|
||||||
&& !theExpansionOptions.isFailOnMissingCodeSystem()
|
&& !theExpansionOptions.isFailOnMissingCodeSystem()
|
||||||
|
@ -2055,7 +2055,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
||||||
.findByResourcePid(valueSetResourcePid.getId())
|
.findByResourcePid(valueSetResourcePid.getId())
|
||||||
.orElseThrow(IllegalStateException::new);
|
.orElseThrow(IllegalStateException::new);
|
||||||
String timingDescription = toHumanReadableExpansionTimestamp(valueSetEntity);
|
String timingDescription = toHumanReadableExpansionTimestamp(valueSetEntity);
|
||||||
String msg = myContext
|
String preExpansionMessage = myContext
|
||||||
.getLocalizer()
|
.getLocalizer()
|
||||||
.getMessage(TermReadSvcImpl.class, "validationPerformedAgainstPreExpansion", timingDescription);
|
.getMessage(TermReadSvcImpl.class, "validationPerformedAgainstPreExpansion", timingDescription);
|
||||||
|
|
||||||
|
@ -2068,14 +2068,18 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
||||||
.setCode(concept.getCode())
|
.setCode(concept.getCode())
|
||||||
.setDisplay(concept.getDisplay())
|
.setDisplay(concept.getDisplay())
|
||||||
.setCodeSystemVersion(concept.getSystemVersion())
|
.setCodeSystemVersion(concept.getSystemVersion())
|
||||||
.setMessage(msg);
|
.setSourceDetails(preExpansionMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String expectedDisplay = concepts.get(0).getDisplay();
|
String expectedDisplay = concepts.get(0).getDisplay();
|
||||||
String append = createMessageAppendForDisplayMismatch(theSystem, theDisplay, expectedDisplay) + " - " + msg;
|
return InMemoryTerminologyServerValidationSupport.createResultForDisplayMismatch(
|
||||||
return createFailureCodeValidationResult(theSystem, theCode, systemVersion, append)
|
myContext,
|
||||||
.setDisplay(expectedDisplay);
|
theCode,
|
||||||
|
theDisplay,
|
||||||
|
expectedDisplay,
|
||||||
|
systemVersion,
|
||||||
|
myStorageSettings.getIssueSeverityForCodeDisplayMismatch());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!concepts.isEmpty()) {
|
if (!concepts.isEmpty()) {
|
||||||
|
@ -2083,7 +2087,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
||||||
.setCode(concepts.get(0).getCode())
|
.setCode(concepts.get(0).getCode())
|
||||||
.setDisplay(concepts.get(0).getDisplay())
|
.setDisplay(concepts.get(0).getDisplay())
|
||||||
.setCodeSystemVersion(concepts.get(0).getSystemVersion())
|
.setCodeSystemVersion(concepts.get(0).getSystemVersion())
|
||||||
.setMessage(msg);
|
.setMessage(preExpansionMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok, we failed
|
// Ok, we failed
|
||||||
|
@ -2096,7 +2100,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
||||||
String unknownCodeMessage = myContext
|
String unknownCodeMessage = myContext
|
||||||
.getLocalizer()
|
.getLocalizer()
|
||||||
.getMessage(TermReadSvcImpl.class, "unknownCodeInSystem", theSystem, theCode);
|
.getMessage(TermReadSvcImpl.class, "unknownCodeInSystem", theSystem, theCode);
|
||||||
append = " - " + unknownCodeMessage + ". " + msg;
|
append = " - " + unknownCodeMessage + ". " + preExpansionMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createFailureCodeValidationResult(theSystem, theCode, null, append);
|
return createFailureCodeValidationResult(theSystem, theCode, null, append);
|
||||||
|
@ -2710,11 +2714,13 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
||||||
|| code.getDisplay().equals(theDisplay)) {
|
|| code.getDisplay().equals(theDisplay)) {
|
||||||
return new CodeValidationResult().setCode(code.getCode()).setDisplay(code.getDisplay());
|
return new CodeValidationResult().setCode(code.getCode()).setDisplay(code.getDisplay());
|
||||||
} else {
|
} else {
|
||||||
String messageAppend =
|
return InMemoryTerminologyServerValidationSupport.createResultForDisplayMismatch(
|
||||||
createMessageAppendForDisplayMismatch(theCodeSystemUrl, theDisplay, code.getDisplay());
|
myContext,
|
||||||
return createFailureCodeValidationResult(
|
theCode,
|
||||||
theCodeSystemUrl, theCode, code.getSystemVersion(), messageAppend)
|
theDisplay,
|
||||||
.setDisplay(code.getDisplay());
|
code.getDisplay(),
|
||||||
|
code.getSystemVersion(),
|
||||||
|
myStorageSettings.getIssueSeverityForCodeDisplayMismatch());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2752,14 +2758,13 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
||||||
|
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
if (valueSet != null) {
|
if (valueSet != null) {
|
||||||
retVal = new InMemoryTerminologyServerValidationSupport(myContext)
|
retVal = myInMemoryTerminologyServerValidationSupport.validateCodeInValueSet(
|
||||||
.validateCodeInValueSet(
|
theValidationSupportContext,
|
||||||
theValidationSupportContext,
|
theValidationOptions,
|
||||||
theValidationOptions,
|
theCodeSystem,
|
||||||
theCodeSystem,
|
theCode,
|
||||||
theCode,
|
theDisplay,
|
||||||
theDisplay,
|
valueSet);
|
||||||
valueSet);
|
|
||||||
} else {
|
} else {
|
||||||
String append = " - Unable to locate ValueSet[" + theValueSetUrl + "]";
|
String append = " - Unable to locate ValueSet[" + theValueSetUrl + "]";
|
||||||
retVal = createFailureCodeValidationResult(theCodeSystem, theCode, null, append);
|
retVal = createFailureCodeValidationResult(theCodeSystem, theCode, null, append);
|
||||||
|
@ -3182,13 +3187,6 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
||||||
return theExpansionOptions.getTheDisplayLanguage().equalsIgnoreCase(theStoredLang);
|
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
|
@Nonnull
|
||||||
private static String createMessageAppendForCodeNotFoundInCodeSystem(String theCodeSystemUrl) {
|
private static String createMessageAppendForCodeNotFoundInCodeSystem(String theCodeSystemUrl) {
|
||||||
return " - Code is not found in CodeSystem: " + theCodeSystemUrl;
|
return " - Code is not found in CodeSystem: " + theCodeSystemUrl;
|
||||||
|
|
|
@ -59,6 +59,9 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
||||||
@Autowired
|
@Autowired
|
||||||
private UnknownCodeSystemWarningValidationSupport myUnknownCodeSystemWarningValidationSupport;
|
private UnknownCodeSystemWarningValidationSupport myUnknownCodeSystemWarningValidationSupport;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private InMemoryTerminologyServerValidationSupport myInMemoryTerminologyServerValidationSupport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -82,7 +85,7 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
||||||
addValidationSupport(myJpaValidationSupport);
|
addValidationSupport(myJpaValidationSupport);
|
||||||
addValidationSupport(myTerminologyService);
|
addValidationSupport(myTerminologyService);
|
||||||
addValidationSupport(new SnapshotGeneratingValidationSupport(myFhirContext));
|
addValidationSupport(new SnapshotGeneratingValidationSupport(myFhirContext));
|
||||||
addValidationSupport(new InMemoryTerminologyServerValidationSupport(myFhirContext));
|
addValidationSupport(myInMemoryTerminologyServerValidationSupport);
|
||||||
addValidationSupport(myNpmJpaValidationSupport);
|
addValidationSupport(myNpmJpaValidationSupport);
|
||||||
addValidationSupport(new CommonCodeSystemsTerminologyService(myFhirContext));
|
addValidationSupport(new CommonCodeSystemsTerminologyService(myFhirContext));
|
||||||
addValidationSupport(myConceptMappingSvc);
|
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;
|
package ca.uhn.fhir.jpa.mdm.config;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
|
|
@ -103,9 +103,10 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
|
||||||
CodingDt coding = null;
|
CodingDt coding = null;
|
||||||
CodeableConceptDt codeableConcept = null;
|
CodeableConceptDt codeableConcept = null;
|
||||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
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("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("Systolic blood pressure at First encounter", result.getDisplay());
|
||||||
|
assertEquals(IValidationSupport.IssueSeverity.WARNING, result.getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -226,7 +226,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
|
||||||
Coding coding = null;
|
Coding coding = null;
|
||||||
CodeableConcept codeableConcept = null;
|
CodeableConcept codeableConcept = null;
|
||||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
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("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.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
||||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
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.ITermCodeSystemStorageSvc;
|
||||||
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
|
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
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);
|
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
ourLog.info(resp);
|
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(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||||
|
|
||||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
assertEquals(ValueSetOperationProvider.DISPLAY, 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("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||||
|
|
||||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
|
||||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
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);
|
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
ourLog.info(resp);
|
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(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||||
|
|
||||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
assertEquals(ValueSetOperationProvider.DISPLAY, 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("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
|
@Test
|
||||||
|
|
|
@ -34,6 +34,7 @@ import ca.uhn.fhir.validation.IValidatorModule;
|
||||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||||
import ca.uhn.fhir.validation.ValidationResult;
|
import ca.uhn.fhir.validation.ValidationResult;
|
||||||
import org.apache.commons.io.IOUtils;
|
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.support.UnknownCodeSystemWarningValidationSupport;
|
||||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
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.AfterEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.util.AopTestUtils;
|
import org.springframework.test.util.AopTestUtils;
|
||||||
|
|
||||||
|
@ -85,15 +89,21 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
private ValidationSettings myValidationSettings;
|
private ValidationSettings myValidationSettings;
|
||||||
@Autowired
|
@Autowired
|
||||||
private UnknownCodeSystemWarningValidationSupport myUnknownCodeSystemWarningValidationSupport;
|
private UnknownCodeSystemWarningValidationSupport myUnknownCodeSystemWarningValidationSupport;
|
||||||
|
@Autowired
|
||||||
|
private InMemoryTerminologyServerValidationSupport myInMemoryTerminologyServerValidationSupport;
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void after() {
|
public void after() {
|
||||||
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
|
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
|
||||||
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
|
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
|
||||||
|
|
||||||
myStorageSettings.setAllowExternalReferences(new JpaStorageSettings().isAllowExternalReferences());
|
JpaStorageSettings defaults = new JpaStorageSettings();
|
||||||
myStorageSettings.setMaximumExpansionSize(JpaStorageSettings.DEFAULT_MAX_EXPANSION_SIZE);
|
myStorageSettings.setAllowExternalReferences(defaults.isAllowExternalReferences());
|
||||||
myStorageSettings.setPreExpandValueSets(new JpaStorageSettings().isPreExpandValueSets());
|
myStorageSettings.setMaximumExpansionSize(defaults.getMaximumExpansionSize());
|
||||||
|
myStorageSettings.setPreExpandValueSets(defaults.isPreExpandValueSets());
|
||||||
|
myStorageSettings.setIssueSeverityForCodeDisplayMismatch(defaults.getIssueSeverityForCodeDisplayMismatch());
|
||||||
|
|
||||||
|
myInMemoryTerminologyServerValidationSupport.setIssueSeverityForCodeDisplayMismatch(defaults.getIssueSeverityForCodeDisplayMismatch());
|
||||||
|
|
||||||
TermReadSvcImpl.setInvokeOnNextCallForUnitTest(null);
|
TermReadSvcImpl.setInvokeOnNextCallForUnitTest(null);
|
||||||
|
|
||||||
|
@ -125,7 +135,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
ourLog.info(encoded);
|
ourLog.info(encoded);
|
||||||
assertEquals(1, oo.getIssue().size(), encoded);
|
assertEquals(1, oo.getIssue().size(), encoded);
|
||||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
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(),
|
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
||||||
containsString("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"));
|
containsString("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"));
|
||||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity(), encoded);
|
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity(), encoded);
|
||||||
|
@ -159,7 +169,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
ourLog.info(encoded);
|
ourLog.info(encoded);
|
||||||
assertEquals(1, oo.getIssue().size());
|
assertEquals(1, oo.getIssue().size());
|
||||||
assertThat(oo.getIssueFirstRep().getDiagnostics(),
|
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(),
|
assertThat(oo.getIssueFirstRep().getDiagnostics(),
|
||||||
containsString("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"));
|
containsString("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"));
|
||||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity());
|
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'"));
|
containsString("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"));
|
||||||
assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssue().get(0).getSeverity());
|
assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssue().get(0).getSeverity());
|
||||||
assertThat(oo.getIssue().get(1).getDiagnostics(),
|
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());
|
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(1).getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +249,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
ourLog.info(encoded);
|
ourLog.info(encoded);
|
||||||
assertEquals(1, oo.getIssue().size());
|
assertEquals(1, oo.getIssue().size());
|
||||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
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());
|
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +345,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
ourLog.info(encoded);
|
ourLog.info(encoded);
|
||||||
assertEquals(1, oo.getIssue().size());
|
assertEquals(1, oo.getIssue().size());
|
||||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
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(),
|
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
||||||
containsString("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code1"));
|
containsString("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code1"));
|
||||||
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
assertThat(oo.getIssue().get(0).getDiagnostics(),
|
||||||
|
@ -343,7 +353,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity());
|
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(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(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.IssueType.PROCESSING, oo.getIssue().get(0).getCode());
|
||||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity());
|
assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity());
|
||||||
assertEquals(2, oo.getIssue().get(0).getLocation().size());
|
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);
|
String outcomeStr = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||||
assertThat(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
|
// Before, the VS wasn't pre-expanded. Try again with it pre-expanded
|
||||||
runInTransaction(() -> {
|
runInTransaction(() -> {
|
||||||
|
@ -538,7 +548,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
outcomeStr = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
outcomeStr = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||||
ourLog.info("Validation outcome: {}", outcomeStr);
|
ourLog.info("Validation outcome: {}", outcomeStr);
|
||||||
assertThat(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
|
@Test
|
||||||
public void testValidateUsingExternallyDefinedCodeMisMatchDisplay_ShouldError() {
|
public void testValidateUsingExternallyDefinedCodeMisMatchDisplay_InMemory_ShouldLogWarning() {
|
||||||
CodeSystem codeSystem = new CodeSystem();
|
CodeSystem codeSystem = new CodeSystem();
|
||||||
codeSystem.setUrl("http://foo");
|
codeSystem.setUrl("http://foo");
|
||||||
codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
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'"));
|
containsString("None of the codings provided are in the value set 'IdentifierType'"));
|
||||||
assertThat(OperationOutcomeUtil.getFirstIssueDetails(myFhirContext, oo),
|
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)"));
|
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());
|
assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssue().get(1).getSeverity());
|
||||||
assertThat(oo.getIssue().get(1).getDiagnostics(), containsString("Unable to validate code http://foo#bar - Concept Display "));
|
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 {
|
private OperationOutcome doTestValidateResourceContainingProfileDeclaration(String methodName, EncodingEnum enc) throws IOException {
|
||||||
|
@ -1994,6 +2004,105 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
assertThat(encoded, containsString("No issues detected"));
|
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
|
* See #1780
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -148,7 +148,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
||||||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "child10", null, "http://vs");
|
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "child10", null, "http://vs");
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
assertTrue(outcome.isOk());
|
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");
|
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "childX", null, "http://vs");
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
|
@ -160,7 +160,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
||||||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "code1", null, "http://vs");
|
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "code1", null, "http://vs");
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
assertTrue(outcome.isOk());
|
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");
|
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "codeX", null, "http://vs");
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
|
@ -251,7 +251,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
||||||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "child10", null, "http://vs");
|
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "child10", null, "http://vs");
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
assertTrue(outcome.isOk());
|
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");
|
outcome = myValidationSupport.validateCode(ctx, options, "http://cs", "childX", null, "http://vs");
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
|
@ -263,7 +263,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
||||||
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "code1", null, "http://vs");
|
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "code1", null, "http://vs");
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
assertTrue(outcome.isOk());
|
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");
|
outcome = myValidationSupport.validateCode(ctx, options, "http://cs-np", "codeX", null, "http://vs");
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
|
@ -344,7 +344,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
|
||||||
Coding coding = null;
|
Coding coding = null;
|
||||||
CodeableConcept codeableConcept = null;
|
CodeableConcept codeableConcept = null;
|
||||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
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("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());
|
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);
|
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
|
|
||||||
assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
assertTrue(((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());
|
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
|
@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.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
@ -1294,14 +1295,14 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv
|
||||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
ourLog.info(resp);
|
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(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||||
|
|
||||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
assertEquals(ValueSetOperationProvider.DISPLAY, 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("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||||
|
|
||||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
|
||||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
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
|
@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.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
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.partition.IRequestPartitionHelperSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
|
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.Answers;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.quartz.JobKey;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
|
@ -466,7 +466,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
||||||
String code = "male";
|
String code = "male";
|
||||||
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
IValidationSupport.CodeValidationResult outcome = myValueSetDao.validateCode(new CodeType(valueSetUrl), null, new CodeType(code), new CodeType(codeSystemUrl), null, null, null, mySrd);
|
||||||
assertTrue(outcome.isOk());
|
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
|
// Validate Code - Bad
|
||||||
code = "AAA";
|
code = "AAA";
|
||||||
|
@ -1635,10 +1635,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
||||||
code = "28571000087109";
|
code = "28571000087109";
|
||||||
display = "BLAH";
|
display = "BLAH";
|
||||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||||
assertFalse(outcome.isOk());
|
assertTrue(outcome.isOk());
|
||||||
assertEquals(null, outcome.getCode());
|
assertEquals("28571000087109", outcome.getCode());
|
||||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
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("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());
|
assertEquals("0.17", outcome.getCodeSystemVersion());
|
||||||
|
|
||||||
// Validate code - good code, good display
|
// Validate code - good code, good display
|
||||||
|
@ -1680,11 +1681,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
|
||||||
code = "28571000087109";
|
code = "28571000087109";
|
||||||
display = "BLAH";
|
display = "BLAH";
|
||||||
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
outcome = myValueSetDao.validateCode(null, vsId, new CodeType(code), new UriType(codeSystemUrl), new StringType(display), null, null, mySrd);
|
||||||
assertFalse(outcome.isOk());
|
assertTrue(outcome.isOk());
|
||||||
assertEquals(null, outcome.getCode());
|
assertEquals("28571000087109", outcome.getCode());
|
||||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
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
|
// Validate code - good code, good display
|
||||||
codeSystemUrl = "http://snomed.info/sct";
|
codeSystemUrl = "http://snomed.info/sct";
|
||||||
|
|
|
@ -117,7 +117,7 @@ public class FhirResourceDaoR5ValueSetTest extends BaseJpaR5Test {
|
||||||
Coding coding = null;
|
Coding coding = null;
|
||||||
CodeableConcept codeableConcept = null;
|
CodeableConcept codeableConcept = null;
|
||||||
IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
|
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("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());
|
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.entity.TermValueSetPreExpansionStatusEnum;
|
||||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
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.ITermCodeSystemStorageSvc;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
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);
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
ourLog.info(resp);
|
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(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
|
||||||
|
|
||||||
assertEquals("message", respParam.getParameter().get(1).getName());
|
assertEquals(ValueSetOperationProvider.DISPLAY, 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("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
|
||||||
|
|
||||||
assertEquals("display", respParam.getParameter().get(2).getName());
|
assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName());
|
||||||
assertEquals("Male", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
|
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
|
// Good code and system, but not in specified valueset
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*-
|
/*-
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR JPA Server - Master Data Management
|
* HAPI FHIR - Master Data Management
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
* %%
|
* %%
|
||||||
|
|
|
@ -42,7 +42,8 @@ public class MailSvcIT {
|
||||||
// execute
|
// execute
|
||||||
fixture.sendMail(email);
|
fixture.sendMail(email);
|
||||||
// validate
|
// validate
|
||||||
assertTrue(ourGreenMail.waitForIncomingEmail(5000, 1));
|
boolean condition = ourGreenMail.waitForIncomingEmail(5000, 1);
|
||||||
|
assertTrue(condition);
|
||||||
final MimeMessage[] receivedMessages = ourGreenMail.getReceivedMessages();
|
final MimeMessage[] receivedMessages = ourGreenMail.getReceivedMessages();
|
||||||
assertEquals(1, receivedMessages.length);
|
assertEquals(1, receivedMessages.length);
|
||||||
assertEquals(SUBJECT, receivedMessages[0].getSubject());
|
assertEquals(SUBJECT, receivedMessages[0].getSubject());
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.jpa.api.config;
|
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.HistoryCountModeEnum;
|
||||||
import ca.uhn.fhir.jpa.api.model.WarmCacheEntry;
|
import ca.uhn.fhir.jpa.api.model.WarmCacheEntry;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
||||||
|
@ -334,6 +335,13 @@ public class JpaStorageSettings extends StorageSettings {
|
||||||
*/
|
*/
|
||||||
private boolean myResourceHistoryDbEnabled = true;
|
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.
|
* This setting allows preventing a conditional update to invalidate the match criteria.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -2376,6 +2384,41 @@ public class JpaStorageSettings extends StorageSettings {
|
||||||
return myNonResourceDbHistoryEnabled;
|
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.
|
* This setting controls whether MdmLink and other non-resource DB history is enabled.
|
||||||
* <p/>
|
* <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;
|
package ca.uhn.fhir.models;
|
||||||
|
|
||||||
import org.springframework.core.io.AbstractResource;
|
import org.springframework.core.io.AbstractResource;
|
||||||
|
|
|
@ -57,16 +57,54 @@ import static org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTermi
|
||||||
@SuppressWarnings("EnhancedSwitchMigration")
|
@SuppressWarnings("EnhancedSwitchMigration")
|
||||||
public class InMemoryTerminologyServerValidationSupport implements IValidationSupport {
|
public class InMemoryTerminologyServerValidationSupport implements IValidationSupport {
|
||||||
private static final String OUR_PIPE_CHARACTER = "|";
|
private static final String OUR_PIPE_CHARACTER = "|";
|
||||||
|
|
||||||
private final FhirContext myCtx;
|
private final FhirContext myCtx;
|
||||||
private final VersionCanonicalizer myVersionCanonicalizer;
|
private final VersionCanonicalizer myVersionCanonicalizer;
|
||||||
|
private IssueSeverity myIssueSeverityForCodeDisplayMismatch = IssueSeverity.WARNING;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param theCtx A FhirContext for the FHIR version being validated
|
||||||
|
*/
|
||||||
public InMemoryTerminologyServerValidationSupport(FhirContext theCtx) {
|
public InMemoryTerminologyServerValidationSupport(FhirContext theCtx) {
|
||||||
Validate.notNull(theCtx, "theCtx must not be null");
|
Validate.notNull(theCtx, "theCtx must not be null");
|
||||||
myCtx = theCtx;
|
myCtx = theCtx;
|
||||||
myVersionCanonicalizer = new VersionCanonicalizer(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
|
@Override
|
||||||
public FhirContext getFhirContext() {
|
public FhirContext getFhirContext() {
|
||||||
return myCtx;
|
return myCtx;
|
||||||
|
@ -517,22 +555,26 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
||||||
.setCodeSystemName(codeSystemResourceName)
|
.setCodeSystemName(codeSystemResourceName)
|
||||||
.setCodeSystemVersion(csVersion);
|
.setCodeSystemVersion(csVersion);
|
||||||
if (isNotBlank(theValueSetUrl)) {
|
if (isNotBlank(theValueSetUrl)) {
|
||||||
codeValidationResult.setMessage(
|
populateSourceDetailsForInMemoryExpansion(theValueSetUrl, codeValidationResult);
|
||||||
"Code was validated against in-memory expansion of ValueSet: " + theValueSetUrl);
|
|
||||||
}
|
}
|
||||||
return codeValidationResult;
|
return codeValidationResult;
|
||||||
} else {
|
} else {
|
||||||
String message = "Concept Display \"" + theDisplayToValidate + "\" does not match expected \""
|
String messageAppend = "";
|
||||||
+ nextExpansionCode.getDisplay() + "\"";
|
|
||||||
if (isNotBlank(theValueSetUrl)) {
|
if (isNotBlank(theValueSetUrl)) {
|
||||||
message += " for in-memory expansion of ValueSet: " + theValueSetUrl;
|
messageAppend = " for in-memory expansion of ValueSet: " + theValueSetUrl;
|
||||||
}
|
}
|
||||||
return new CodeValidationResult()
|
CodeValidationResult codeValidationResult = createResultForDisplayMismatch(
|
||||||
.setSeverity(IssueSeverity.ERROR)
|
myCtx,
|
||||||
.setDisplay(nextExpansionCode.getDisplay())
|
theCodeToValidate,
|
||||||
.setMessage(message)
|
theDisplayToValidate,
|
||||||
.setCodeSystemName(codeSystemResourceName)
|
nextExpansionCode.getDisplay(),
|
||||||
.setCodeSystemVersion(csVersion);
|
csVersion,
|
||||||
|
messageAppend,
|
||||||
|
getIssueSeverityForCodeDisplayMismatch());
|
||||||
|
if (isNotBlank(theValueSetUrl)) {
|
||||||
|
populateSourceDetailsForInMemoryExpansion(theValueSetUrl, codeValidationResult);
|
||||||
|
}
|
||||||
|
return codeValidationResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1194,24 +1236,59 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
||||||
return theVersion;
|
return theVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum FailureType {
|
private static void populateSourceDetailsForInMemoryExpansion(
|
||||||
UNKNOWN_CODE_SYSTEM,
|
String theValueSetUrl, CodeValidationResult codeValidationResult) {
|
||||||
OTHER
|
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 static CodeValidationResult createResultForDisplayMismatch(
|
||||||
private final FailureType myFailureType;
|
FhirContext theFhirContext,
|
||||||
|
String theCode,
|
||||||
|
String theDisplay,
|
||||||
|
String theExpectedDisplay,
|
||||||
|
String theCodeSystemVersion,
|
||||||
|
String theMessageAppend,
|
||||||
|
IssueSeverity theIssueSeverityForCodeDisplayMismatch) {
|
||||||
|
|
||||||
public ExpansionCouldNotBeCompletedInternallyException(String theMessage, FailureType theFailureType) {
|
String message;
|
||||||
super(theMessage);
|
IssueSeverity issueSeverity = theIssueSeverityForCodeDisplayMismatch;
|
||||||
myFailureType = theFailureType;
|
if (issueSeverity == IssueSeverity.INFORMATION) {
|
||||||
}
|
message = null;
|
||||||
|
issueSeverity = null;
|
||||||
public FailureType getFailureType() {
|
} else {
|
||||||
return myFailureType;
|
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(
|
private static void flattenAndConvertCodesDstu2(
|
||||||
|
@ -1273,4 +1350,24 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu
|
||||||
flattenAndConvertCodesR5(next.getContains(), theFhirVersionIndependentConcepts);
|
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.setSeverity(myNonExistentCodeSystemSeverity);
|
||||||
result.setMessage("CodeSystem is unknown and can't be validated: " + theCodeSystem);
|
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) {
|
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.setCode(theCode);
|
||||||
|
result.setSeverity(null);
|
||||||
|
result.setMessage(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -37,7 +37,13 @@ abstract class BaseValidatorBridge implements IValidatorModule {
|
||||||
ResultSeverityEnum.fromCode(riMessage.getLevel().toCode()));
|
ResultSeverityEnum.fromCode(riMessage.getLevel().toCode()));
|
||||||
}
|
}
|
||||||
if (riMessage.getMessageId() != null) {
|
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) {
|
if (riMessage.sliceText != null && riMessage.sliceText.length > 0) {
|
||||||
hapiMessage.setSliceMessages(Arrays.asList(riMessage.sliceText));
|
hapiMessage.setSliceMessages(Arrays.asList(riMessage.sliceText));
|
||||||
|
|
|
@ -255,23 +255,30 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
||||||
String code = theResult.getCode();
|
String code = theResult.getCode();
|
||||||
String display = theResult.getDisplay();
|
String display = theResult.getDisplay();
|
||||||
|
|
||||||
String issueSeverity = theResult.getSeverityCode();
|
String issueSeverityCode = theResult.getSeverityCode();
|
||||||
String message = theResult.getMessage();
|
String message = theResult.getMessage();
|
||||||
if (isNotBlank(code)) {
|
ValidationMessage.IssueSeverity issueSeverity = null;
|
||||||
retVal = new ValidationResult(
|
if (issueSeverityCode != null) {
|
||||||
theSystem,
|
issueSeverity = ValidationMessage.IssueSeverity.fromCode(issueSeverityCode);
|
||||||
null,
|
} else if (isNotBlank(message)) {
|
||||||
new org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent()
|
issueSeverity = ValidationMessage.IssueSeverity.INFORMATION;
|
||||||
.setCode(code)
|
|
||||||
.setDisplay(display),
|
|
||||||
null);
|
|
||||||
} else if (isNotBlank(issueSeverity)) {
|
|
||||||
retVal = new ValidationResult(
|
|
||||||
ValidationMessage.IssueSeverity.fromCode(issueSeverity),
|
|
||||||
message,
|
|
||||||
TerminologyServiceErrorClass.UNKNOWN,
|
|
||||||
null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
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);
|
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 static class ResourceKey {
|
||||||
private final int myHashCode;
|
private final int myHashCode;
|
||||||
private final String myResourceName;
|
private final String myResourceName;
|
||||||
|
@ -729,30 +762,4 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
||||||
return myHashCode;
|
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.HashMap;
|
||||||
import java.util.Map;
|
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.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
@ -62,7 +65,7 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
||||||
// ValidateCode
|
// ValidateCode
|
||||||
outcome = myChain.validateCode(valCtx, options, null, "txt", null, valueSetUrl);
|
outcome = myChain.validateCode(valCtx, options, null, "txt", null, valueSetUrl);
|
||||||
assertTrue(outcome.isOk());
|
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());
|
assertEquals("txt", outcome.getCode());
|
||||||
|
|
||||||
// ValidateCodeInValueSet
|
// ValidateCodeInValueSet
|
||||||
|
@ -70,7 +73,7 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
||||||
assertNotNull(valueSet);
|
assertNotNull(valueSet);
|
||||||
outcome = myChain.validateCodeInValueSet(valCtx, options, null, "txt", null, valueSet);
|
outcome = myChain.validateCodeInValueSet(valCtx, options, null, "txt", null, valueSet);
|
||||||
assertTrue(outcome.isOk());
|
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());
|
assertEquals("txt", outcome.getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +94,7 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
||||||
IValidationSupport.CodeValidationResult outcome;
|
IValidationSupport.CodeValidationResult outcome;
|
||||||
|
|
||||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code1", null, vs);
|
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());
|
assertTrue(outcome.isOk());
|
||||||
|
|
||||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code99", null, vs);
|
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code99", null, vs);
|
||||||
|
@ -127,7 +130,9 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
||||||
IValidationSupport.CodeValidationResult outcome;
|
IValidationSupport.CodeValidationResult outcome;
|
||||||
|
|
||||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code1", null, vs);
|
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());
|
assertTrue(outcome.isOk());
|
||||||
|
|
||||||
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code99", null, vs);
|
outcome = myChain.validateCodeInValueSet(valCtx, options, "http://cs", "code99", null, vs);
|
||||||
|
@ -243,10 +248,13 @@ public class InMemoryTerminologyServerValidationSupportTest {
|
||||||
code = "28571000087109";
|
code = "28571000087109";
|
||||||
display = "BLAH";
|
display = "BLAH";
|
||||||
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, display, valueSetUrl);
|
outcome = mySvc.validateCode(valCtx, options, codeSystemUrl, code, display, valueSetUrl);
|
||||||
assertFalse(outcome.isOk());
|
assertTrue(outcome.isOk());
|
||||||
assertEquals(null, outcome.getCode());
|
assertEquals("28571000087109", outcome.getCode());
|
||||||
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay());
|
||||||
assertEquals("0.17", outcome.getCodeSystemVersion());
|
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
|
// Validate code - good code, good display
|
||||||
codeSystemUrl = "http://snomed.info/sct";
|
codeSystemUrl = "http://snomed.info/sct";
|
||||||
|
|
|
@ -798,7 +798,7 @@ public class FhirInstanceValidatorDstu3Test {
|
||||||
Patient resource = loadResource("/dstu3/nl/nl-core-patient-01.json", Patient.class);
|
Patient resource = loadResource("/dstu3/nl/nl-core-patient-01.json", Patient.class);
|
||||||
ValidationResult results = myVal.validateWithResult(resource);
|
ValidationResult results = myVal.validateWithResult(resource);
|
||||||
List<SingleValidationMessage> outcome = logResultsAndReturnNonInformationalOnes(results);
|
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 {
|
private void loadNL() throws IOException {
|
||||||
|
|
Loading…
Reference in New Issue