commit
490cecaced
|
@ -0,0 +1,4 @@
|
||||||
|
* Batch validation of codes in value sets
|
||||||
|
* Fix path problem doing comparisons
|
||||||
|
* Don't produce 2 columns for status for deprecated codes
|
||||||
|
* Fix errors in StructureDefinition validation
|
|
@ -35,8 +35,10 @@ import java.net.URISyntaxException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hl7.fhir.convertors.VersionConvertor_10_50;
|
import org.hl7.fhir.convertors.VersionConvertor_10_50;
|
||||||
|
import org.hl7.fhir.convertors.VersionConvertor_40_50;
|
||||||
import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient;
|
import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
import org.hl7.fhir.r5.model.Parameters;
|
import org.hl7.fhir.r5.model.Parameters;
|
||||||
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
||||||
|
@ -117,5 +119,10 @@ public class TerminologyClientR2 implements TerminologyClient {
|
||||||
return client.getRetryCount();
|
return client.getRetryCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle validateBatch(Bundle batch) {
|
||||||
|
return (Bundle) VersionConvertor_10_50.convertResource(client.transaction((org.hl7.fhir.dstu2.model.Bundle) VersionConvertor_10_50.convertResource(batch)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -35,8 +35,10 @@ import java.net.URISyntaxException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hl7.fhir.convertors.VersionConvertor_30_50;
|
import org.hl7.fhir.convertors.VersionConvertor_30_50;
|
||||||
|
import org.hl7.fhir.convertors.VersionConvertor_40_50;
|
||||||
import org.hl7.fhir.dstu3.utils.client.FHIRToolingClient;
|
import org.hl7.fhir.dstu3.utils.client.FHIRToolingClient;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
import org.hl7.fhir.r5.model.Parameters;
|
import org.hl7.fhir.r5.model.Parameters;
|
||||||
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
||||||
|
@ -117,4 +119,10 @@ public class TerminologyClientR3 implements TerminologyClient {
|
||||||
return client.getRetryCount();
|
return client.getRetryCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle validateBatch(Bundle batch) {
|
||||||
|
return (Bundle) VersionConvertor_30_50.convertResource(client.transaction((org.hl7.fhir.dstu3.model.Bundle) VersionConvertor_30_50.convertResource(batch, false)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -38,6 +38,7 @@ import org.hl7.fhir.convertors.VersionConvertor_40_50;
|
||||||
import org.hl7.fhir.convertors.conv40_50.TerminologyCapabilities40_50;
|
import org.hl7.fhir.convertors.conv40_50.TerminologyCapabilities40_50;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.r4.utils.client.FHIRToolingClient;
|
import org.hl7.fhir.r4.utils.client.FHIRToolingClient;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
import org.hl7.fhir.r5.model.Parameters;
|
import org.hl7.fhir.r5.model.Parameters;
|
||||||
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
||||||
|
@ -118,4 +119,9 @@ public class TerminologyClientR4 implements TerminologyClient {
|
||||||
return client.getRetryCount();
|
return client.getRetryCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle validateBatch(Bundle batch) {
|
||||||
|
return (Bundle) VersionConvertor_40_50.convertResource(client.transaction((org.hl7.fhir.r4.model.Bundle) VersionConvertor_40_50.convertResource(batch)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -35,6 +35,7 @@ import java.net.URISyntaxException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
import org.hl7.fhir.r5.model.CodeSystem;
|
import org.hl7.fhir.r5.model.CodeSystem;
|
||||||
import org.hl7.fhir.r5.model.Parameters;
|
import org.hl7.fhir.r5.model.Parameters;
|
||||||
|
@ -110,4 +111,9 @@ public class TerminologyClientR5 implements TerminologyClient {
|
||||||
return client.getRetryCount();
|
return client.getRetryCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle validateBatch(Bundle batch) {
|
||||||
|
return client.transaction(batch);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -146,8 +146,8 @@ public class ComparisonRenderer implements IEvaluationContext {
|
||||||
vars.put("concepts", new StringType(new XhtmlComposer(true).compose(cs.renderConcepts(comp, "", ""))));
|
vars.put("concepts", new StringType(new XhtmlComposer(true).compose(cs.renderConcepts(comp, "", ""))));
|
||||||
String cnt = processTemplate(template, "CodeSystem", vars);
|
String cnt = processTemplate(template, "CodeSystem", vars);
|
||||||
TextFile.stringToFile(cnt, file(comp.getId()+".html"));
|
TextFile.stringToFile(cnt, file(comp.getId()+".html"));
|
||||||
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-union.json")), comp.getUnion());
|
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion());
|
||||||
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-intersection.json")), comp.getIntersection());
|
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String file(String name) throws IOException {
|
private String file(String name) throws IOException {
|
||||||
|
@ -170,8 +170,8 @@ public class ComparisonRenderer implements IEvaluationContext {
|
||||||
vars.put("expansion", new StringType(new XhtmlComposer(true).compose(cs.renderExpansion(comp, "", ""))));
|
vars.put("expansion", new StringType(new XhtmlComposer(true).compose(cs.renderExpansion(comp, "", ""))));
|
||||||
String cnt = processTemplate(template, "ValueSet", vars);
|
String cnt = processTemplate(template, "ValueSet", vars);
|
||||||
TextFile.stringToFile(cnt, file(comp.getId()+".html"));
|
TextFile.stringToFile(cnt, file(comp.getId()+".html"));
|
||||||
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-union.json")), comp.getUnion());
|
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion());
|
||||||
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-intersection.json")), comp.getIntersection());
|
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderProfile(String id, ProfileComparison comp) throws IOException {
|
private void renderProfile(String id, ProfileComparison comp) throws IOException {
|
||||||
|
@ -189,8 +189,8 @@ public class ComparisonRenderer implements IEvaluationContext {
|
||||||
vars.put("structure", new StringType(new XhtmlComposer(true).compose(cs.renderStructure(comp, "", "", "http://hl7.org/fhir"))));
|
vars.put("structure", new StringType(new XhtmlComposer(true).compose(cs.renderStructure(comp, "", "", "http://hl7.org/fhir"))));
|
||||||
String cnt = processTemplate(template, "CodeSystem", vars);
|
String cnt = processTemplate(template, "CodeSystem", vars);
|
||||||
TextFile.stringToFile(cnt, file(comp.getId()+".html"));
|
TextFile.stringToFile(cnt, file(comp.getId()+".html"));
|
||||||
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-union.json")), comp.getUnion());
|
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion());
|
||||||
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-intersection.json")), comp.getIntersection());
|
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String processTemplate(String template, String name, Map<String, Base> vars) {
|
private String processTemplate(String template, String name, Map<String, Base> vars) {
|
||||||
|
|
|
@ -2596,7 +2596,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
if (derived.hasIsSummaryElement()) {
|
if (derived.hasIsSummaryElement()) {
|
||||||
if (!Base.compareDeep(derived.getIsSummaryElement(), base.getIsSummaryElement(), false)) {
|
if (!Base.compareDeep(derived.getIsSummaryElement(), base.getIsSummaryElement(), false)) {
|
||||||
if (base.hasIsSummary() && !context.getVersion().equals("1.4.0")) // work around a known issue with some 1.4.0 cosntraints
|
if (base.hasIsSummary() && !context.getVersion().equals("1.4.0")) // work around a known issue with some 1.4.0 cosntraints
|
||||||
throw new Error(context.formatMessage(I18nConstants.ERROR_IN_PROFILE__AT__BASE_ISSUMMARY___DERIVED_ISSUMMARY__, pn, derived.getPath(), base.getIsSummaryElement().asStringValue(), derived.getIsSummaryElement().asStringValue()));
|
throw new Error(context.formatMessage(I18nConstants.ERROR_IN_PROFILE__AT__BASE_ISSUMMARY___DERIVED_ISSUMMARY__, purl, derived.getPath(), base.getIsSummaryElement().asStringValue(), derived.getIsSummaryElement().asStringValue()));
|
||||||
base.setIsSummaryElement(derived.getIsSummaryElement().copy());
|
base.setIsSummaryElement(derived.getIsSummaryElement().copy());
|
||||||
} else if (trimDifferential)
|
} else if (trimDifferential)
|
||||||
derived.setIsSummaryElement(null);
|
derived.setIsSummaryElement(null);
|
||||||
|
|
|
@ -55,6 +55,7 @@ import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory;
|
import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory;
|
||||||
import org.hl7.fhir.r5.context.TerminologyCache.CacheToken;
|
import org.hl7.fhir.r5.context.TerminologyCache.CacheToken;
|
||||||
import org.hl7.fhir.r5.model.BooleanType;
|
import org.hl7.fhir.r5.model.BooleanType;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
import org.hl7.fhir.r5.model.CodeSystem;
|
import org.hl7.fhir.r5.model.CodeSystem;
|
||||||
|
@ -74,6 +75,7 @@ import org.hl7.fhir.r5.model.NamingSystem;
|
||||||
import org.hl7.fhir.r5.model.NamingSystem.NamingSystemIdentifierType;
|
import org.hl7.fhir.r5.model.NamingSystem.NamingSystemIdentifierType;
|
||||||
import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent;
|
import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent;
|
||||||
import org.hl7.fhir.r5.model.OperationDefinition;
|
import org.hl7.fhir.r5.model.OperationDefinition;
|
||||||
|
import org.hl7.fhir.r5.model.OperationOutcome;
|
||||||
import org.hl7.fhir.r5.model.Parameters;
|
import org.hl7.fhir.r5.model.Parameters;
|
||||||
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
|
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
|
||||||
import org.hl7.fhir.r5.model.PlanDefinition;
|
import org.hl7.fhir.r5.model.PlanDefinition;
|
||||||
|
@ -88,8 +90,12 @@ import org.hl7.fhir.r5.model.StructureMap;
|
||||||
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
||||||
import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent;
|
import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent;
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle.BundleType;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle.HTTPVerb;
|
||||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
||||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
|
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
|
||||||
|
import org.hl7.fhir.r5.renderers.OperationOutcomeRenderer;
|
||||||
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
||||||
import org.hl7.fhir.r5.terminologies.TerminologyClient;
|
import org.hl7.fhir.r5.terminologies.TerminologyClient;
|
||||||
import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple;
|
import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple;
|
||||||
|
@ -111,6 +117,8 @@ import org.hl7.fhir.utilities.validation.ValidationOptions.ValueSetMode;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||||
|
|
||||||
public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext{
|
public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext{
|
||||||
|
|
||||||
public class MetadataResourceVersionComparator<T extends CanonicalResource> implements Comparator<T> {
|
public class MetadataResourceVersionComparator<T extends CanonicalResource> implements Comparator<T> {
|
||||||
|
@ -685,10 +693,103 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
return validateCode(options.guessSystem(), c, vs);
|
return validateCode(options.guessSystem(), c, vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs) {
|
||||||
|
if (options == null) {
|
||||||
|
options = ValidationOptions.defaults();
|
||||||
|
}
|
||||||
|
// 1st pass: what is in the cache?
|
||||||
|
// 2nd pass: What can we do internally
|
||||||
|
// 3rd pass: hit the server
|
||||||
|
for (CodingValidationRequest t : codes) {
|
||||||
|
t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vs) : null);
|
||||||
|
if (txCache != null) {
|
||||||
|
t.setResult(txCache.getValidation(t.getCacheToken()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (options.isUseClient()) {
|
||||||
|
for (CodingValidationRequest t : codes) {
|
||||||
|
if (!t.hasResult()) {
|
||||||
|
try {
|
||||||
|
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
|
||||||
|
ValidationResult res = vsc.validateCode(t.getCoding());
|
||||||
|
if (txCache != null)
|
||||||
|
txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT);
|
||||||
|
t.setResult(res);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CodingValidationRequest t : codes) {
|
||||||
|
if (!t.hasResult()) {
|
||||||
|
if (!options.isUseServer()) {
|
||||||
|
t.setResult(new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS));
|
||||||
|
} else if (unsupportedCodeSystems.contains(t.getCoding().getSystem())) {
|
||||||
|
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED));
|
||||||
|
} else if (noTerminologyServer) {
|
||||||
|
t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expParameters == null)
|
||||||
|
throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED));
|
||||||
|
// for those that that failed, we try to validate on the server
|
||||||
|
Bundle batch = new Bundle();
|
||||||
|
batch.setType(BundleType.BATCH);
|
||||||
|
Set<String> systems = new HashSet<>();
|
||||||
|
for (CodingValidationRequest t : codes) {
|
||||||
|
if (!t.hasResult()) {
|
||||||
|
Parameters pIn = new Parameters();
|
||||||
|
pIn.addParameter().setName("coding").setValue(t.getCoding());
|
||||||
|
if (options.isGuessSystem())
|
||||||
|
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
|
||||||
|
if (vs != null) {
|
||||||
|
pIn.addParameter().setName("valueSet").setResource(vs);
|
||||||
|
}
|
||||||
|
pIn.addParameter().setName("profile").setResource(expParameters);
|
||||||
|
setTerminologyOptions(options, pIn);
|
||||||
|
BundleEntryComponent be = batch.addEntry();
|
||||||
|
be.setResource(pIn);
|
||||||
|
be.getRequest().setMethod(HTTPVerb.POST);
|
||||||
|
be.getRequest().setUrl("ValueSet/$validate-code");
|
||||||
|
be.setUserData("source", t);
|
||||||
|
systems.add(t.getCoding().getSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (batch.getEntry().size() > 0) {
|
||||||
|
tlog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString());
|
||||||
|
if (txClient == null) {
|
||||||
|
throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
|
||||||
|
}
|
||||||
|
if (txLog != null) {
|
||||||
|
txLog.clearLastId();
|
||||||
|
}
|
||||||
|
Bundle resp = txClient.validateBatch(batch);
|
||||||
|
for (int i = 0; i < batch.getEntry().size(); i++) {
|
||||||
|
CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source");
|
||||||
|
BundleEntryComponent r = resp.getEntry().get(i);
|
||||||
|
if (r.getResource() instanceof Parameters) {
|
||||||
|
t.setResult(processValidationResult((Parameters) r.getResource()));
|
||||||
|
} else {
|
||||||
|
t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource())).setTxLink(txLog == null ? null : txLog.getLastId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getResponseText(Resource resource) {
|
||||||
|
if (resource instanceof OperationOutcome) {
|
||||||
|
return OperationOutcomeRenderer.toString((OperationOutcome) resource);
|
||||||
|
}
|
||||||
|
return "Todo";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) {
|
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) {
|
||||||
assert options != null;
|
|
||||||
|
|
||||||
if (options == null) {
|
if (options == null) {
|
||||||
options = ValidationOptions.defaults();
|
options = ValidationOptions.defaults();
|
||||||
}
|
}
|
||||||
|
@ -813,6 +914,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
pOut = txClient.validateCS(pin);
|
pOut = txClient.validateCS(pin);
|
||||||
else
|
else
|
||||||
pOut = txClient.validateVS(pin);
|
pOut = txClient.validateVS(pin);
|
||||||
|
return processValidationResult(pOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationResult processValidationResult(Parameters pOut) {
|
||||||
boolean ok = false;
|
boolean ok = false;
|
||||||
String message = "No Message returned";
|
String message = "No Message returned";
|
||||||
String display = null;
|
String display = null;
|
||||||
|
@ -1622,4 +1727,5 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
return txClient;
|
return txClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -44,6 +44,8 @@ import org.fhir.ucum.UcumService;
|
||||||
import org.hl7.fhir.exceptions.DefinitionException;
|
import org.hl7.fhir.exceptions.DefinitionException;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext.CodingValidationRequest;
|
||||||
|
import org.hl7.fhir.r5.context.TerminologyCache.CacheToken;
|
||||||
import org.hl7.fhir.r5.formats.IParser;
|
import org.hl7.fhir.r5.formats.IParser;
|
||||||
import org.hl7.fhir.r5.formats.ParserType;
|
import org.hl7.fhir.r5.formats.ParserType;
|
||||||
import org.hl7.fhir.r5.model.Bundle;
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
|
@ -93,6 +95,53 @@ import com.google.gson.JsonSyntaxException;
|
||||||
*/
|
*/
|
||||||
public interface IWorkerContext {
|
public interface IWorkerContext {
|
||||||
|
|
||||||
|
public class CodingValidationRequest {
|
||||||
|
private Coding coding;
|
||||||
|
private ValidationResult result;
|
||||||
|
private CacheToken cacheToken;
|
||||||
|
|
||||||
|
public CodingValidationRequest(Coding coding) {
|
||||||
|
super();
|
||||||
|
this.coding = coding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationResult getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResult(ValidationResult result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coding getCoding() {
|
||||||
|
return coding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasResult() {
|
||||||
|
return result != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* internal logic; external users of batch validation should ignore this property
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public CacheToken getCacheToken() {
|
||||||
|
return cacheToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* internal logic; external users of batch validation should ignore this property
|
||||||
|
*
|
||||||
|
* @param cacheToken
|
||||||
|
*/
|
||||||
|
public void setCacheToken(CacheToken cacheToken) {
|
||||||
|
this.cacheToken = cacheToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public class PackageVersion {
|
public class PackageVersion {
|
||||||
private String id;
|
private String id;
|
||||||
private String version;
|
private String version;
|
||||||
|
@ -620,6 +669,8 @@ public interface IWorkerContext {
|
||||||
*/
|
*/
|
||||||
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs);
|
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs);
|
||||||
|
|
||||||
|
public void validateCodeBatch(ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the recommended tla for the type (from the structure definitions)
|
* returns the recommended tla for the type (from the structure definitions)
|
||||||
*
|
*
|
||||||
|
|
|
@ -138,6 +138,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
|
||||||
boolean display = false;
|
boolean display = false;
|
||||||
boolean hierarchy = false;
|
boolean hierarchy = false;
|
||||||
boolean version = false;
|
boolean version = false;
|
||||||
|
boolean ignoreStatus = false;
|
||||||
List<PropertyComponent> properties = new ArrayList<>();
|
List<PropertyComponent> properties = new ArrayList<>();
|
||||||
for (PropertyComponent cp : cs.getProperty()) {
|
for (PropertyComponent cp : cs.getProperty()) {
|
||||||
if (showPropertyInTable(cp)) {
|
if (showPropertyInTable(cp)) {
|
||||||
|
@ -147,12 +148,15 @@ public class CodeSystemRenderer extends TerminologyRenderer {
|
||||||
}
|
}
|
||||||
if (exists) {
|
if (exists) {
|
||||||
properties.add(cp);
|
properties.add(cp);
|
||||||
|
if ("status".equals(cp.getCode())) {
|
||||||
|
ignoreStatus = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (ConceptDefinitionComponent c : cs.getConcept()) {
|
for (ConceptDefinitionComponent c : cs.getConcept()) {
|
||||||
commentS = commentS || conceptsHaveComments(c);
|
commentS = commentS || conceptsHaveComments(c);
|
||||||
deprecated = deprecated || conceptsHaveDeprecated(cs, c);
|
deprecated = deprecated || conceptsHaveDeprecated(cs, c, ignoreStatus);
|
||||||
display = display || conceptsHaveDisplay(c);
|
display = display || conceptsHaveDisplay(c);
|
||||||
version = version || conceptsHaveVersion(c);
|
version = version || conceptsHaveVersion(c);
|
||||||
hierarchy = hierarchy || c.hasConcept();
|
hierarchy = hierarchy || c.hasConcept();
|
||||||
|
@ -255,11 +259,11 @@ public class CodeSystemRenderer extends TerminologyRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean conceptsHaveDeprecated(CodeSystem cs, ConceptDefinitionComponent c) {
|
private boolean conceptsHaveDeprecated(CodeSystem cs, ConceptDefinitionComponent c, boolean ignoreStatus) {
|
||||||
if (CodeSystemUtilities.isDeprecated(cs, c))
|
if (CodeSystemUtilities.isDeprecated(cs, c, ignoreStatus))
|
||||||
return true;
|
return true;
|
||||||
for (ConceptDefinitionComponent g : c.getConcept())
|
for (ConceptDefinitionComponent g : c.getConcept())
|
||||||
if (conceptsHaveDeprecated(cs, g))
|
if (conceptsHaveDeprecated(cs, g, ignoreStatus))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -325,7 +329,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
|
||||||
}
|
}
|
||||||
if (deprecated) {
|
if (deprecated) {
|
||||||
td = tr.td();
|
td = tr.td();
|
||||||
Boolean b = CodeSystemUtilities.isDeprecated(cs, c);
|
Boolean b = CodeSystemUtilities.isDeprecated(cs, c, false);
|
||||||
if (b != null && b) {
|
if (b != null && b) {
|
||||||
smartAddText(td, getContext().getWorker().translator().translate("xhtml-gen-cs", "Deprecated", getContext().getLang()));
|
smartAddText(td, getContext().getWorker().translator().translate("xhtml-gen-cs", "Deprecated", getContext().getLang()));
|
||||||
hasExtensions = true;
|
hasExtensions = true;
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.hl7.fhir.r5.model.StringType;
|
||||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
||||||
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
|
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
|
||||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||||
|
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
|
|
||||||
public class OperationOutcomeRenderer extends ResourceRenderer {
|
public class OperationOutcomeRenderer extends ResourceRenderer {
|
||||||
|
@ -89,4 +90,11 @@ public class OperationOutcomeRenderer extends ResourceRenderer {
|
||||||
return display((OperationOutcome) r);
|
return display((OperationOutcome) r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String toString(OperationOutcome oo) {
|
||||||
|
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||||
|
for (OperationOutcomeIssueComponent issue : oo.getIssue()) {
|
||||||
|
b.append(issue.getSeverity().toCode()+": "+issue.getDetails().getText());
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,7 +270,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, boolean title, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
|
private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode parent, XhtmlNode x, boolean title, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
|
||||||
if (ew == null)
|
if (ew == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -361,7 +361,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
||||||
} else {
|
} else {
|
||||||
x.an(rw.getId());
|
x.an(rw.getId());
|
||||||
ResourceRenderer rr = RendererFactory.factory(rw, context.copy().setAddGeneratedNarrativeHeader(false));
|
ResourceRenderer rr = RendererFactory.factory(rw, context.copy().setAddGeneratedNarrativeHeader(false));
|
||||||
rr.render(x.blockquote(), rw);
|
rr.render(parent.blockquote(), rw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -598,7 +598,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
||||||
|
|
||||||
private void generateByProfile(ResourceWrapper res, StructureDefinition profile, BaseWrapper e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
|
private void generateByProfile(ResourceWrapper res, StructureDefinition profile, BaseWrapper e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
|
||||||
if (children.isEmpty()) {
|
if (children.isEmpty()) {
|
||||||
renderLeaf(res, e, defn, x, false, showCodeDetails, readDisplayHints(defn), path, indent);
|
renderLeaf(res, e, defn, x, x, false, showCodeDetails, readDisplayHints(defn), path, indent);
|
||||||
} else {
|
} else {
|
||||||
for (PropertyWrapper p : splitExtensions(profile, e.children())) {
|
for (PropertyWrapper p : splitExtensions(profile, e.children())) {
|
||||||
if (p.hasValues()) {
|
if (p.hasValues()) {
|
||||||
|
@ -626,7 +626,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
||||||
if (renderAsList(child) && p.getValues().size() > 1) {
|
if (renderAsList(child) && p.getValues().size() > 1) {
|
||||||
XhtmlNode list = x.ul();
|
XhtmlNode list = x.ul();
|
||||||
for (BaseWrapper v : p.getValues())
|
for (BaseWrapper v : p.getValues())
|
||||||
renderLeaf(res, v, child, list.li(), false, showCodeDetails, displayHints, path, indent);
|
renderLeaf(res, v, child, x, list.li(), false, showCodeDetails, displayHints, path, indent);
|
||||||
} else {
|
} else {
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
for (BaseWrapper v : p.getValues()) {
|
for (BaseWrapper v : p.getValues()) {
|
||||||
|
@ -634,7 +634,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
||||||
first = false;
|
first = false;
|
||||||
else
|
else
|
||||||
para.tx(", ");
|
para.tx(", ");
|
||||||
renderLeaf(res, v, child, para, false, showCodeDetails, displayHints, path, indent);
|
renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -730,10 +730,12 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
|
||||||
private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List<ElementDefinition> grandChildren, BaseWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
|
private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List<ElementDefinition> grandChildren, BaseWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome {
|
||||||
for (ElementDefinition e : grandChildren) {
|
for (ElementDefinition e : grandChildren) {
|
||||||
PropertyWrapper p = v.getChildByName(e.getPath().substring(e.getPath().lastIndexOf(".")+1));
|
PropertyWrapper p = v.getChildByName(e.getPath().substring(e.getPath().lastIndexOf(".")+1));
|
||||||
|
XhtmlNode td = tr.td();
|
||||||
if (p == null || p.getValues().size() == 0 || p.getValues().get(0) == null)
|
if (p == null || p.getValues().size() == 0 || p.getValues().get(0) == null)
|
||||||
tr.td().tx(" ");
|
td.tx(" ");
|
||||||
else
|
else {
|
||||||
renderLeaf(res, p.getValues().get(0), e, tr.td(), false, showCodeDetails, displayHints, path, indent);
|
renderLeaf(res, p.getValues().get(0), e, td, td, false, showCodeDetails, displayHints, path, indent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,11 +208,13 @@ public class CodeSystemUtilities {
|
||||||
defineCodeSystemProperty(cs, "child", "The concept identified in this property is a child of the concept on which it is a property. The property type will be 'code'. The meaning of parent/child relationships is defined by the hierarchyMeaning attribute", PropertyType.CODE);
|
defineCodeSystemProperty(cs, "child", "The concept identified in this property is a child of the concept on which it is a property. The property type will be 'code'. The meaning of parent/child relationships is defined by the hierarchyMeaning attribute", PropertyType.CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDeprecated(CodeSystem cs, ConceptDefinitionComponent def) {
|
public static boolean isDeprecated(CodeSystem cs, ConceptDefinitionComponent def, boolean ignoreStatus) {
|
||||||
try {
|
try {
|
||||||
for (ConceptPropertyComponent p : def.getProperty()) {
|
for (ConceptPropertyComponent p : def.getProperty()) {
|
||||||
|
if (!ignoreStatus) {
|
||||||
if (p.getCode().equals("status") && p.hasValue() && p.hasValueCodeType() && p.getValueCodeType().getCode().equals("deprecated"))
|
if (p.getCode().equals("status") && p.hasValue() && p.hasValueCodeType() && p.getValueCodeType().getCode().equals("deprecated"))
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
// this, though status should also be set
|
// this, though status should also be set
|
||||||
if (p.getCode().equals("deprecationDate") && p.hasValue() && p.getValue() instanceof DateTimeType)
|
if (p.getCode().equals("deprecationDate") && p.hasValue() && p.getValue() instanceof DateTimeType)
|
||||||
return ((DateTimeType) p.getValue()).before(new DateTimeType(Calendar.getInstance()));
|
return ((DateTimeType) p.getValue()).before(new DateTimeType(Calendar.getInstance()));
|
||||||
|
|
|
@ -34,6 +34,7 @@ package org.hl7.fhir.r5.terminologies;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
import org.hl7.fhir.r5.model.Parameters;
|
import org.hl7.fhir.r5.model.Parameters;
|
||||||
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
import org.hl7.fhir.r5.model.TerminologyCapabilities;
|
||||||
|
@ -52,4 +53,5 @@ public interface TerminologyClient {
|
||||||
public TerminologyClient setRetryCount(int retryCount) throws FHIRException;
|
public TerminologyClient setRetryCount(int retryCount) throws FHIRException;
|
||||||
public CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException;
|
public CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException;
|
||||||
public Parameters lookupCode(Map<String, String> params) throws FHIRException;
|
public Parameters lookupCode(Map<String, String> params) throws FHIRException;
|
||||||
|
public Bundle validateBatch(Bundle batch);
|
||||||
}
|
}
|
|
@ -402,19 +402,22 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
|
||||||
|
|
||||||
private boolean inComponent(ConceptSetComponent vsi, String system, String code, boolean only) throws FHIRException {
|
private boolean inComponent(ConceptSetComponent vsi, String system, String code, boolean only) throws FHIRException {
|
||||||
for (UriType uri : vsi.getValueSet()) {
|
for (UriType uri : vsi.getValueSet()) {
|
||||||
if (inImport(uri.getValue(), system, code))
|
if (inImport(uri.getValue(), system, code)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!vsi.hasSystem())
|
if (!vsi.hasSystem()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
if (only && system == null) {
|
if (only && system == null) {
|
||||||
// whether we know the system or not, we'll accept the stated codes at face value
|
// whether we know the system or not, we'll accept the stated codes at face value
|
||||||
for (ConceptReferenceComponent cc : vsi.getConcept())
|
for (ConceptReferenceComponent cc : vsi.getConcept()) {
|
||||||
if (cc.getCode().equals(code))
|
if (cc.getCode().equals(code)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!system.equals(vsi.getSystem()))
|
if (!system.equals(vsi.getSystem()))
|
||||||
return false;
|
return false;
|
||||||
|
@ -443,14 +446,17 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
|
||||||
List<ConceptDefinitionComponent> list = cs.getConcept();
|
List<ConceptDefinitionComponent> list = cs.getConcept();
|
||||||
boolean ok = validateCodeInConceptList(code, cs, list);
|
boolean ok = validateCodeInConceptList(code, cs, list);
|
||||||
if (ok && vsi.hasConcept()) {
|
if (ok && vsi.hasConcept()) {
|
||||||
for (ConceptReferenceComponent cc : vsi.getConcept())
|
for (ConceptReferenceComponent cc : vsi.getConcept()) {
|
||||||
if (cc.getCode().equals(code))
|
if (cc.getCode().equals(code)) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else {
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean codeInFilter(CodeSystem cs, String system, ConceptSetFilterComponent f, String code) throws FHIRException {
|
private boolean codeInFilter(CodeSystem cs, String system, ConceptSetFilterComponent f, String code) throws FHIRException {
|
||||||
if ("concept".equals(f.getProperty()))
|
if ("concept".equals(f.getProperty()))
|
||||||
|
|
|
@ -293,7 +293,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
|
||||||
if (exclusion.getCode().equals(def.getCode()))
|
if (exclusion.getCode().equals(def.getCode()))
|
||||||
return; // excluded.
|
return; // excluded.
|
||||||
}
|
}
|
||||||
if (!CodeSystemUtilities.isDeprecated(cs, def)) {
|
if (!CodeSystemUtilities.isDeprecated(cs, def, false)) {
|
||||||
ValueSetExpansionContainsComponent np = null;
|
ValueSetExpansionContainsComponent np = null;
|
||||||
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
|
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
|
||||||
boolean inc = CodeSystemUtilities.isInactive(cs, def);
|
boolean inc = CodeSystemUtilities.isInactive(cs, def);
|
||||||
|
|
|
@ -513,7 +513,6 @@ public class FHIRToolingClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
||||||
public <T extends Resource> OperationOutcome validate(Class<T> resourceClass, T resource, String id) {
|
public <T extends Resource> OperationOutcome validate(Class<T> resourceClass, T resource, String id) {
|
||||||
ResourceRequest<T> result = null;
|
ResourceRequest<T> result = null;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -333,6 +333,7 @@ public class I18nConstants {
|
||||||
public static final String RESOURCE_RES_ID_PROHIBITED = "Resource_RES_ID_Prohibited";
|
public static final String RESOURCE_RES_ID_PROHIBITED = "Resource_RES_ID_Prohibited";
|
||||||
public static final String RESOURCE_TYPE_MISMATCH_FOR___ = "Resource_type_mismatch_for___";
|
public static final String RESOURCE_TYPE_MISMATCH_FOR___ = "Resource_type_mismatch_for___";
|
||||||
public static final String SAME_ID_ON_MULTIPLE_ELEMENTS__IN_ = "Same_id_on_multiple_elements__in_";
|
public static final String SAME_ID_ON_MULTIPLE_ELEMENTS__IN_ = "Same_id_on_multiple_elements__in_";
|
||||||
|
public static final String SD_MUST_HAVE_DERIVATION = "SD_MUST_HAVE_DERIVATION";
|
||||||
public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG";
|
public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG";
|
||||||
public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG";
|
public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG";
|
||||||
public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND";
|
public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND";
|
||||||
|
|
|
@ -570,3 +570,4 @@ XHTML_XHTML_DOCTYPE_ILLEGAL = Malformed XHTML: Found a DocType declaration, and
|
||||||
PACKAGE_VERSION_MISMATCH = FHIR Version mismatch in package {0}: version is {2} but must be {1} (path: {3})
|
PACKAGE_VERSION_MISMATCH = FHIR Version mismatch in package {0}: version is {2} but must be {1} (path: {3})
|
||||||
VALUESET_REFERENCE_UNKNOWN = The value set import {0} could not be found so cannot be checked
|
VALUESET_REFERENCE_UNKNOWN = The value set import {0} could not be found so cannot be checked
|
||||||
VALUESET_REFERENCE_INVALID_TYPE = The value set import {0} points to a resource of type {1} which is not valid
|
VALUESET_REFERENCE_INVALID_TYPE = The value set import {0} points to a resource of type {1} which is not valid
|
||||||
|
SD_MUST_HAVE_DERIVATION = StructureDefinition {0} must have a derivation, since it has a baseDefinition
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.hl7.fhir.r5.model.ElementDefinition;
|
||||||
import org.hl7.fhir.r5.model.ExpressionNode;
|
import org.hl7.fhir.r5.model.ExpressionNode;
|
||||||
import org.hl7.fhir.r5.model.ExpressionNode.Kind;
|
import org.hl7.fhir.r5.model.ExpressionNode.Kind;
|
||||||
import org.hl7.fhir.r5.model.ExpressionNode.Operation;
|
import org.hl7.fhir.r5.model.ExpressionNode.Operation;
|
||||||
|
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
|
||||||
import org.hl7.fhir.r5.model.SearchParameter;
|
import org.hl7.fhir.r5.model.SearchParameter;
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
import org.hl7.fhir.r5.utils.FHIRPathEngine;
|
||||||
|
@ -66,6 +67,8 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
sd.setSnapshot(null);
|
sd.setSnapshot(null);
|
||||||
StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
|
StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
|
||||||
if (warning(errors, IssueType.NOTFOUND, stack.getLiteralPath(), base != null, I18nConstants.UNABLE_TO_FIND_BASE__FOR_, sd.getBaseDefinition(), "StructureDefinition, so can't check the differential")) {
|
if (warning(errors, IssueType.NOTFOUND, stack.getLiteralPath(), base != null, I18nConstants.UNABLE_TO_FIND_BASE__FOR_, sd.getBaseDefinition(), "StructureDefinition, so can't check the differential")) {
|
||||||
|
if (rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasDerivation(), I18nConstants.SD_MUST_HAVE_DERIVATION, sd.getUrl())) {
|
||||||
|
if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) {
|
||||||
List<ValidationMessage> msgs = new ArrayList<>();
|
List<ValidationMessage> msgs = new ArrayList<>();
|
||||||
ProfileUtilities pu = new ProfileUtilities(context, msgs, null);
|
ProfileUtilities pu = new ProfileUtilities(context, msgs, null);
|
||||||
pu.generateSnapshot(base, sd, sd.getUrl(), "http://hl7.org/fhir", sd.getName());
|
pu.generateSnapshot(base, sd, sd.getUrl(), "http://hl7.org/fhir", sd.getName());
|
||||||
|
@ -81,12 +84,14 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
errors.add(msg);
|
errors.add(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!snapshot.isEmpty()) {
|
if (!snapshot.isEmpty()) {
|
||||||
int was = snapshot.size();
|
int was = snapshot.size();
|
||||||
int is = sd.getSnapshot().getElement().size();
|
int is = sd.getSnapshot().getElement().size();
|
||||||
rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), was == is, I18nConstants.SNAPSHOT_EXISTING_PROBLEM, was, is);
|
rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), was == is, I18nConstants.SNAPSHOT_EXISTING_PROBLEM, was, is);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (FHIRException | IOException e) {
|
} catch (FHIRException | IOException e) {
|
||||||
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
|
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package org.hl7.fhir.validation.instance.type;
|
package org.hl7.fhir.validation.instance.type;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext.CodingValidationRequest;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
|
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
|
||||||
import org.hl7.fhir.r5.elementmodel.Element;
|
import org.hl7.fhir.r5.elementmodel.Element;
|
||||||
import org.hl7.fhir.r5.model.CodeSystem;
|
import org.hl7.fhir.r5.model.CodeSystem;
|
||||||
|
@ -19,10 +21,26 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||||
import org.hl7.fhir.validation.BaseValidator;
|
import org.hl7.fhir.validation.BaseValidator;
|
||||||
import org.hl7.fhir.validation.TimeTracker;
|
import org.hl7.fhir.validation.TimeTracker;
|
||||||
|
import org.hl7.fhir.validation.instance.type.ValueSetValidator.VSCodingValidationRequest;
|
||||||
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||||
|
|
||||||
public class ValueSetValidator extends BaseValidator {
|
public class ValueSetValidator extends BaseValidator {
|
||||||
|
|
||||||
|
public class VSCodingValidationRequest extends CodingValidationRequest {
|
||||||
|
|
||||||
|
private NodeStack stack;
|
||||||
|
|
||||||
|
public VSCodingValidationRequest(NodeStack stack, Coding code) {
|
||||||
|
super(code);
|
||||||
|
this.stack = stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeStack getStack() {
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public ValueSetValidator(IWorkerContext context, TimeTracker timeTracker) {
|
public ValueSetValidator(IWorkerContext context, TimeTracker timeTracker) {
|
||||||
super(context);
|
super(context);
|
||||||
source = Source.InstanceValidator;
|
source = Source.InstanceValidator;
|
||||||
|
@ -58,7 +76,6 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
private void validateValueSetInclude(List<ValidationMessage> errors, Element include, NodeStack stack) {
|
private void validateValueSetInclude(List<ValidationMessage> errors, Element include, NodeStack stack) {
|
||||||
String system = include.getChildValue("system");
|
String system = include.getChildValue("system");
|
||||||
String version = include.getChildValue("version");
|
String version = include.getChildValue("version");
|
||||||
boolean systemOk = true;
|
|
||||||
List<Element> valuesets = include.getChildrenByName("valueSet");
|
List<Element> valuesets = include.getChildrenByName("valueSet");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Element ve : valuesets) {
|
for (Element ve : valuesets) {
|
||||||
|
@ -79,13 +96,31 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
List<Element> concepts = include.getChildrenByName("concept");
|
List<Element> concepts = include.getChildrenByName("concept");
|
||||||
List<Element> filters = include.getChildrenByName("filter");
|
List<Element> filters = include.getChildrenByName("filter");
|
||||||
if (!Utilities.noString(system)) {
|
if (!Utilities.noString(system)) {
|
||||||
|
boolean systemOk = true;
|
||||||
int cc = 0;
|
int cc = 0;
|
||||||
|
List<VSCodingValidationRequest> batch = new ArrayList<>();
|
||||||
|
boolean first = true;
|
||||||
for (Element concept : concepts) {
|
for (Element concept : concepts) {
|
||||||
if (systemOk && !validateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version)) {
|
// we treat the first differently because we want to know if tbe system is worth validating. if it is, then we batch the rest
|
||||||
systemOk = false;
|
if (first) {
|
||||||
|
systemOk = validateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version);
|
||||||
|
first = false;
|
||||||
|
} else if (systemOk) {
|
||||||
|
batch.add(prepareValidateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version));
|
||||||
}
|
}
|
||||||
cc++;
|
cc++;
|
||||||
}
|
}
|
||||||
|
if (batch.size() > 0) {
|
||||||
|
context.validateCodeBatch(ValidationOptions.defaults(), batch, null);
|
||||||
|
for (VSCodingValidationRequest cv : batch) {
|
||||||
|
if (version == null) {
|
||||||
|
warning(errors, IssueType.BUSINESSRULE, cv.getStack().getLiteralPath(), cv.getResult().isOk(), I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE, system, cv.getCoding().getCode());
|
||||||
|
} else {
|
||||||
|
warning(errors, IssueType.BUSINESSRULE, cv.getStack().getLiteralPath(), cv.getResult().isOk(), I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER, system, version, cv.getCoding().getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int cf = 0;
|
int cf = 0;
|
||||||
for (Element filter : filters) {
|
for (Element filter : filters) {
|
||||||
if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version)) {
|
if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version)) {
|
||||||
|
@ -121,6 +156,15 @@ public class ValueSetValidator extends BaseValidator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VSCodingValidationRequest prepareValidateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String system, String version) {
|
||||||
|
String code = concept.getChildValue("code");
|
||||||
|
Coding c = new Coding(system, code, null);
|
||||||
|
if (version != null) {
|
||||||
|
c.setVersion(version);
|
||||||
|
}
|
||||||
|
return new VSCodingValidationRequest(stack, c);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element include, NodeStack push, String system, String version) {
|
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element include, NodeStack push, String system, String version) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private static final String DEF_TX = "http://tx.fhir.org";
|
private static final String DEF_TX = "http://tx.fhir.org";
|
||||||
|
// private static final String DEF_TX = "http://local.fhir.org:960";
|
||||||
private static Map<String, ValidationEngine> ve = new HashMap<>();
|
private static Map<String, ValidationEngine> ve = new HashMap<>();
|
||||||
private static ValidationEngine vCurr;
|
private static ValidationEngine vCurr;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue