terminology service bug fixes
This commit is contained in:
parent
5078a95016
commit
6f0d54a8dc
|
@ -113,6 +113,8 @@ import org.hl7.fhir.r5.renderers.OperationOutcomeRenderer;
|
|||
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
||||
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpander;
|
||||
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext.TerminologyServiceProtectionException;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
|
||||
import org.hl7.fhir.r5.terminologies.validation.VSCheckerException;
|
||||
import org.hl7.fhir.r5.terminologies.validation.ValueSetValidator;
|
||||
|
@ -1185,12 +1187,17 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
|||
} catch (VSCheckerException e) {
|
||||
if (e.isWarning()) {
|
||||
localWarning = e.getMessage();
|
||||
} else {
|
||||
} else {
|
||||
localError = e.getMessage();
|
||||
}
|
||||
if (e.getIssues() != null) {
|
||||
issues.addAll(e.getIssues());
|
||||
}
|
||||
} catch (TerminologyServiceProtectionException e) {
|
||||
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, e.getType());
|
||||
iss.getDetails().setText(e.getMessage());
|
||||
issues.add(iss);
|
||||
return new ValidationResult(IssueSeverity.ERROR, e.getMessage(), e.getError(), issues);
|
||||
} catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
localError = e.getMessage();
|
||||
|
@ -1246,15 +1253,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
|||
}
|
||||
|
||||
protected ValueSetExpander constructValueSetExpanderSimple() {
|
||||
return new ValueSetExpander(this);
|
||||
return new ValueSetExpander(this, new TerminologyOperationContext(this));
|
||||
}
|
||||
|
||||
protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs, ValidationContextCarrier ctxt) {
|
||||
return new ValueSetValidator(options, vs, this, ctxt, expParameters, tcc.getTxcaps());
|
||||
return new ValueSetValidator(this, new TerminologyOperationContext(this), options, vs, ctxt, expParameters, tcc.getTxcaps());
|
||||
}
|
||||
|
||||
protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs) {
|
||||
return new ValueSetValidator(options, vs, this, expParameters, tcc.getTxcaps());
|
||||
return new ValueSetValidator(this, new TerminologyOperationContext(this), options, vs, expParameters, tcc.getTxcaps());
|
||||
}
|
||||
|
||||
protected Parameters constructParameters(ValueSet vs, boolean hierarchical) {
|
||||
|
|
|
@ -594,7 +594,7 @@ public class CanonicalResourceManager<T extends CanonicalResource> {
|
|||
if (list != null) {
|
||||
for (CanonicalResourceManager<T>.CachedCanonicalResource<T> t : list) {
|
||||
possibleMatches = true;
|
||||
if (pvlist == null || pvlist.contains(t.getPackageInfo().getVID())) {
|
||||
if (pvlist == null || t.getPackageInfo() == null || pvlist.contains(t.getPackageInfo().getVID())) {
|
||||
res.add(t.getResource());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,9 +114,12 @@ import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
|||
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
|
||||
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProvider;
|
||||
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProviderExtension;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext.TerminologyServiceProtectionException;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.ValueSetProcessBase;
|
||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
|
||||
|
@ -127,20 +130,18 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
|
||||
private ValueSet focus;
|
||||
private List<String> allErrors = new ArrayList<>();
|
||||
private List<String> requiredSupplements = new ArrayList<>();
|
||||
private int maxExpansionSize = 1000;
|
||||
private WorkingContext dwc = new WorkingContext();
|
||||
|
||||
private boolean checkCodesWhenExpanding;
|
||||
private boolean includeAbstract = true;
|
||||
|
||||
public ValueSetExpander(IWorkerContext context) {
|
||||
this.context = context;
|
||||
public ValueSetExpander(IWorkerContext context, TerminologyOperationContext opContext) {
|
||||
super(context, opContext);
|
||||
}
|
||||
|
||||
public ValueSetExpander(IWorkerContext context, List<String> allErrors) {
|
||||
super();
|
||||
this.context = context;
|
||||
public ValueSetExpander(IWorkerContext context, TerminologyOperationContext opContext, List<String> allErrors) {
|
||||
super(context, opContext);
|
||||
this.allErrors = allErrors;
|
||||
}
|
||||
|
||||
|
@ -151,7 +152,8 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
private ValueSetExpansionContainsComponent addCode(WorkingContext wc, String system, String code, String display, String dispLang, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations, Parameters expParams,
|
||||
boolean isAbstract, boolean inactive, List<ValueSet> filters, boolean noInactive, boolean deprecated, List<ValueSetExpansionPropertyComponent> vsProp,
|
||||
List<ConceptPropertyComponent> csProps, List<org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent> expProps, List<Extension> csExtList, List<Extension> vsExtList, ValueSetExpansionComponent exp) throws ETooCostly {
|
||||
|
||||
opContext.deadCheck();
|
||||
|
||||
if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code, exp))
|
||||
return null;
|
||||
if (noInactive && inactive) {
|
||||
|
@ -311,6 +313,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private void addCodeAndDescendents(WorkingContext wc, ValueSetExpansionContainsComponent focus, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, ValueSet vsSrc, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly {
|
||||
opContext.deadCheck();
|
||||
focus.checkNoModifiers("Expansion.contains", "expanding");
|
||||
ValueSetExpansionContainsComponent np = null;
|
||||
for (String code : getCodesForConcept(focus, expParams)) {
|
||||
|
@ -360,6 +363,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
|
||||
private void addCodeAndDescendents(WorkingContext wc, CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters,
|
||||
ConceptDefinitionComponent exclusion, ConceptFilter filterFunc, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, List<WorkingContext> otherFilters, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly {
|
||||
opContext.deadCheck();
|
||||
def.checkNoModifiers("Code in Code System", "expanding");
|
||||
if (exclusion != null) {
|
||||
if (exclusion.getCode().equals(def.getCode()))
|
||||
|
@ -432,6 +436,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private void excludeCodes(WorkingContext wc, ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params, String ctxt) throws FHIRException {
|
||||
opContext.deadCheck();
|
||||
exc.checkNoModifiers("Compose.exclude", "expanding");
|
||||
if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) {
|
||||
wc.getExcludeSystems().add(exc.getSystem());
|
||||
|
@ -460,6 +465,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private void excludeCodes(WorkingContext wc, ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) {
|
||||
opContext.deadCheck();
|
||||
for (ValueSetExpansionContainsComponent c : expand.getContains()) {
|
||||
excludeCode(wc, c.getSystem(), c.getCode());
|
||||
}
|
||||
|
@ -475,8 +481,11 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
public ValueSetExpansionOutcome expand(ValueSet source, Parameters expParams) {
|
||||
|
||||
allErrors.clear();
|
||||
try {
|
||||
opContext.seeContext(source.getVersionedUrl());
|
||||
|
||||
return expandInternal(source, expParams);
|
||||
} catch (NoTerminologyServiceException e) {
|
||||
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
|
||||
|
@ -486,6 +495,12 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
|
||||
// that might fail too, but it might not, later.
|
||||
return new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.INTERNAL_ERROR, allErrors);
|
||||
} catch (TerminologyServiceProtectionException e) {
|
||||
if (opContext.isOriginal()) {
|
||||
return new ValueSetExpansionOutcome(e.getMessage(), e.getError(), allErrors);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} catch (ETooCostly e) {
|
||||
return new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.TOO_COSTLY, allErrors);
|
||||
} catch (Exception e) {
|
||||
|
@ -574,8 +589,8 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
if (dwc.getTotal() >= 0) {
|
||||
focus.getExpansion().setTotal(dwc.getTotal());
|
||||
}
|
||||
if (!requiredSupplements.isEmpty()) {
|
||||
return new ValueSetExpansionOutcome("Required supplements not found: "+requiredSupplements.toString(), TerminologyServiceErrorClass.BUSINESS_RULE, allErrors);
|
||||
if (!requiredSupplements.isEmpty()) {
|
||||
return new ValueSetExpansionOutcome(context.formatMessagePlural(requiredSupplements.size(), I18nConstants.VALUESET_SUPPLEMENT_MISSING, CommaSeparatedStringBuilder.build(requiredSupplements)), TerminologyServiceErrorClass.BUSINESS_RULE, allErrors);
|
||||
}
|
||||
if (!expParams.hasParameter("includeDefinition") || !expParams.getParameterBool("includeDefinition")) {
|
||||
focus.setCompose(null);
|
||||
|
@ -656,7 +671,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
expParams = expParams.copy();
|
||||
expParams.addParameter("activeOnly", true);
|
||||
}
|
||||
ValueSetExpansionOutcome vso = new ValueSetExpander(context, allErrors).expand(vs, expParams);
|
||||
ValueSetExpansionOutcome vso = new ValueSetExpander(context, opContext.copy(), allErrors).expand(vs, expParams);
|
||||
if (vso.getError() != null) {
|
||||
addErrors(vso.getAllErrors());
|
||||
throw fail("Unable to expand imported value set "+vs.getUrl()+": " + vso.getError());
|
||||
|
@ -697,6 +712,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
public void copyExpansion(WorkingContext wc,List<ValueSetExpansionContainsComponent> list) {
|
||||
opContext.deadCheck();
|
||||
for (ValueSetExpansionContainsComponent cc : list) {
|
||||
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
|
||||
n.setSystem(cc.getSystem());
|
||||
|
@ -725,6 +741,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filter, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, ValueSet vsSrc, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly {
|
||||
opContext.deadCheck();
|
||||
for (ValueSetExpansionContainsComponent c : list) {
|
||||
c.checkNoModifiers("Imported Expansion in Code System", "expanding");
|
||||
ValueSetExpansionContainsComponent np = addCode(dwc, c.getSystem(), c.getCode(), c.getDisplay(), vsSrc.getLanguage(), parent, null, expParams, c.getAbstract(), c.getInactive(),
|
||||
|
@ -734,6 +751,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private void includeCodes(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams, boolean heirarchical, boolean noInactive, List<Extension> extensions, ValueSet valueSet) throws ETooCostly, FileNotFoundException, IOException, FHIRException, CodeSystemProviderExtension {
|
||||
opContext.deadCheck();
|
||||
inc.checkNoModifiers("Compose.include", "expanding");
|
||||
List<ValueSet> imports = new ArrayList<ValueSet>();
|
||||
for (CanonicalType imp : inc.getValueSet()) {
|
||||
|
@ -764,6 +782,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private void doServerIncludeCodes(ConceptSetComponent inc, boolean heirarchical, ValueSetExpansionComponent exp, List<ValueSet> imports, Parameters expParams, List<Extension> extensions, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps) throws FHIRException, CodeSystemProviderExtension, ETooCostly {
|
||||
opContext.deadCheck();
|
||||
CodeSystemProvider csp = CodeSystemProvider.factory(inc.getSystem());
|
||||
if (csp != null) {
|
||||
csp.includeCodes(inc, heirarchical, exp, imports, expParams, extensions, noInactive, vsProps);
|
||||
|
@ -800,6 +819,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
|
||||
|
||||
public void doInternalIncludeCodes(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams, List<ValueSet> imports, CodeSystem cs, boolean noInactive, Resource vsSrc) throws NoTerminologyServiceException, TerminologyServiceException, FHIRException, ETooCostly {
|
||||
opContext.deadCheck();
|
||||
if (cs == null) {
|
||||
if (context.isNoTerminologyServer())
|
||||
throw failTSE("Unable to find code system " + inc.getSystem().toString());
|
||||
|
@ -884,6 +904,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
private void processFilter(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams,
|
||||
List<ValueSet> imports, CodeSystem cs, boolean noInactive, ConceptSetFilterComponent fc, WorkingContext wc, List<WorkingContext> filters)
|
||||
throws ETooCostly {
|
||||
opContext.deadCheck();
|
||||
if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) {
|
||||
// special: all codes in the target code system under the value
|
||||
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
|
||||
|
@ -919,6 +940,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
|
|||
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
|
||||
if (def.getDisplay().contains(fc.getValue()) && passesOtherFilters(filters, cs, def.getCode())) {
|
||||
for (String code : getCodesForConcept(def, expParams)) {
|
||||
opContext.deadCheck();
|
||||
ValueSetExpansionContainsComponent t = addCode(wc, inc.getSystem(), code, def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
|
||||
imports, noInactive, false, exp.getProperty(), makeCSProps(def.getDefinition(), def.getProperty()), null, def.getExtension(), null, exp);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
package org.hl7.fhir.r5.terminologies.utilities;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext.TerminologyServiceProtectionException;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class TerminologyOperationContext {
|
||||
|
||||
public class TerminologyServiceProtectionException extends FHIRException {
|
||||
|
||||
private TerminologyServiceErrorClass error;
|
||||
private IssueType type;
|
||||
|
||||
protected TerminologyServiceProtectionException(String message, TerminologyServiceErrorClass error, IssueType type) {
|
||||
super(message);
|
||||
this.error = error;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public TerminologyServiceErrorClass getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public IssueType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean debugging = java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("-agentlib:jdwp") > 0;
|
||||
private static final int EXPANSION_DEAD_TIME_SECS = 60;
|
||||
private long deadTime;
|
||||
private List<String> contexts = new ArrayList<>();
|
||||
private IWorkerContext worker;
|
||||
private boolean original;
|
||||
|
||||
public TerminologyOperationContext(IWorkerContext worker) {
|
||||
super();
|
||||
this.worker = worker;
|
||||
this.original = true;
|
||||
|
||||
if (EXPANSION_DEAD_TIME_SECS == 0 || debugging) {
|
||||
deadTime = 0;
|
||||
} else {
|
||||
deadTime = System.currentTimeMillis() + (EXPANSION_DEAD_TIME_SECS * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private TerminologyOperationContext() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TerminologyOperationContext copy() {
|
||||
TerminologyOperationContext ret = new TerminologyOperationContext();
|
||||
ret.worker = worker;
|
||||
ret.contexts.addAll(contexts);
|
||||
ret.deadTime = deadTime;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void deadCheck() {
|
||||
if (deadTime != 0 && System.currentTimeMillis() > deadTime) {
|
||||
throw new TerminologyServiceProtectionException(worker.formatMessage(I18nConstants.VALUESET_TOO_COSTLY_TIME, contexts.get(0), EXPANSION_DEAD_TIME_SECS), TerminologyServiceErrorClass.TOO_COSTLY, IssueType.TOOCOSTLY);
|
||||
}
|
||||
}
|
||||
|
||||
public void seeContext(String context) {
|
||||
if (contexts.contains(context)) {
|
||||
throw new TerminologyServiceProtectionException(worker.formatMessage(I18nConstants.VALUESET_CIRCULAR_REFERENCE, context, contexts.toString()), TerminologyServiceErrorClass.BUSINESS_RULE, IssueType.BUSINESSRULE);
|
||||
}
|
||||
contexts.add(context);
|
||||
}
|
||||
|
||||
public boolean isOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -25,7 +25,14 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
|||
public class ValueSetProcessBase {
|
||||
|
||||
protected IWorkerContext context;
|
||||
|
||||
protected TerminologyOperationContext opContext;
|
||||
protected List<String> requiredSupplements = new ArrayList<>();
|
||||
|
||||
protected ValueSetProcessBase(IWorkerContext context, TerminologyOperationContext opContext) {
|
||||
super();
|
||||
this.context = context;
|
||||
this.opContext = opContext;
|
||||
}
|
||||
public static class AlternateCodesProcessingRules {
|
||||
private boolean all;
|
||||
private List<String> uses = new ArrayList<>();
|
||||
|
@ -106,7 +113,9 @@ public class ValueSetProcessBase {
|
|||
break;
|
||||
}
|
||||
result.setCode(type);
|
||||
result.addLocation(location);
|
||||
if (location != null) {
|
||||
result.addLocation(location);
|
||||
}
|
||||
result.getDetails().setText(message);
|
||||
ArrayList<OperationOutcomeIssueComponent> list = new ArrayList<>();
|
||||
list.add(result);
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.hl7.fhir.exceptions.NoTerminologyServiceException;
|
|||
import org.hl7.fhir.r5.context.ContextUtilities;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
|
||||
import org.hl7.fhir.r5.extensions.ExtensionConstants;
|
||||
import org.hl7.fhir.r5.model.CanonicalType;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
|
||||
|
@ -79,6 +80,7 @@ import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
|
|||
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProvider;
|
||||
import org.hl7.fhir.r5.terminologies.providers.SpecialCodeSystem;
|
||||
import org.hl7.fhir.r5.terminologies.providers.URICodeSystem;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.ValueSetProcessBase;
|
||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
|
@ -106,18 +108,18 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
private Set<String> unknownSystems;
|
||||
private boolean throwToServer;
|
||||
|
||||
public ValueSetValidator(ValidationOptions options, ValueSet source, IWorkerContext context, Parameters expansionProfile, TerminologyCapabilities txCaps) {
|
||||
public ValueSetValidator(IWorkerContext context, TerminologyOperationContext opContext, ValidationOptions options, ValueSet source, Parameters expansionProfile, TerminologyCapabilities txCaps) {
|
||||
super(context, opContext);
|
||||
this.valueset = source;
|
||||
this.context = context;
|
||||
this.options = options;
|
||||
this.expansionProfile = expansionProfile;
|
||||
this.txCaps = txCaps;
|
||||
analyseValueSet();
|
||||
}
|
||||
|
||||
public ValueSetValidator(ValidationOptions options, ValueSet source, IWorkerContext context, ValidationContextCarrier ctxt, Parameters expansionProfile, TerminologyCapabilities txCaps) {
|
||||
public ValueSetValidator(IWorkerContext context, TerminologyOperationContext opContext, ValidationOptions options, ValueSet source, ValidationContextCarrier ctxt, Parameters expansionProfile, TerminologyCapabilities txCaps) {
|
||||
super(context, opContext);
|
||||
this.valueset = source;
|
||||
this.context = context;
|
||||
this.options = options.copy();
|
||||
this.options.setEnglishOk(true);
|
||||
this.localContext = ctxt;
|
||||
|
@ -143,6 +145,13 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private void analyseValueSet() {
|
||||
if (valueset != null) {
|
||||
opContext.seeContext(valueset.getVersionedUrl());
|
||||
for (Extension s : valueset.getExtensionsByUrl(ExtensionConstants.EXT_VSSUPPLEMENT)) {
|
||||
requiredSupplements.add(s.getValue().primitiveValue());
|
||||
}
|
||||
}
|
||||
|
||||
altCodeParams.seeParameters(expansionProfile);
|
||||
altCodeParams.seeValueSet(valueset);
|
||||
if (localContext != null) {
|
||||
|
@ -158,6 +167,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private void analyseComponent(ConceptSetComponent i) {
|
||||
opContext.deadCheck();
|
||||
if (i.getSystemElement().hasExtension(ToolingExtensions.EXT_VALUESET_SYSTEM)) {
|
||||
String ref = i.getSystemElement().getExtensionString(ToolingExtensions.EXT_VALUESET_SYSTEM);
|
||||
if (ref.startsWith("#")) {
|
||||
|
@ -179,6 +189,8 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
public ValidationResult validateCode(String path, CodeableConcept code) throws FHIRException {
|
||||
opContext.deadCheck();
|
||||
|
||||
// first, we validate the codings themselves
|
||||
ValidationProcessInfo info = new ValidationProcessInfo();
|
||||
|
||||
|
@ -250,7 +262,9 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.CODEINVALID, path, msg));
|
||||
}
|
||||
}
|
||||
if (info.hasErrors()) {
|
||||
if (!checkRequiredSupplements(info)) {
|
||||
return new ValidationResult(IssueSeverity.ERROR, info.getIssues().get(info.getIssues().size()-1).getDetails().getText(), info.getIssues());
|
||||
} else if (info.hasErrors()) {
|
||||
ValidationResult res = new ValidationResult(IssueSeverity.ERROR, info.summary(), info.getIssues());
|
||||
if (foundCoding != null) {
|
||||
ConceptDefinitionComponent cd = new ConceptDefinitionComponent(foundCoding.getCode());
|
||||
|
@ -279,6 +293,13 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean checkRequiredSupplements(ValidationProcessInfo info) {
|
||||
if (!requiredSupplements.isEmpty()) {
|
||||
info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, null, context.formatMessagePlural(requiredSupplements.size(), I18nConstants.VALUESET_SUPPLEMENT_MISSING, CommaSeparatedStringBuilder.build(requiredSupplements))));
|
||||
}
|
||||
return requiredSupplements.isEmpty();
|
||||
}
|
||||
|
||||
private boolean valueSetDependsOn(String system, String version) {
|
||||
for (ConceptSetComponent inc : valueset.getCompose().getInclude()) {
|
||||
if (system.equals(inc.getSystem()) && (version == null || inc.getVersion() == null || version.equals(inc.getVersion()))) {
|
||||
|
@ -315,7 +336,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
return t;
|
||||
}
|
||||
}
|
||||
CodeSystem cs = context.fetchCodeSystem(system, version);
|
||||
CodeSystem cs = context.fetchSupplementedCodeSystem(system, version);
|
||||
if (cs == null) {
|
||||
cs = findSpecialCodeSystem(system, version);
|
||||
}
|
||||
|
@ -342,6 +363,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
public ValidationResult validateCode(String path, Coding code) throws FHIRException {
|
||||
opContext.deadCheck();
|
||||
String warningMessage = null;
|
||||
// first, we validate the concept itself
|
||||
|
||||
|
@ -460,6 +482,9 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
inInclude = checkInclude(code, vi);
|
||||
}
|
||||
String wv = vi.getVersion(system, code.getVersion());
|
||||
if (!checkRequiredSupplements(info)) {
|
||||
return new ValidationResult(IssueSeverity.ERROR, issues.get(issues.size()-1).getDetails().getText(), issues);
|
||||
}
|
||||
|
||||
|
||||
// then, if we have a value set, we check it's in the value set
|
||||
|
@ -597,6 +622,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
|
||||
private ValidationResult findCodeInExpansion(Coding code, List<ValueSetExpansionContainsComponent> contains) {
|
||||
for (ValueSetExpansionContainsComponent containsComponent: contains) {
|
||||
opContext.deadCheck();
|
||||
if (containsComponent.getSystem().equals(code.getSystem()) && containsComponent.getCode().equals(code.getCode())) {
|
||||
ConceptDefinitionComponent ccd = new ConceptDefinitionComponent();
|
||||
ccd.setCode(containsComponent.getCode());
|
||||
|
@ -623,6 +649,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
|
||||
private boolean checkExpansion(Coding code, List<ValueSetExpansionContainsComponent> contains, VersionInfo vi) {
|
||||
for (ValueSetExpansionContainsComponent containsComponent: contains) {
|
||||
opContext.deadCheck();
|
||||
if (containsComponent.hasSystem() && containsComponent.hasCode() && containsComponent.getSystem().equals(code.getSystem()) && containsComponent.getCode().equals(code.getCode())) {
|
||||
vi.setExpansionVersion(containsComponent.getVersion());
|
||||
return true;
|
||||
|
@ -667,6 +694,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
for (ConceptDefinitionDesignationComponent ds : cc.getDesignation()) {
|
||||
opContext.deadCheck();
|
||||
if (isOkLanguage(ds.getLanguage())) {
|
||||
b.append("'"+ds.getValue()+"'");
|
||||
if (code.getDisplay().equalsIgnoreCase(ds.getValue())) {
|
||||
|
@ -688,6 +716,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
}
|
||||
for (ConceptReferenceDesignationComponent ds : vs.getCc().getDesignation()) {
|
||||
opContext.deadCheck();
|
||||
if (isOkLanguage(ds.getLanguage())) {
|
||||
b.append("'"+ds.getValue()+"'");
|
||||
if (code.getDisplay().equalsIgnoreCase(ds.getValue())) {
|
||||
|
@ -732,6 +761,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
return null;
|
||||
// if it has an expansion
|
||||
for (ValueSetExpansionContainsComponent exp : valueset.getExpansion().getContains()) {
|
||||
opContext.deadCheck();
|
||||
if (system.equals(exp.getSystem()) && code.equals(exp.getCode())) {
|
||||
ConceptReferenceComponent cc = new ConceptReferenceComponent();
|
||||
cc.setDisplay(exp.getDisplay());
|
||||
|
@ -810,6 +840,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private ConceptDefinitionComponent findCodeInConcept(ConceptDefinitionComponent concept, String code, AlternateCodesProcessingRules altCodeRules) {
|
||||
opContext.deadCheck();
|
||||
if (code.equals(concept.getCode())) {
|
||||
return concept;
|
||||
}
|
||||
|
@ -879,6 +910,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
|
||||
int i = 0;
|
||||
for (ConceptSetComponent vsi : valueset.getCompose().getInclude()) {
|
||||
opContext.deadCheck();
|
||||
if (vsi.hasValueSet()) {
|
||||
for (CanonicalType u : vsi.getValueSet()) {
|
||||
if (!checkForCodeInValueSet(code, u.getValue(), sys, problems)) {
|
||||
|
@ -963,6 +995,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
*/
|
||||
private boolean checkSystems(List<ValueSetExpansionContainsComponent> contains, String code, Set<String> systems, List<String> problems) {
|
||||
for (ValueSetExpansionContainsComponent c: contains) {
|
||||
opContext.deadCheck();
|
||||
if (c.getCode().equals(code)) {
|
||||
systems.add(c.getSystem());
|
||||
}
|
||||
|
@ -976,6 +1009,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
if (valueset == null) {
|
||||
return false;
|
||||
}
|
||||
opContext.deadCheck();
|
||||
checkCanonical(info.getIssues(), path, valueset, valueset);
|
||||
Boolean result = false;
|
||||
VersionInfo vi = new VersionInfo(this);
|
||||
|
@ -1010,6 +1044,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
private Boolean inComponent(String path, ConceptSetComponent vsi, int vsiIndex, String system, String version, String code, boolean only, ValidationProcessInfo info) throws FHIRException {
|
||||
opContext.deadCheck();
|
||||
boolean ok = true;
|
||||
|
||||
if (vsi.hasValueSet()) {
|
||||
|
@ -1189,6 +1224,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
}
|
||||
|
||||
public boolean validateCodeInConceptList(String code, CodeSystem def, List<ConceptDefinitionComponent> list, AlternateCodesProcessingRules altCodeRules) {
|
||||
opContext.deadCheck();
|
||||
if (def.getCaseSensitive()) {
|
||||
for (ConceptDefinitionComponent cc : list) {
|
||||
if (cc.getCode().equals(code)) {
|
||||
|
@ -1219,7 +1255,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
|
|||
return inner.get(url);
|
||||
}
|
||||
ValueSet vs = context.fetchResource(ValueSet.class, url, valueset);
|
||||
ValueSetValidator vsc = new ValueSetValidator(options, vs, context, localContext, expansionProfile, txCaps);
|
||||
ValueSetValidator vsc = new ValueSetValidator(context, opContext.copy(), options, vs, localContext, expansionProfile, txCaps);
|
||||
vsc.setThrowToServer(throwToServer);
|
||||
inner.put(url, vsc);
|
||||
return vsc;
|
||||
|
|
|
@ -117,4 +117,12 @@ public class CommaSeparatedStringBuilder {
|
|||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public static String build(List<String> list) {
|
||||
CommaSeparatedStringBuilder self = new CommaSeparatedStringBuilder();
|
||||
for (String s : list) {
|
||||
self.append(s);
|
||||
}
|
||||
return self.toString();
|
||||
}
|
||||
}
|
|
@ -866,6 +866,7 @@ public class I18nConstants {
|
|||
public static final String UNKNOWN_CODESYSTEM = "UNKNOWN_CODESYSTEM";
|
||||
public static final String UNKNOWN_CODESYSTEM_VERSION = "UNKNOWN_CODESYSTEM_VERSION";
|
||||
public static final String VALUESET_TOO_COSTLY = "VALUESET_TOO_COSTLY";
|
||||
public static final String VALUESET_TOO_COSTLY_TIME = "VALUESET_TOO_COSTLY_TIME";
|
||||
public static final String NO_VALID_DISPLAY_FOUND = "NO_VALID_DISPLAY_FOUND";
|
||||
public static final String SD_NO_CONTEXT_WHEN_NOT_EXTENSION = "SD_NO_CONTEXT_WHEN_NOT_EXTENSION";
|
||||
public static final String SD_CONTEXT_SHOULD_NOT_BE_ELEMENT = "SD_CONTEXT_SHOULD_NOT_BE_ELEMENT";
|
||||
|
@ -977,6 +978,8 @@ public class I18nConstants {
|
|||
public static final String CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO = "CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO";
|
||||
public static final String CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG = "CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG";
|
||||
public static final String CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED = "CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED";
|
||||
public static final String VALUESET_CIRCULAR_REFERENCE = "VALUESET_CIRCULAR_REFERENCE";
|
||||
public static final String VALUESET_SUPPLEMENT_MISSING = "VALUESET_SUPPLEMENT_MISSING";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -925,8 +925,9 @@ UNKNOWN_CODESYSTEM = The CodeSystem {0} is unknown
|
|||
UNKNOWN_CODESYSTEM_VERSION = The CodeSystem {0} version {1} is unknown. Valid versions: {2}
|
||||
UNABLE_TO_INFER_CODESYSTEM = The System URI could not be determined for the code {0} in the ValueSet {1}
|
||||
VALUESET_TOO_COSTLY = The value set {0} has too many codes to display ({1})
|
||||
NO_VALID_DISPLAY_FOUND_one = No valid Display Names found for {0}#{1} in the language {3}
|
||||
NO_VALID_DISPLAY_FOUND_other = No valid Display Names found for {0}#{1} in the languages {3}
|
||||
VALUESET_TOO_COSTLY_TIME = The value set {0} took too long to process (>{1}sec)
|
||||
NO_VALID_DISPLAY_FOUND_one = No valid Display Names found for {1}#{2} in the language {4}
|
||||
NO_VALID_DISPLAY_FOUND_other = No valid Display Names found for {1}#{2} in the languages {4}
|
||||
SD_NO_CONTEXT_WHEN_NOT_EXTENSION = The type is {0} so an extension context should not be specified
|
||||
SD_NO_CONTEXT_INV_WHEN_NOT_EXTENSION = The type is {0} so an extension context invariants should not be specified
|
||||
SD_CONTEXT_SHOULD_NOT_BE_ELEMENT = Review the extension type: extensions should not have a context of {0} unless it''s really intended that they can be used anywhere
|
||||
|
@ -1036,5 +1037,7 @@ CODESYSTEM_CS_COUNT_FRAGMENT_WRONG = The code system is a fragment/example, but
|
|||
CODESYSTEM_CS_COUNT_NOTPRESENT_ZERO = The code system has no content, but the exceeds the stated total number is 0 concepts - check that this isn't a complete code system that has no concepts, or update/remove the stated count
|
||||
CODESYSTEM_CS_COUNT_SUPPLEMENT_WRONG = The code system supplement states the total number of concepts as {1}, but this is different to the underlying code system that states a value of {0}
|
||||
CODESYSTEM_CS_COUNT_NO_CONTENT_ALLOWED = The code system says it has no content present, but concepts are found
|
||||
|
||||
VALUESET_CIRCULAR_REFERENCE = Found a circularity pointing to {0} processing ValueSet with pathway {1}
|
||||
VALUESET_SUPPLEMENT_MISSING_one = Required supplement not found: {1}
|
||||
VALUESET_SUPPLEMENT_MISSING_other = Required supplements not found: {1}
|
||||
|
|
@ -838,3 +838,6 @@ Display_Name_WS_for__should_be_one_of__instead_of_other =
|
|||
SD_ED_TYPE_PROFILE_WRONG_TYPE_one =
|
||||
SD_ED_TYPE_PROFILE_WRONG_TYPE_many =
|
||||
SD_ED_TYPE_PROFILE_WRONG_TYPE_other =
|
||||
VALUESET_SUPPLEMENT_MISSING_one =
|
||||
VALUESET_SUPPLEMENT_MISSING_many =
|
||||
VALUESET_SUPPLEMENT_MISSING_other =
|
||||
|
|
|
@ -46,8 +46,8 @@ public class ExternalTerminologyServiceTests implements ITxTesterLoader {
|
|||
private JsonObject test;
|
||||
}
|
||||
|
||||
// private static final String SERVER = FhirSettings.getTxFhirDevelopment();
|
||||
private static final String SERVER = FhirSettings.getTxFhirLocal();
|
||||
private static final String SERVER = FhirSettings.getTxFhirDevelopment();
|
||||
// private static final String SERVER = FhirSettings.getTxFhirLocal();
|
||||
// private static final String SERVER = "https://r4.ontoserver.csiro.au/fhir";
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue