terminology service bug fixes

This commit is contained in:
Grahame Grieve 2023-08-19 09:03:59 +10:00
parent 5078a95016
commit 6f0d54a8dc
11 changed files with 206 additions and 29 deletions

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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";
}

View File

@ -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}

View File

@ -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 =

View File

@ -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";